wasm-linkage.h 11.3 KB
Newer Older
1 2 3 4
// Copyright 2018 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.

5 6 7 8
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif  // !V8_ENABLE_WEBASSEMBLY

9 10 11
#ifndef V8_WASM_WASM_LINKAGE_H_
#define V8_WASM_WASM_LINKAGE_H_

12
#include "src/codegen/aligned-slot-allocator.h"
13 14 15
#include "src/codegen/assembler-arch.h"
#include "src/codegen/machine-type.h"
#include "src/codegen/signature.h"
16 17 18 19 20 21
#include "src/wasm/value-type.h"

namespace v8 {
namespace internal {
namespace wasm {

22
// TODO(wasm): optimize calling conventions to be both closer to C++ (to
23
// reduce adapter costs for fast Wasm <-> C++ calls) and to be more efficient
24 25
// in general.

26 27 28 29
#if V8_TARGET_ARCH_IA32
// ===========================================================================
// == ia32 ===================================================================
// ===========================================================================
30
constexpr Register kGpParamRegisters[] = {esi, eax, edx, ecx};
31 32 33 34 35 36 37 38 39
constexpr Register kGpReturnRegisters[] = {eax, edx};
constexpr DoubleRegister kFpParamRegisters[] = {xmm1, xmm2, xmm3,
                                                xmm4, xmm5, xmm6};
constexpr DoubleRegister kFpReturnRegisters[] = {xmm1, xmm2};

#elif V8_TARGET_ARCH_X64
// ===========================================================================
// == x64 ====================================================================
// ===========================================================================
40
constexpr Register kGpParamRegisters[] = {rsi, rax, rdx, rcx, rbx, r9};
41 42 43 44 45 46 47 48 49
constexpr Register kGpReturnRegisters[] = {rax, rdx};
constexpr DoubleRegister kFpParamRegisters[] = {xmm1, xmm2, xmm3,
                                                xmm4, xmm5, xmm6};
constexpr DoubleRegister kFpReturnRegisters[] = {xmm1, xmm2};

#elif V8_TARGET_ARCH_ARM
// ===========================================================================
// == arm ====================================================================
// ===========================================================================
50
constexpr Register kGpParamRegisters[] = {r3, r0, r2, r6};
51
constexpr Register kGpReturnRegisters[] = {r0, r1};
52
// ARM d-registers must be in even/odd D-register pairs for correct allocation.
53 54 55 56 57 58 59
constexpr DoubleRegister kFpParamRegisters[] = {d0, d1, d2, d3, d4, d5, d6, d7};
constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1};

#elif V8_TARGET_ARCH_ARM64
// ===========================================================================
// == arm64 ====================================================================
// ===========================================================================
60
constexpr Register kGpParamRegisters[] = {x7, x0, x2, x3, x4, x5, x6};
61 62 63 64 65 66 67 68
constexpr Register kGpReturnRegisters[] = {x0, x1};
constexpr DoubleRegister kFpParamRegisters[] = {d0, d1, d2, d3, d4, d5, d6, d7};
constexpr DoubleRegister kFpReturnRegisters[] = {d0, d1};

#elif V8_TARGET_ARCH_MIPS
// ===========================================================================
// == mips ===================================================================
// ===========================================================================
69
constexpr Register kGpParamRegisters[] = {a0, a2, a3};
70 71 72 73 74 75 76 77
constexpr Register kGpReturnRegisters[] = {v0, v1};
constexpr DoubleRegister kFpParamRegisters[] = {f2, f4, f6, f8, f10, f12, f14};
constexpr DoubleRegister kFpReturnRegisters[] = {f2, f4};

#elif V8_TARGET_ARCH_MIPS64
// ===========================================================================
// == mips64 =================================================================
// ===========================================================================
78
constexpr Register kGpParamRegisters[] = {a0, a2, a3, a4, a5, a6, a7};
79 80 81 82
constexpr Register kGpReturnRegisters[] = {v0, v1};
constexpr DoubleRegister kFpParamRegisters[] = {f2, f4, f6, f8, f10, f12, f14};
constexpr DoubleRegister kFpReturnRegisters[] = {f2, f4};

83 84 85 86 87 88 89 90 91
#elif V8_TARGET_ARCH_LOONG64
// ===========================================================================
// == LOONG64 ================================================================
// ===========================================================================
constexpr Register kGpParamRegisters[] = {a0, a2, a3, a4, a5, a6, a7};
constexpr Register kGpReturnRegisters[] = {a0, a1};
constexpr DoubleRegister kFpParamRegisters[] = {f0, f1, f2, f3, f4, f5, f6, f7};
constexpr DoubleRegister kFpReturnRegisters[] = {f0, f1};

92 93 94 95
#elif V8_TARGET_ARCH_PPC || V8_TARGET_ARCH_PPC64
// ===========================================================================
// == ppc & ppc64 ============================================================
// ===========================================================================
96
constexpr Register kGpParamRegisters[] = {r10, r3, r5, r6, r7, r8, r9};
97 98 99 100 101 102 103 104
constexpr Register kGpReturnRegisters[] = {r3, r4};
constexpr DoubleRegister kFpParamRegisters[] = {d1, d2, d3, d4, d5, d6, d7, d8};
constexpr DoubleRegister kFpReturnRegisters[] = {d1, d2};

#elif V8_TARGET_ARCH_S390X
// ===========================================================================
// == s390x ==================================================================
// ===========================================================================
105
constexpr Register kGpParamRegisters[] = {r6, r2, r4, r5};
106 107 108 109 110 111 112 113
constexpr Register kGpReturnRegisters[] = {r2, r3};
constexpr DoubleRegister kFpParamRegisters[] = {d0, d2, d4, d6};
constexpr DoubleRegister kFpReturnRegisters[] = {d0, d2, d4, d6};

#elif V8_TARGET_ARCH_S390
// ===========================================================================
// == s390 ===================================================================
// ===========================================================================
114
constexpr Register kGpParamRegisters[] = {r6, r2, r4, r5};
115 116 117 118
constexpr Register kGpReturnRegisters[] = {r2, r3};
constexpr DoubleRegister kFpParamRegisters[] = {d0, d2};
constexpr DoubleRegister kFpReturnRegisters[] = {d0, d2};

Brice Dobry's avatar
Brice Dobry committed
119 120 121 122 123 124
#elif V8_TARGET_ARCH_RISCV64
// ===========================================================================
// == riscv64 =================================================================
// ===========================================================================
// Note that kGpParamRegisters and kFpParamRegisters are used in
// Builtins::Generate_WasmCompileLazy (builtins-riscv64.cc)
125
constexpr Register kGpParamRegisters[] = {a0, a2, a3, a4, a5, a6, a7};
Brice Dobry's avatar
Brice Dobry committed
126 127 128 129 130
constexpr Register kGpReturnRegisters[] = {a0, a1};
constexpr DoubleRegister kFpParamRegisters[] = {fa0, fa1, fa2, fa3,
                                                fa4, fa5, fa6};
constexpr DoubleRegister kFpReturnRegisters[] = {fa0, fa1};

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
#else
// ===========================================================================
// == unknown ================================================================
// ===========================================================================
// Do not use any registers, we will just always use the stack.
constexpr Register kGpParamRegisters[] = {};
constexpr Register kGpReturnRegisters[] = {};
constexpr DoubleRegister kFpParamRegisters[] = {};
constexpr DoubleRegister kFpReturnRegisters[] = {};

#endif

// The parameter index where the instance parameter should be placed in wasm
// call descriptors. This is used by the Int64Lowering::LowerNode method.
constexpr int kWasmInstanceParameterIndex = 0;

class LinkageAllocator {
 public:
  template <size_t kNumGpRegs, size_t kNumFpRegs>
  constexpr LinkageAllocator(const Register (&gp)[kNumGpRegs],
                             const DoubleRegister (&fp)[kNumFpRegs])
      : LinkageAllocator(gp, kNumGpRegs, fp, kNumFpRegs) {}

  constexpr LinkageAllocator(const Register* gp, int gpc,
                             const DoubleRegister* fp, int fpc)
      : gp_count_(gpc), gp_regs_(gp), fp_count_(fpc), fp_regs_(fp) {}

158 159 160 161
  bool CanAllocateGP() const { return gp_offset_ < gp_count_; }
  bool CanAllocateFP(MachineRepresentation rep) const {
#if V8_TARGET_ARCH_ARM
    switch (rep) {
162 163 164 165 166 167 168 169 170 171 172 173 174 175
      case MachineRepresentation::kFloat32: {
        // Get the next D-register (Liftoff only uses the even S-registers).
        int next = fp_allocator_.NextSlot(2) / 2;
        // Only the lower 16 D-registers alias S-registers.
        return next < fp_count_ && fp_regs_[next].code() < 16;
      }
      case MachineRepresentation::kFloat64: {
        int next = fp_allocator_.NextSlot(2) / 2;
        return next < fp_count_;
      }
      case MachineRepresentation::kSimd128: {
        int next = fp_allocator_.NextSlot(4) / 2;
        return next < fp_count_ - 1;  // 2 D-registers are required.
      }
176 177 178 179
      default:
        UNREACHABLE();
        return false;
    }
180
#else
181
    return fp_offset_ < fp_count_;
182
#endif
183
  }
184

185
  int NextGpReg() {
186
    DCHECK_LT(gp_offset_, gp_count_);
187
    return gp_regs_[gp_offset_++].code();
188 189
  }

190
  int NextFpReg(MachineRepresentation rep) {
191
    DCHECK(CanAllocateFP(rep));
192 193 194
#if V8_TARGET_ARCH_ARM
    switch (rep) {
      case MachineRepresentation::kFloat32: {
195 196 197
        // Liftoff uses only even-numbered S-registers, and encodes them using
        // the code of the corresponding D-register. This limits the calling
        // interface to only using the even-numbered S-registers.
198
        int d_reg_code = NextFpReg(MachineRepresentation::kFloat64);
199
        DCHECK_GT(16, d_reg_code);  // D16 - D31 don't alias S-registers.
200
        return d_reg_code * 2;
201 202
      }
      case MachineRepresentation::kFloat64: {
203 204
        int next = fp_allocator_.Allocate(2) / 2;
        return fp_regs_[next].code();
205 206
      }
      case MachineRepresentation::kSimd128: {
207 208 209 210 211 212
        int next = fp_allocator_.Allocate(4) / 2;
        int d_reg_code = fp_regs_[next].code();
        // Check that result and the next D-register pair.
        DCHECK_EQ(0, d_reg_code % 2);
        DCHECK_EQ(d_reg_code + 1, fp_regs_[next + 1].code());
        return d_reg_code / 2;
213 214 215 216 217 218 219
      }
      default:
        UNREACHABLE();
    }
#else
    return fp_regs_[fp_offset_++].code();
#endif
220 221 222
  }

  // Stackslots are counted upwards starting from 0 (or the offset set by
223 224
  // {SetStackOffset}. If {type} needs more than one stack slot, the lowest
  // used stack slot is returned.
225
  int NextStackSlot(MachineRepresentation type) {
226 227 228 229
    int num_slots =
        AlignedSlotAllocator::NumSlotsForWidth(ElementSizeInBytes(type));
    int slot = slot_allocator_.Allocate(num_slots);
    return slot;
230 231 232 233
  }

  // Set an offset for the stack slots returned by {NextStackSlot} and
  // {NumStackSlots}. Can only be called before any call to {NextStackSlot}.
234 235 236 237
  void SetStackOffset(int offset) {
    DCHECK_LE(0, offset);
    DCHECK_EQ(0, slot_allocator_.Size());
    slot_allocator_.AllocateUnaligned(offset);
238 239
  }

240 241 242
  int NumStackSlots() const { return slot_allocator_.Size(); }

  void EndSlotArea() { slot_allocator_.AllocateUnaligned(0); }
243 244 245 246 247 248 249

 private:
  const int gp_count_;
  int gp_offset_ = 0;
  const Register* const gp_regs_;

  const int fp_count_;
250
#if V8_TARGET_ARCH_ARM
251 252 253 254 255
  // Use an aligned slot allocator to model ARM FP register aliasing. The slots
  // are 32 bits, so 2 slots are required for a D-register, 4 for a Q-register.
  AlignedSlotAllocator fp_allocator_;
#else
  int fp_offset_ = 0;
256
#endif
257
  const DoubleRegister* const fp_regs_;
258

259
  AlignedSlotAllocator slot_allocator_;
260 261 262 263 264 265 266
};

}  // namespace wasm
}  // namespace internal
}  // namespace v8

#endif  // V8_WASM_WASM_LINKAGE_H_