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