// 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-inl.h" #include "src/base/lazy-instance.h" #include "src/macro-assembler.h" #include "src/register-configuration.h" #include "src/compiler/linkage.h" #include "src/compiler/wasm-compiler.h" #include "src/zone/zone.h" namespace v8 { namespace internal { namespace compiler { using wasm::ValueType; namespace { MachineType MachineTypeFor(ValueType type) { switch (type) { case wasm::kWasmI32: return MachineType::Int32(); case wasm::kWasmI64: return MachineType::Int64(); case wasm::kWasmF64: return MachineType::Float64(); case wasm::kWasmF32: return MachineType::Float32(); case wasm::kWasmS128: return MachineType::Simd128(); default: UNREACHABLE(); } } LinkageLocation stackloc(int i, MachineType type) { return LinkageLocation::ForCallerFrameSlot(i, type); } #if V8_TARGET_ARCH_IA32 // =========================================================================== // == ia32 =================================================================== // =========================================================================== #define GP_PARAM_REGISTERS esi, eax, edx, ecx, ebx #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 rsi, rax, rdx, rcx, rbx, 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_ARM // =========================================================================== // == arm ==================================================================== // =========================================================================== #define GP_PARAM_REGISTERS r3, r0, r1, r2 #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 x7, x0, x1, x2, x3, x4, x5, x6 #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 r10, r3, r4, r5, r6, r7, r8, r9 #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 r6, r2, r3, r4, r5 #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 r6, r2, r3, r4, r5 #define GP_RETURN_REGISTERS r2, r3 #define FP_PARAM_REGISTERS d0, d2 #define FP_RETURN_REGISTERS d0, d2 #else // =========================================================================== // == unknown ================================================================ // =========================================================================== // Do not use any registers, we will just always use the stack. #define GP_PARAM_REGISTERS #define GP_RETURN_REGISTERS #define FP_PARAM_REGISTERS #define FP_RETURN_REGISTERS #endif // Helper for allocating either an GP or FP reg, or the next stack slot. struct Allocator { constexpr 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 == wasm::kWasmF32) { int float_reg_code = reg.code() * 2; DCHECK_GT(RegisterConfiguration::kMaxFPRegisters, float_reg_code); return LinkageLocation::ForRegister( DoubleRegister::from_code(float_reg_code).code(), MachineTypeFor(type)); } #endif return LinkageLocation::ForRegister(reg.code(), 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 LinkageLocation::ForRegister(gp_regs[gp_offset++].code(), MachineTypeFor(type)); } else { int offset = -1 - stack_offset; stack_offset += Words(type); return stackloc(offset, MachineTypeFor(type)); } } } bool IsFloatingPoint(ValueType type) { return type == wasm::kWasmF32 || type == wasm::kWasmF64; } int Words(ValueType type) { if (kPointerSize < 8 && (type == wasm::kWasmI64 || type == wasm::kWasmF64)) { return 2; } return 1; } }; static constexpr Register kGPReturnRegisters[] = {GP_RETURN_REGISTERS}; static constexpr DoubleRegister kFPReturnRegisters[] = {FP_RETURN_REGISTERS}; static constexpr Register kGPParamRegisters[] = {GP_PARAM_REGISTERS}; static constexpr DoubleRegister kFPParamRegisters[] = {FP_PARAM_REGISTERS}; static constexpr Allocator return_registers(kGPReturnRegisters, arraysize(kGPReturnRegisters), kFPReturnRegisters, arraysize(kFPReturnRegisters)); static constexpr Allocator parameter_registers(kGPParamRegisters, arraysize(kGPParamRegisters), kFPParamRegisters, arraysize(kFPParamRegisters)); } // namespace // General code uses the above configuration data. CallDescriptor* GetWasmCallDescriptor(Zone* zone, wasm::FunctionSig* fsig) { // The '+ 1' here is to accomodate the wasm_context as first parameter. LocationSignature::Builder locations(zone, fsig->return_count(), fsig->parameter_count() + 1); Allocator rets = return_registers; // Add return location(s). const int return_count = static_cast(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; // Add parameter for the wasm_context. locations.AddParam(params.Next(MachineType::PointerRepresentation())); // Add register and/or stack parameter(s). const int parameter_count = static_cast(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; 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; 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* GetI32WasmCallDescriptor(Zone* zone, CallDescriptor* descriptor) { return ReplaceTypeInCallDescriptorWith(zone, descriptor, 2, MachineType::Int64(), MachineRepresentation::kWord32); } CallDescriptor* GetI32WasmCallDescriptorForSimd(Zone* zone, CallDescriptor* descriptor) { return ReplaceTypeInCallDescriptorWith(zone, descriptor, 4, MachineType::Simd128(), MachineRepresentation::kWord32); } #undef GP_PARAM_REGISTERS #undef GP_RETURN_REGISTERS #undef FP_PARAM_REGISTERS #undef FP_RETURN_REGISTERS } // namespace compiler } // namespace internal } // namespace v8