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