| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/assembler.h" |
| #include "src/base/lazy-instance.h" |
| #include "src/macro-assembler.h" |
| #include "src/objects-inl.h" |
| #include "src/register-configuration.h" |
| |
| #include "src/wasm/wasm-module.h" |
| |
| #include "src/compiler/linkage.h" |
| |
| #include "src/zone/zone.h" |
| |
| namespace v8 { |
| namespace internal { |
| // TODO(titzer): this should not be in the WASM namespace. |
| namespace wasm { |
| |
| using compiler::LocationSignature; |
| using compiler::CallDescriptor; |
| using compiler::LinkageLocation; |
| |
| namespace { |
| |
| MachineType MachineTypeFor(ValueType type) { |
| switch (type) { |
| case kWasmI32: |
| return MachineType::Int32(); |
| case kWasmI64: |
| return MachineType::Int64(); |
| case kWasmF64: |
| return MachineType::Float64(); |
| case kWasmF32: |
| return MachineType::Float32(); |
| case kWasmS128: |
| return MachineType::Simd128(); |
| default: |
| UNREACHABLE(); |
| return MachineType::AnyTagged(); |
| } |
| } |
| |
| LinkageLocation regloc(Register reg, MachineType type) { |
| return LinkageLocation::ForRegister(reg.code(), type); |
| } |
| |
| LinkageLocation regloc(DoubleRegister reg, MachineType type) { |
| return LinkageLocation::ForRegister(reg.code(), type); |
| } |
| |
| LinkageLocation stackloc(int i, MachineType type) { |
| return LinkageLocation::ForCallerFrameSlot(i, type); |
| } |
| |
| |
| #if V8_TARGET_ARCH_IA32 |
| // =========================================================================== |
| // == ia32 =================================================================== |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi |
| #define GP_RETURN_REGISTERS eax, edx |
| #define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6 |
| #define FP_RETURN_REGISTERS xmm1, xmm2 |
| |
| #elif V8_TARGET_ARCH_X64 |
| // =========================================================================== |
| // == x64 ==================================================================== |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS rax, rdx, rcx, rbx, rsi, rdi |
| #define GP_RETURN_REGISTERS rax, rdx |
| #define FP_PARAM_REGISTERS xmm1, xmm2, xmm3, xmm4, xmm5, xmm6 |
| #define FP_RETURN_REGISTERS xmm1, xmm2 |
| |
| #elif V8_TARGET_ARCH_X87 |
| // =========================================================================== |
| // == x87 ==================================================================== |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS eax, edx, ecx, ebx, esi |
| #define GP_RETURN_REGISTERS eax, edx |
| #define FP_RETURN_REGISTERS stX_0 |
| |
| #elif V8_TARGET_ARCH_ARM |
| // =========================================================================== |
| // == arm ==================================================================== |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS r0, r1, r2, r3 |
| #define GP_RETURN_REGISTERS r0, r1 |
| #define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7 |
| #define FP_RETURN_REGISTERS d0, d1 |
| |
| #elif V8_TARGET_ARCH_ARM64 |
| // =========================================================================== |
| // == arm64 ==================================================================== |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS x0, x1, x2, x3, x4, x5, x6, x7 |
| #define GP_RETURN_REGISTERS x0, x1 |
| #define FP_PARAM_REGISTERS d0, d1, d2, d3, d4, d5, d6, d7 |
| #define FP_RETURN_REGISTERS d0, d1 |
| |
| #elif V8_TARGET_ARCH_MIPS |
| // =========================================================================== |
| // == mips =================================================================== |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS a0, a1, a2, a3 |
| #define GP_RETURN_REGISTERS v0, v1 |
| #define FP_PARAM_REGISTERS f2, f4, f6, f8, f10, f12, f14 |
| #define FP_RETURN_REGISTERS f2, f4 |
| |
| #elif V8_TARGET_ARCH_MIPS64 |
| // =========================================================================== |
| // == mips64 ================================================================= |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS a0, a1, a2, a3, a4, a5, a6, a7 |
| #define GP_RETURN_REGISTERS v0, v1 |
| #define FP_PARAM_REGISTERS f2, f4, f6, f8, f10, f12, f14 |
| #define FP_RETURN_REGISTERS f2, f4 |
| |
| #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 |
| // =========================================================================== |
| // == ppc & ppc64 ============================================================ |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS r3, r4, r5, r6, r7, r8, r9, r10 |
| #define GP_RETURN_REGISTERS r3, r4 |
| #define FP_PARAM_REGISTERS d1, d2, d3, d4, d5, d6, d7, d8 |
| #define FP_RETURN_REGISTERS d1, d2 |
| |
| #elif V8_TARGET_ARCH_S390X |
| // =========================================================================== |
| // == s390x ================================================================== |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS r2, r3, r4, r5, r6 |
| #define GP_RETURN_REGISTERS r2, r3 |
| #define FP_PARAM_REGISTERS d0, d2, d4, d6 |
| #define FP_RETURN_REGISTERS d0, d2, d4, d6 |
| |
| #elif V8_TARGET_ARCH_S390 |
| // =========================================================================== |
| // == s390 =================================================================== |
| // =========================================================================== |
| #define GP_PARAM_REGISTERS r2, r3, r4, r5, r6 |
| #define GP_RETURN_REGISTERS r2, r3 |
| #define FP_PARAM_REGISTERS d0, d2 |
| #define FP_RETURN_REGISTERS d0, d2 |
| |
| #else |
| // =========================================================================== |
| // == unknown ================================================================ |
| // =========================================================================== |
| // Don't define anything. We'll just always use the stack. |
| #endif |
| |
| |
| // Helper for allocating either an GP or FP reg, or the next stack slot. |
| struct Allocator { |
| Allocator(const Register* gp, int gpc, const DoubleRegister* fp, int fpc) |
| : gp_count(gpc), |
| gp_offset(0), |
| gp_regs(gp), |
| fp_count(fpc), |
| fp_offset(0), |
| fp_regs(fp), |
| stack_offset(0) {} |
| |
| int gp_count; |
| int gp_offset; |
| const Register* gp_regs; |
| |
| int fp_count; |
| int fp_offset; |
| const DoubleRegister* fp_regs; |
| |
| int stack_offset; |
| |
| LinkageLocation Next(ValueType type) { |
| if (IsFloatingPoint(type)) { |
| // Allocate a floating point register/stack location. |
| if (fp_offset < fp_count) { |
| DoubleRegister reg = fp_regs[fp_offset++]; |
| #if V8_TARGET_ARCH_ARM |
| // Allocate floats using a double register, but modify the code to |
| // reflect how ARM FP registers alias. |
| // TODO(bbudge) Modify wasm linkage to allow use of all float regs. |
| if (type == kWasmF32) { |
| int float_reg_code = reg.code() * 2; |
| DCHECK(float_reg_code < RegisterConfiguration::kMaxFPRegisters); |
| return regloc(DoubleRegister::from_code(float_reg_code), |
| MachineTypeFor(type)); |
| } |
| #endif |
| return regloc(reg, MachineTypeFor(type)); |
| } else { |
| int offset = -1 - stack_offset; |
| stack_offset += Words(type); |
| return stackloc(offset, MachineTypeFor(type)); |
| } |
| } else { |
| // Allocate a general purpose register/stack location. |
| if (gp_offset < gp_count) { |
| return regloc(gp_regs[gp_offset++], MachineTypeFor(type)); |
| } else { |
| int offset = -1 - stack_offset; |
| stack_offset += Words(type); |
| return stackloc(offset, MachineTypeFor(type)); |
| } |
| } |
| } |
| bool IsFloatingPoint(ValueType type) { |
| return type == kWasmF32 || type == kWasmF64; |
| } |
| int Words(ValueType type) { |
| if (kPointerSize < 8 && (type == kWasmI64 || type == kWasmF64)) { |
| return 2; |
| } |
| return 1; |
| } |
| }; |
| } // namespace |
| |
| struct ParameterRegistersCreateTrait { |
| static void Construct(Allocator* allocated_ptr) { |
| #ifdef GP_PARAM_REGISTERS |
| static const Register kGPParamRegisters[] = {GP_PARAM_REGISTERS}; |
| static const int kGPParamRegistersCount = |
| static_cast<int>(arraysize(kGPParamRegisters)); |
| #else |
| static const Register* kGPParamRegisters = nullptr; |
| static const int kGPParamRegistersCount = 0; |
| #endif |
| |
| #ifdef FP_PARAM_REGISTERS |
| static const DoubleRegister kFPParamRegisters[] = {FP_PARAM_REGISTERS}; |
| static const int kFPParamRegistersCount = |
| static_cast<int>(arraysize(kFPParamRegisters)); |
| #else |
| static const DoubleRegister* kFPParamRegisters = nullptr; |
| static const int kFPParamRegistersCount = 0; |
| #endif |
| |
| new (allocated_ptr) Allocator(kGPParamRegisters, kGPParamRegistersCount, |
| kFPParamRegisters, kFPParamRegistersCount); |
| } |
| }; |
| |
| static base::LazyInstance<Allocator, ParameterRegistersCreateTrait>::type |
| parameter_registers = LAZY_INSTANCE_INITIALIZER; |
| |
| struct ReturnRegistersCreateTrait { |
| static void Construct(Allocator* allocated_ptr) { |
| #ifdef GP_RETURN_REGISTERS |
| static const Register kGPReturnRegisters[] = {GP_RETURN_REGISTERS}; |
| static const int kGPReturnRegistersCount = |
| static_cast<int>(arraysize(kGPReturnRegisters)); |
| #else |
| static const Register* kGPReturnRegisters = nullptr; |
| static const int kGPReturnRegistersCount = 0; |
| #endif |
| |
| #ifdef FP_RETURN_REGISTERS |
| static const DoubleRegister kFPReturnRegisters[] = {FP_RETURN_REGISTERS}; |
| static const int kFPReturnRegistersCount = |
| static_cast<int>(arraysize(kFPReturnRegisters)); |
| #else |
| static const DoubleRegister* kFPReturnRegisters = nullptr; |
| static const int kFPReturnRegistersCount = 0; |
| #endif |
| |
| new (allocated_ptr) Allocator(kGPReturnRegisters, kGPReturnRegistersCount, |
| kFPReturnRegisters, kFPReturnRegistersCount); |
| } |
| }; |
| |
| static base::LazyInstance<Allocator, ReturnRegistersCreateTrait>::type |
| return_registers = LAZY_INSTANCE_INITIALIZER; |
| |
| // General code uses the above configuration data. |
| CallDescriptor* ModuleEnv::GetWasmCallDescriptor(Zone* zone, |
| FunctionSig* fsig) { |
| LocationSignature::Builder locations(zone, fsig->return_count(), |
| fsig->parameter_count()); |
| |
| Allocator rets = return_registers.Get(); |
| |
| // Add return location(s). |
| const int return_count = static_cast<int>(locations.return_count_); |
| for (int i = 0; i < return_count; i++) { |
| ValueType ret = fsig->GetReturn(i); |
| locations.AddReturn(rets.Next(ret)); |
| } |
| |
| Allocator params = parameter_registers.Get(); |
| |
| // Add register and/or stack parameter(s). |
| const int parameter_count = static_cast<int>(fsig->parameter_count()); |
| for (int i = 0; i < parameter_count; i++) { |
| ValueType param = fsig->GetParam(i); |
| locations.AddParam(params.Next(param)); |
| } |
| |
| const RegList kCalleeSaveRegisters = 0; |
| const RegList kCalleeSaveFPRegisters = 0; |
| |
| // The target for WASM calls is always a code object. |
| MachineType target_type = MachineType::AnyTagged(); |
| LinkageLocation target_loc = LinkageLocation::ForAnyRegister(target_type); |
| |
| return new (zone) CallDescriptor( // -- |
| CallDescriptor::kCallCodeObject, // kind |
| target_type, // target MachineType |
| target_loc, // target location |
| locations.Build(), // location_sig |
| params.stack_offset, // stack_parameter_count |
| compiler::Operator::kNoProperties, // properties |
| kCalleeSaveRegisters, // callee-saved registers |
| kCalleeSaveFPRegisters, // callee-saved fp regs |
| CallDescriptor::kUseNativeStack, // flags |
| "wasm-call"); |
| } |
| |
| CallDescriptor* ReplaceTypeInCallDescriptorWith( |
| Zone* zone, CallDescriptor* descriptor, size_t num_replacements, |
| MachineType input_type, MachineRepresentation output_type) { |
| size_t parameter_count = descriptor->ParameterCount(); |
| size_t return_count = descriptor->ReturnCount(); |
| for (size_t i = 0; i < descriptor->ParameterCount(); i++) { |
| if (descriptor->GetParameterType(i) == input_type) { |
| parameter_count += num_replacements - 1; |
| } |
| } |
| for (size_t i = 0; i < descriptor->ReturnCount(); i++) { |
| if (descriptor->GetReturnType(i) == input_type) { |
| return_count += num_replacements - 1; |
| } |
| } |
| if (parameter_count == descriptor->ParameterCount() && |
| return_count == descriptor->ReturnCount()) { |
| return descriptor; |
| } |
| |
| LocationSignature::Builder locations(zone, return_count, parameter_count); |
| |
| Allocator rets = return_registers.Get(); |
| |
| for (size_t i = 0; i < descriptor->ReturnCount(); i++) { |
| if (descriptor->GetReturnType(i) == input_type) { |
| for (size_t j = 0; j < num_replacements; j++) { |
| locations.AddReturn(rets.Next(output_type)); |
| } |
| } else { |
| locations.AddReturn( |
| rets.Next(descriptor->GetReturnType(i).representation())); |
| } |
| } |
| |
| Allocator params = parameter_registers.Get(); |
| |
| for (size_t i = 0; i < descriptor->ParameterCount(); i++) { |
| if (descriptor->GetParameterType(i) == input_type) { |
| for (size_t j = 0; j < num_replacements; j++) { |
| locations.AddParam(params.Next(output_type)); |
| } |
| } else { |
| locations.AddParam( |
| params.Next(descriptor->GetParameterType(i).representation())); |
| } |
| } |
| |
| return new (zone) CallDescriptor( // -- |
| descriptor->kind(), // kind |
| descriptor->GetInputType(0), // target MachineType |
| descriptor->GetInputLocation(0), // target location |
| locations.Build(), // location_sig |
| params.stack_offset, // stack_parameter_count |
| descriptor->properties(), // properties |
| descriptor->CalleeSavedRegisters(), // callee-saved registers |
| descriptor->CalleeSavedFPRegisters(), // callee-saved fp regs |
| descriptor->flags(), // flags |
| descriptor->debug_name()); |
| } |
| |
| CallDescriptor* ModuleEnv::GetI32WasmCallDescriptor( |
| Zone* zone, CallDescriptor* descriptor) { |
| return ReplaceTypeInCallDescriptorWith(zone, descriptor, 2, |
| MachineType::Int64(), |
| MachineRepresentation::kWord32); |
| } |
| |
| CallDescriptor* ModuleEnv::GetI32WasmCallDescriptorForSimd( |
| Zone* zone, CallDescriptor* descriptor) { |
| return ReplaceTypeInCallDescriptorWith(zone, descriptor, 4, |
| MachineType::Simd128(), |
| MachineRepresentation::kWord32); |
| } |
| |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |