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