1 // Copyright 2018 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 #ifndef V8_WASM_WASM_LINKAGE_H_ 6 #define V8_WASM_WASM_LINKAGE_H_ 7 8 #include "src/codegen/assembler-arch.h" 9 #include "src/codegen/machine-type.h" 10 #include "src/codegen/signature.h" 11 #include "src/wasm/value-type.h" 12 13 namespace v8 { 14 namespace internal { 15 namespace wasm { 16 17 // TODO(wasm): optimize calling conventions to be both closer to C++ (to 18 // reduce adapter costs for fast Wasm <-> C++ calls) and to be more efficient 19 // in general. 20 21 #if V8_TARGET_ARCH_IA32 22 // =========================================================================== 23 // == ia32 =================================================================== 24 // =========================================================================== 25 constexpr Register kGpParamRegisters[] = {esi, eax, edx, ecx}; 26 constexpr Register kGpReturnRegisters[] = {eax, edx}; 27 constexpr DoubleRegister kFpParamRegisters[] = {xmm1, xmm2, xmm3, 28 xmm4, xmm5, xmm6}; 29 constexpr DoubleRegister kFpReturnRegisters[] = {xmm1, xmm2}; 30 31 #elif V8_TARGET_ARCH_X64 32 // =========================================================================== 33 // == x64 ==================================================================== 34 // =========================================================================== 35 constexpr Register kGpParamRegisters[] = {rsi, rax, rdx, rcx, rbx, r9}; 36 constexpr Register kGpReturnRegisters[] = {rax, rdx}; 37 constexpr DoubleRegister kFpParamRegisters[] = {xmm1, xmm2, xmm3, 38 xmm4, xmm5, xmm6}; 39 constexpr DoubleRegister kFpReturnRegisters[] = {xmm1, xmm2}; 40 41 #elif V8_TARGET_ARCH_ARM 42 // =========================================================================== 43 // == arm ==================================================================== 44 // =========================================================================== 45 constexpr Register kGpParamRegisters[] = {r3, r0, r2, r6}; 46 constexpr Register kGpReturnRegisters[] = {r0, r1}; 47 // ARM d-registers must be in ascending order for correct allocation. 48 constexpr DoubleRegister kFpParamRegisters[] = {d0, d1, d2, d3, d4, d5, d6, d7}; 49 constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1}; 50 51 #elif V8_TARGET_ARCH_ARM64 52 // =========================================================================== 53 // == arm64 ==================================================================== 54 // =========================================================================== 55 constexpr Register kGpParamRegisters[] = {x7, x0, x2, x3, x4, x5, x6}; 56 constexpr Register kGpReturnRegisters[] = {x0, x1}; 57 constexpr DoubleRegister kFpParamRegisters[] = {d0, d1, d2, d3, d4, d5, d6, d7}; 58 constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1}; 59 60 #elif V8_TARGET_ARCH_MIPS 61 // =========================================================================== 62 // == mips =================================================================== 63 // =========================================================================== 64 constexpr Register kGpParamRegisters[] = {a0, a2, a3}; 65 constexpr Register kGpReturnRegisters[] = {v0, v1}; 66 constexpr DoubleRegister kFpParamRegisters[] = {f2, f4, f6, f8, f10, f12, f14}; 67 constexpr DoubleRegister kFpReturnRegisters[] = {f2, f4}; 68 69 #elif V8_TARGET_ARCH_MIPS64 70 // =========================================================================== 71 // == mips64 ================================================================= 72 // =========================================================================== 73 constexpr Register kGpParamRegisters[] = {a0, a2, a3, a4, a5, a6, a7}; 74 constexpr Register kGpReturnRegisters[] = {v0, v1}; 75 constexpr DoubleRegister kFpParamRegisters[] = {f2, f4, f6, f8, f10, f12, f14}; 76 constexpr DoubleRegister kFpReturnRegisters[] = {f2, f4}; 77 78 #elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64 79 // =========================================================================== 80 // == ppc & ppc64 ============================================================ 81 // =========================================================================== 82 constexpr Register kGpParamRegisters[] = {r10, r3, r5, r6, r7, r8, r9}; 83 constexpr Register kGpReturnRegisters[] = {r3, r4}; 84 constexpr DoubleRegister kFpParamRegisters[] = {d1, d2, d3, d4, d5, d6, d7, d8}; 85 constexpr DoubleRegister kFpReturnRegisters[] = {d1, d2}; 86 87 #elif V8_TARGET_ARCH_S390X 88 // =========================================================================== 89 // == s390x ================================================================== 90 // =========================================================================== 91 constexpr Register kGpParamRegisters[] = {r6, r2, r4, r5}; 92 constexpr Register kGpReturnRegisters[] = {r2, r3}; 93 constexpr DoubleRegister kFpParamRegisters[] = {d0, d2, d4, d6}; 94 constexpr DoubleRegister kFpReturnRegisters[] = {d0, d2, d4, d6}; 95 96 #elif V8_TARGET_ARCH_S390 97 // =========================================================================== 98 // == s390 =================================================================== 99 // =========================================================================== 100 constexpr Register kGpParamRegisters[] = {r6, r2, r4, r5}; 101 constexpr Register kGpReturnRegisters[] = {r2, r3}; 102 constexpr DoubleRegister kFpParamRegisters[] = {d0, d2}; 103 constexpr DoubleRegister kFpReturnRegisters[] = {d0, d2}; 104 105 #else 106 // =========================================================================== 107 // == unknown ================================================================ 108 // =========================================================================== 109 // Do not use any registers, we will just always use the stack. 110 constexpr Register kGpParamRegisters[] = {}; 111 constexpr Register kGpReturnRegisters[] = {}; 112 constexpr DoubleRegister kFpParamRegisters[] = {}; 113 constexpr DoubleRegister kFpReturnRegisters[] = {}; 114 115 #endif 116 117 // The parameter index where the instance parameter should be placed in wasm 118 // call descriptors. This is used by the Int64Lowering::LowerNode method. 119 constexpr int kWasmInstanceParameterIndex = 0; 120 121 class LinkageAllocator { 122 public: 123 template <size_t kNumGpRegs, size_t kNumFpRegs> LinkageAllocator(const Register (& gp)[kNumGpRegs],const DoubleRegister (& fp)[kNumFpRegs])124 constexpr LinkageAllocator(const Register (&gp)[kNumGpRegs], 125 const DoubleRegister (&fp)[kNumFpRegs]) 126 : LinkageAllocator(gp, kNumGpRegs, fp, kNumFpRegs) {} 127 LinkageAllocator(const Register * gp,int gpc,const DoubleRegister * fp,int fpc)128 constexpr LinkageAllocator(const Register* gp, int gpc, 129 const DoubleRegister* fp, int fpc) 130 : gp_count_(gpc), gp_regs_(gp), fp_count_(fpc), fp_regs_(fp) {} 131 CanAllocateGP()132 bool CanAllocateGP() const { return gp_offset_ < gp_count_; } CanAllocateFP(MachineRepresentation rep)133 bool CanAllocateFP(MachineRepresentation rep) const { 134 #if V8_TARGET_ARCH_ARM 135 switch (rep) { 136 case MachineRepresentation::kFloat32: 137 return fp_offset_ < fp_count_ && fp_regs_[fp_offset_].code() < 16; 138 case MachineRepresentation::kFloat64: 139 return extra_double_reg_ >= 0 || fp_offset_ < fp_count_; 140 case MachineRepresentation::kSimd128: 141 return ((fp_offset_ + 1) & ~1) + 1 < fp_count_; 142 default: 143 UNREACHABLE(); 144 return false; 145 } 146 #endif 147 return fp_offset_ < fp_count_; 148 } 149 NextGpReg()150 int NextGpReg() { 151 DCHECK_LT(gp_offset_, gp_count_); 152 return gp_regs_[gp_offset_++].code(); 153 } 154 NextFpReg(MachineRepresentation rep)155 int NextFpReg(MachineRepresentation rep) { 156 #if V8_TARGET_ARCH_ARM 157 switch (rep) { 158 case MachineRepresentation::kFloat32: { 159 // Liftoff uses only even-numbered f32 registers, and encodes them using 160 // the code of the corresponding f64 register. This limits the calling 161 // interface to only using the even-numbered f32 registers. 162 int d_reg_code = NextFpReg(MachineRepresentation::kFloat64); 163 DCHECK_GT(16, d_reg_code); // D-registers 16 - 31 can't split. 164 return d_reg_code * 2; 165 } 166 case MachineRepresentation::kFloat64: { 167 // Use the extra D-register if there is one. 168 if (extra_double_reg_ >= 0) { 169 int reg_code = extra_double_reg_; 170 extra_double_reg_ = -1; 171 return reg_code; 172 } 173 DCHECK_LT(fp_offset_, fp_count_); 174 return fp_regs_[fp_offset_++].code(); 175 } 176 case MachineRepresentation::kSimd128: { 177 // Q-register must be an even-odd pair, so we must try to allocate at 178 // the end, not using extra_double_reg_. If we are at an odd D-register, 179 // skip past it (saving it to extra_double_reg_). 180 DCHECK_LT(((fp_offset_ + 1) & ~1) + 1, fp_count_); 181 int d_reg1_code = fp_regs_[fp_offset_++].code(); 182 if (d_reg1_code % 2 != 0) { 183 // If we're misaligned then extra_double_reg_ must have been consumed. 184 DCHECK_EQ(-1, extra_double_reg_); 185 int odd_double_reg = d_reg1_code; 186 d_reg1_code = fp_regs_[fp_offset_++].code(); 187 extra_double_reg_ = odd_double_reg; 188 } 189 // Combine the current D-register with the next to form a Q-register. 190 int d_reg2_code = fp_regs_[fp_offset_++].code(); 191 DCHECK_EQ(0, d_reg1_code % 2); 192 DCHECK_EQ(d_reg1_code + 1, d_reg2_code); 193 USE(d_reg2_code); 194 return d_reg1_code / 2; 195 } 196 default: 197 UNREACHABLE(); 198 } 199 #else 200 DCHECK_LT(fp_offset_, fp_count_); 201 return fp_regs_[fp_offset_++].code(); 202 #endif 203 } 204 205 // Stackslots are counted upwards starting from 0 (or the offset set by 206 // {SetStackOffset}. NumStackSlots(MachineRepresentation type)207 int NumStackSlots(MachineRepresentation type) { 208 return std::max(1, ElementSizeInBytes(type) / kSystemPointerSize); 209 } 210 211 // Stackslots are counted upwards starting from 0 (or the offset set by 212 // {SetStackOffset}. If {type} needs more than 213 // one stack slot, the lowest used stack slot is returned. NextStackSlot(MachineRepresentation type)214 int NextStackSlot(MachineRepresentation type) { 215 int num_stack_slots = NumStackSlots(type); 216 int offset = stack_offset_; 217 stack_offset_ += num_stack_slots; 218 return offset; 219 } 220 221 // Set an offset for the stack slots returned by {NextStackSlot} and 222 // {NumStackSlots}. Can only be called before any call to {NextStackSlot}. SetStackOffset(int num)223 void SetStackOffset(int num) { 224 DCHECK_LE(0, num); 225 DCHECK_EQ(0, stack_offset_); 226 stack_offset_ = num; 227 } 228 NumStackSlots()229 int NumStackSlots() const { return stack_offset_; } 230 231 private: 232 const int gp_count_; 233 int gp_offset_ = 0; 234 const Register* const gp_regs_; 235 236 const int fp_count_; 237 int fp_offset_ = 0; 238 const DoubleRegister* const fp_regs_; 239 240 #if V8_TARGET_ARCH_ARM 241 // Track fragments of registers below fp_offset_ here. There can only be one 242 // extra double register. 243 int extra_double_reg_ = -1; 244 #endif 245 246 int stack_offset_ = 0; 247 }; 248 249 } // namespace wasm 250 } // namespace internal 251 } // namespace v8 252 253 #endif // V8_WASM_WASM_LINKAGE_H_ 254