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
17 namespace v8 {
18 namespace internal {
19 // TODO(titzer): this should not be in the WASM namespace.
20 namespace wasm {
21
22 using compiler::LocationSignature;
23 using compiler::CallDescriptor;
24 using compiler::LinkageLocation;
25
26 namespace {
27
MachineTypeFor(ValueType type)28 MachineType 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
regloc(Register reg,MachineType type)46 LinkageLocation regloc(Register reg, MachineType type) {
47 return LinkageLocation::ForRegister(reg.code(), type);
48 }
49
regloc(DoubleRegister reg,MachineType type)50 LinkageLocation regloc(DoubleRegister reg, MachineType type) {
51 return LinkageLocation::ForRegister(reg.code(), type);
52 }
53
stackloc(int i,MachineType type)54 LinkageLocation 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.
157 struct Allocator {
Allocatorv8::internal::wasm::__anon762a1cc20111::Allocator158 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
Nextv8::internal::wasm::__anon762a1cc20111::Allocator177 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 }
IsFloatingPointv8::internal::wasm::__anon762a1cc20111::Allocator210 bool IsFloatingPoint(ValueType type) {
211 return type == kWasmF32 || type == kWasmF64;
212 }
Wordsv8::internal::wasm::__anon762a1cc20111::Allocator213 int Words(ValueType type) {
214 if (kPointerSize < 8 && (type == kWasmI64 || type == kWasmF64)) {
215 return 2;
216 }
217 return 1;
218 }
219 };
220 } // namespace
221
222 struct ParameterRegistersCreateTrait {
Constructv8::internal::wasm::ParameterRegistersCreateTrait223 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
247 static base::LazyInstance<Allocator, ParameterRegistersCreateTrait>::type
248 parameter_registers = LAZY_INSTANCE_INITIALIZER;
249
250 struct ReturnRegistersCreateTrait {
Constructv8::internal::wasm::ReturnRegistersCreateTrait251 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
275 static base::LazyInstance<Allocator, ReturnRegistersCreateTrait>::type
276 return_registers = LAZY_INSTANCE_INITIALIZER;
277
278 // General code uses the above configuration data.
GetWasmCallDescriptor(Zone * zone,FunctionSig * fsig)279 CallDescriptor* 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
ReplaceTypeInCallDescriptorWith(Zone * zone,CallDescriptor * descriptor,size_t num_replacements,MachineType input_type,MachineRepresentation output_type)322 CallDescriptor* 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
GetI32WasmCallDescriptor(Zone * zone,CallDescriptor * descriptor)383 CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor(
384 Zone* zone, CallDescriptor* descriptor) {
385 return ReplaceTypeInCallDescriptorWith(zone, descriptor, 2,
386 MachineType::Int64(),
387 MachineRepresentation::kWord32);
388 }
389
GetI32WasmCallDescriptorForSimd(Zone * zone,CallDescriptor * descriptor)390 CallDescriptor* 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