wasm-linkage.cc revision 21efce637eb329c94f1323b6a2334a1c977e1a9d
1// Copyright 2015 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/assembler.h"
6#include "src/macro-assembler.h"
7#include "src/register-configuration.h"
8
9#include "src/wasm/wasm-module.h"
10
11#include "src/compiler/linkage.h"
12
13#include "src/zone.h"
14
15namespace v8 {
16namespace internal {
17// TODO(titzer): this should not be in the WASM namespace.
18namespace wasm {
19
20using compiler::LocationSignature;
21using compiler::CallDescriptor;
22using compiler::LinkageLocation;
23
24namespace {
25MachineType MachineTypeFor(LocalType type) {
26  switch (type) {
27    case kAstI32:
28      return MachineType::Int32();
29    case kAstI64:
30      return MachineType::Int64();
31    case kAstF64:
32      return MachineType::Float64();
33    case kAstF32:
34      return MachineType::Float32();
35    case kAstS128:
36      return MachineType::Simd128();
37    default:
38      UNREACHABLE();
39      return MachineType::AnyTagged();
40  }
41}
42
43
44// Platform-specific configuration for C calling convention.
45LinkageLocation regloc(Register reg) {
46  return LinkageLocation::ForRegister(reg.code());
47}
48
49
50LinkageLocation regloc(DoubleRegister reg) {
51  return LinkageLocation::ForRegister(reg.code());
52}
53
54
55LinkageLocation stackloc(int i) {
56  return LinkageLocation::ForCallerFrameSlot(i);
57}
58
59
60#if V8_TARGET_ARCH_IA32
61// ===========================================================================
62// == ia32 ===================================================================
63// ===========================================================================
64#define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi
65#define GP_RETURN_REGISTERS eax, edx
66#define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
67#define FP_RETURN_REGISTERS xmm1, xmm2
68
69#elif V8_TARGET_ARCH_X64
70// ===========================================================================
71// == x64 ====================================================================
72// ===========================================================================
73#define GP_PARAM_REGISTERS rax, rdx, rcx, rbx, rsi, rdi
74#define GP_RETURN_REGISTERS rax, rdx
75#define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
76#define FP_RETURN_REGISTERS xmm1, xmm2
77
78#elif V8_TARGET_ARCH_X87
79// ===========================================================================
80// == x87 ====================================================================
81// ===========================================================================
82#define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi
83#define GP_RETURN_REGISTERS eax, edx
84#define FP_RETURN_REGISTERS stX_0
85
86#elif V8_TARGET_ARCH_ARM
87// ===========================================================================
88// == arm ====================================================================
89// ===========================================================================
90#define GP_PARAM_REGISTERS r0, r1, r2, r3
91#define GP_RETURN_REGISTERS r0, r1
92#define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
93#define FP_RETURN_REGISTERS d0, d1
94
95#elif V8_TARGET_ARCH_ARM64
96// ===========================================================================
97// == arm64 ====================================================================
98// ===========================================================================
99#define GP_PARAM_REGISTERS x0, x1, x2, x3, x4, x5, x6, x7
100#define GP_RETURN_REGISTERS x0, x1
101#define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7
102#define FP_RETURN_REGISTERS d0, d1
103
104#elif V8_TARGET_ARCH_MIPS
105// ===========================================================================
106// == mips ===================================================================
107// ===========================================================================
108#define GP_PARAM_REGISTERS a0, a1, a2, a3
109#define GP_RETURN_REGISTERS v0, v1
110#define FP_PARAM_REGISTERS f2, f4, f6, f8, f10, f12, f14
111#define FP_RETURN_REGISTERS f2, f4
112
113#elif V8_TARGET_ARCH_MIPS64
114// ===========================================================================
115// == mips64 =================================================================
116// ===========================================================================
117#define GP_PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7
118#define GP_RETURN_REGISTERS v0, v1
119#define FP_PARAM_REGISTERS f2, f4, f6, f8, f10, f12, f14
120#define FP_RETURN_REGISTERS f2, f4
121
122#elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
123// ===========================================================================
124// == ppc & ppc64 ============================================================
125// ===========================================================================
126#define GP_PARAM_REGISTERS r3, r4, r5, r6, r7, r8, r9, r10
127#define GP_RETURN_REGISTERS r3, r4
128#define FP_PARAM_REGISTERS d1, d2, d3, d4, d5, d6, d7, d8
129#define FP_RETURN_REGISTERS d1, d2
130
131#elif V8_TARGET_ARCH_S390X
132// ===========================================================================
133// == s390x ==================================================================
134// ===========================================================================
135#define GP_PARAM_REGISTERS r2, r3, r4, r5, r6
136#define GP_RETURN_REGISTERS r2
137#define FP_PARAM_REGISTERS d0, d2, d4, d6
138#define FP_RETURN_REGISTERS d0, d2, d4, d6
139
140#elif V8_TARGET_ARCH_S390
141// ===========================================================================
142// == s390 ===================================================================
143// ===========================================================================
144#define GP_PARAM_REGISTERS r2, r3, r4, r5, r6
145#define GP_RETURN_REGISTERS r2, r3
146#define FP_PARAM_REGISTERS d0, d2
147#define FP_RETURN_REGISTERS d0, d2
148
149#else
150// ===========================================================================
151// == unknown ================================================================
152// ===========================================================================
153// Don't define anything. We'll just always use the stack.
154#endif
155
156
157// Helper for allocating either an GP or FP reg, or the next stack slot.
158struct Allocator {
159  Allocator(const Register* gp, int gpc, const DoubleRegister* fp, int fpc)
160      : gp_count(gpc),
161        gp_offset(0),
162        gp_regs(gp),
163        fp_count(fpc),
164        fp_offset(0),
165        fp_regs(fp),
166        stack_offset(0) {}
167
168  int gp_count;
169  int gp_offset;
170  const Register* gp_regs;
171
172  int fp_count;
173  int fp_offset;
174  const DoubleRegister* fp_regs;
175
176  int stack_offset;
177
178  LinkageLocation Next(LocalType type) {
179    if (IsFloatingPoint(type)) {
180      // Allocate a floating point register/stack location.
181      if (fp_offset < fp_count) {
182        DoubleRegister reg = fp_regs[fp_offset++];
183#if V8_TARGET_ARCH_ARM
184        // Allocate floats using a double register, but modify the code to
185        // reflect how ARM FP registers alias.
186        // TODO(bbudge) Modify wasm linkage to allow use of all float regs.
187        if (type == kAstF32) {
188          int float_reg_code = reg.code() * 2;
189          DCHECK(float_reg_code < RegisterConfiguration::kMaxFPRegisters);
190          return regloc(DoubleRegister::from_code(float_reg_code));
191        }
192#endif
193        return regloc(reg);
194      } else {
195        int offset = -1 - stack_offset;
196        stack_offset += Words(type);
197        return stackloc(offset);
198      }
199    } else {
200      // Allocate a general purpose register/stack location.
201      if (gp_offset < gp_count) {
202        return regloc(gp_regs[gp_offset++]);
203      } else {
204        int offset = -1 - stack_offset;
205        stack_offset += Words(type);
206        return stackloc(offset);
207      }
208    }
209  }
210  bool IsFloatingPoint(LocalType type) {
211    return type == kAstF32 || type == kAstF64;
212  }
213  int Words(LocalType type) {
214    if (kPointerSize < 8 && (type == kAstI64 || type == kAstF64)) {
215      return 2;
216    }
217    return 1;
218  }
219};
220}  // namespace
221
222static Allocator GetReturnRegisters() {
223#ifdef GP_RETURN_REGISTERS
224  static const Register kGPReturnRegisters[] = {GP_RETURN_REGISTERS};
225  static const int kGPReturnRegistersCount =
226      static_cast<int>(arraysize(kGPReturnRegisters));
227#else
228  static const Register* kGPReturnRegisters = nullptr;
229  static const int kGPReturnRegistersCount = 0;
230#endif
231
232#ifdef FP_RETURN_REGISTERS
233  static const DoubleRegister kFPReturnRegisters[] = {FP_RETURN_REGISTERS};
234  static const int kFPReturnRegistersCount =
235      static_cast<int>(arraysize(kFPReturnRegisters));
236#else
237  static const DoubleRegister* kFPReturnRegisters = nullptr;
238  static const int kFPReturnRegistersCount = 0;
239#endif
240
241  Allocator rets(kGPReturnRegisters, kGPReturnRegistersCount,
242                 kFPReturnRegisters, kFPReturnRegistersCount);
243
244  return rets;
245}
246
247static Allocator GetParameterRegisters() {
248#ifdef GP_PARAM_REGISTERS
249  static const Register kGPParamRegisters[] = {GP_PARAM_REGISTERS};
250  static const int kGPParamRegistersCount =
251      static_cast<int>(arraysize(kGPParamRegisters));
252#else
253  static const Register* kGPParamRegisters = nullptr;
254  static const int kGPParamRegistersCount = 0;
255#endif
256
257#ifdef FP_PARAM_REGISTERS
258  static const DoubleRegister kFPParamRegisters[] = {FP_PARAM_REGISTERS};
259  static const int kFPParamRegistersCount =
260      static_cast<int>(arraysize(kFPParamRegisters));
261#else
262  static const DoubleRegister* kFPParamRegisters = nullptr;
263  static const int kFPParamRegistersCount = 0;
264#endif
265
266  Allocator params(kGPParamRegisters, kGPParamRegistersCount, kFPParamRegisters,
267                   kFPParamRegistersCount);
268
269  return params;
270}
271
272// General code uses the above configuration data.
273CallDescriptor* ModuleEnv::GetWasmCallDescriptor(Zone* zone,
274                                                 FunctionSig* fsig) {
275  MachineSignature::Builder msig(zone, fsig->return_count(),
276                                 fsig->parameter_count());
277  LocationSignature::Builder locations(zone, fsig->return_count(),
278                                       fsig->parameter_count());
279
280  Allocator rets = GetReturnRegisters();
281
282  // Add return location(s).
283  const int return_count = static_cast<int>(locations.return_count_);
284  for (int i = 0; i < return_count; i++) {
285    LocalType ret = fsig->GetReturn(i);
286    msig.AddReturn(MachineTypeFor(ret));
287    locations.AddReturn(rets.Next(ret));
288  }
289
290  Allocator params = GetParameterRegisters();
291
292  // Add register and/or stack parameter(s).
293  const int parameter_count = static_cast<int>(fsig->parameter_count());
294  for (int i = 0; i < parameter_count; i++) {
295    LocalType param = fsig->GetParam(i);
296    msig.AddParam(MachineTypeFor(param));
297    locations.AddParam(params.Next(param));
298  }
299
300  const RegList kCalleeSaveRegisters = 0;
301  const RegList kCalleeSaveFPRegisters = 0;
302
303  // The target for WASM calls is always a code object.
304  MachineType target_type = MachineType::AnyTagged();
305  LinkageLocation target_loc = LinkageLocation::ForAnyRegister();
306
307  return new (zone) CallDescriptor(       // --
308      CallDescriptor::kCallCodeObject,    // kind
309      target_type,                        // target MachineType
310      target_loc,                         // target location
311      msig.Build(),                       // machine_sig
312      locations.Build(),                  // location_sig
313      params.stack_offset,                // stack_parameter_count
314      compiler::Operator::kNoProperties,  // properties
315      kCalleeSaveRegisters,               // callee-saved registers
316      kCalleeSaveFPRegisters,             // callee-saved fp regs
317      CallDescriptor::kUseNativeStack,    // flags
318      "wasm-call");
319}
320
321CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
322    Zone* zone, CallDescriptor* descriptor) {
323  const MachineSignature* signature = descriptor->GetMachineSignature();
324  size_t parameter_count = signature->parameter_count();
325  size_t return_count = signature->return_count();
326  for (size_t i = 0; i < signature->parameter_count(); i++) {
327    if (signature->GetParam(i) == MachineType::Int64()) {
328      // For each int64 input we get two int32 inputs.
329      parameter_count++;
330    }
331  }
332  for (size_t i = 0; i < signature->return_count(); i++) {
333    if (signature->GetReturn(i) == MachineType::Int64()) {
334      // For each int64 return we get two int32 returns.
335      return_count++;
336    }
337  }
338  if (parameter_count == signature->parameter_count() &&
339      return_count == signature->return_count()) {
340    // If there is no int64 parameter or return value, we can just return the
341    // original descriptor.
342    return descriptor;
343  }
344
345  MachineSignature::Builder msig(zone, return_count, parameter_count);
346  LocationSignature::Builder locations(zone, return_count, parameter_count);
347
348  Allocator rets = GetReturnRegisters();
349
350  for (size_t i = 0; i < signature->return_count(); i++) {
351    if (signature->GetReturn(i) == MachineType::Int64()) {
352      // For each int64 return we get two int32 returns.
353      msig.AddReturn(MachineType::Int32());
354      msig.AddReturn(MachineType::Int32());
355      locations.AddReturn(rets.Next(MachineRepresentation::kWord32));
356      locations.AddReturn(rets.Next(MachineRepresentation::kWord32));
357    } else {
358      msig.AddReturn(signature->GetReturn(i));
359      locations.AddReturn(rets.Next(signature->GetReturn(i).representation()));
360    }
361  }
362
363  Allocator params = GetParameterRegisters();
364
365  for (size_t i = 0; i < signature->parameter_count(); i++) {
366    if (signature->GetParam(i) == MachineType::Int64()) {
367      // For each int64 input we get two int32 inputs.
368      msig.AddParam(MachineType::Int32());
369      msig.AddParam(MachineType::Int32());
370      locations.AddParam(params.Next(MachineRepresentation::kWord32));
371      locations.AddParam(params.Next(MachineRepresentation::kWord32));
372    } else {
373      msig.AddParam(signature->GetParam(i));
374      locations.AddParam(params.Next(signature->GetParam(i).representation()));
375    }
376  }
377
378  return new (zone) CallDescriptor(          // --
379      descriptor->kind(),                    // kind
380      descriptor->GetInputType(0),           // target MachineType
381      descriptor->GetInputLocation(0),       // target location
382      msig.Build(),                          // machine_sig
383      locations.Build(),                     // location_sig
384      params.stack_offset,                   // stack_parameter_count
385      descriptor->properties(),              // properties
386      descriptor->CalleeSavedRegisters(),    // callee-saved registers
387      descriptor->CalleeSavedFPRegisters(),  // callee-saved fp regs
388      descriptor->flags(),                   // flags
389      descriptor->debug_name());
390
391  return descriptor;
392}
393
394}  // namespace wasm
395}  // namespace internal
396}  // namespace v8
397