summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/baseline/liftoff-assembler.h
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/baseline/liftoff-assembler.h')
-rw-r--r--deps/v8/src/wasm/baseline/liftoff-assembler.h348
1 files changed, 202 insertions, 146 deletions
diff --git a/deps/v8/src/wasm/baseline/liftoff-assembler.h b/deps/v8/src/wasm/baseline/liftoff-assembler.h
index 55deb593f8..b91f6d7c88 100644
--- a/deps/v8/src/wasm/baseline/liftoff-assembler.h
+++ b/deps/v8/src/wasm/baseline/liftoff-assembler.h
@@ -5,38 +5,21 @@
#ifndef V8_WASM_BASELINE_LIFTOFF_ASSEMBLER_H_
#define V8_WASM_BASELINE_LIFTOFF_ASSEMBLER_H_
+#include <iosfwd>
#include <memory>
// Clients of this interface shouldn't depend on lots of compiler internals.
// Do not include anything from src/compiler here!
+#include "src/base/bits.h"
#include "src/frames.h"
#include "src/macro-assembler.h"
+#include "src/wasm/baseline/liftoff-assembler-defs.h"
+#include "src/wasm/baseline/liftoff-register.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-value.h"
-// Include platform specific definitions.
-#if V8_TARGET_ARCH_IA32
-#include "src/wasm/baseline/ia32/liftoff-assembler-ia32-defs.h"
-#elif V8_TARGET_ARCH_X64
-#include "src/wasm/baseline/x64/liftoff-assembler-x64-defs.h"
-#elif V8_TARGET_ARCH_ARM64
-#include "src/wasm/baseline/arm64/liftoff-assembler-arm64-defs.h"
-#elif V8_TARGET_ARCH_ARM
-#include "src/wasm/baseline/arm/liftoff-assembler-arm-defs.h"
-#elif V8_TARGET_ARCH_PPC
-#include "src/wasm/baseline/ppc/liftoff-assembler-ppc-defs.h"
-#elif V8_TARGET_ARCH_MIPS
-#include "src/wasm/baseline/mips/liftoff-assembler-mips-defs.h"
-#elif V8_TARGET_ARCH_MIPS64
-#include "src/wasm/baseline/mips64/liftoff-assembler-mips64-defs.h"
-#elif V8_TARGET_ARCH_S390
-#include "src/wasm/baseline/s390/liftoff-assembler-s390-defs.h"
-#else
-#error Unsupported architecture.
-#endif
-
namespace v8 {
namespace internal {
namespace wasm {
@@ -44,51 +27,26 @@ namespace wasm {
// Forward declarations.
struct ModuleEnv;
-enum RegClass { kNoReg, kGpReg, kFpReg };
-
-// TODO(clemensh): Switch to a switch once we require C++14 support.
-static constexpr RegClass reg_class_for(ValueType type) {
- return type == kWasmI32 || type == kWasmI64 // int types
- ? kGpReg
- : type == kWasmF32 || type == kWasmF64 // float types
- ? kFpReg
- : kNoReg; // other (unsupported) types
-}
-
class LiftoffAssembler : public TurboAssembler {
public:
// TODO(clemensh): Remove this limitation by allocating more stack space if
// needed.
static constexpr int kMaxValueStackHeight = 8;
- class PinnedRegisterScope {
- public:
- PinnedRegisterScope() : pinned_regs_(0) {}
- explicit PinnedRegisterScope(RegList regs) : pinned_regs_(regs) {}
-
- Register pin(Register reg) {
- pinned_regs_ |= reg.bit();
- return reg;
- }
-
- RegList pinned_regs() const { return pinned_regs_; }
- bool has(Register reg) const { return (pinned_regs_ & reg.bit()) != 0; }
-
- private:
- RegList pinned_regs_ = 0;
- };
- static_assert(IS_TRIVIALLY_COPYABLE(PinnedRegisterScope),
- "PinnedRegisterScope can be passed by value");
+ // Each slot in our stack frame currently has exactly 8 bytes.
+ static constexpr uint32_t kStackSlotSize = 8;
class VarState {
public:
- enum Location : uint8_t { kStack, kRegister, kConstant };
+ enum Location : uint8_t { kStack, kRegister, kI32Const };
explicit VarState(ValueType type) : loc_(kStack), type_(type) {}
- explicit VarState(ValueType type, Register r)
- : loc_(kRegister), type_(type), reg_(r) {}
+ explicit VarState(ValueType type, LiftoffRegister r)
+ : loc_(kRegister), type_(type), reg_(r) {
+ DCHECK_EQ(r.reg_class(), reg_class_for(type));
+ }
explicit VarState(ValueType type, uint32_t i32_const)
- : loc_(kConstant), type_(type), i32_const_(i32_const) {
+ : loc_(kI32Const), type_(type), i32_const_(i32_const) {
DCHECK(type_ == kWasmI32 || type_ == kWasmI64);
}
@@ -99,29 +57,33 @@ class LiftoffAssembler : public TurboAssembler {
return true;
case kRegister:
return reg_ == other.reg_;
- case kConstant:
+ case kI32Const:
return i32_const_ == other.i32_const_;
}
UNREACHABLE();
}
bool is_stack() const { return loc_ == kStack; }
+ bool is_gp_reg() const { return loc_ == kRegister && reg_.is_gp(); }
+ bool is_fp_reg() const { return loc_ == kRegister && reg_.is_fp(); }
bool is_reg() const { return loc_ == kRegister; }
- bool is_const() const { return loc_ == kConstant; }
+ bool is_const() const { return loc_ == kI32Const; }
ValueType type() const { return type_; }
Location loc() const { return loc_; }
uint32_t i32_const() const {
- DCHECK_EQ(loc_, kConstant);
+ DCHECK_EQ(loc_, kI32Const);
return i32_const_;
}
-
- Register reg() const {
+ Register gp_reg() const { return reg().gp(); }
+ DoubleRegister fp_reg() const { return reg().fp(); }
+ LiftoffRegister reg() const {
DCHECK_EQ(loc_, kRegister);
return reg_;
}
+ RegClass reg_class() const { return reg().reg_class(); }
void MakeStack() { loc_ = kStack; }
@@ -132,10 +94,11 @@ class LiftoffAssembler : public TurboAssembler {
ValueType type_;
union {
- Register reg_; // used if loc_ == kRegister
- uint32_t i32_const_; // used if loc_ == kConstant
+ LiftoffRegister reg_; // used if loc_ == kRegister
+ uint32_t i32_const_; // used if loc_ == kI32Const
};
};
+
static_assert(IS_TRIVIALLY_COPYABLE(VarState),
"VarState should be trivially copyable");
@@ -147,80 +110,102 @@ class LiftoffAssembler : public TurboAssembler {
// TODO(clemensh): Improve memory management here; avoid std::vector.
std::vector<VarState> stack_state;
- RegList used_registers = 0;
- // TODO(clemensh): Replace this by CountLeadingZeros(kGpCacheRegs) once that
- // method is constexpr.
- static constexpr int kMaxRegisterCode = 7;
- uint32_t register_use_count[kMaxRegisterCode + 1] = {0};
+ LiftoffRegList used_registers;
+ uint32_t register_use_count[kAfterMaxLiftoffRegCode] = {0};
+ LiftoffRegList last_spilled_regs;
// TODO(clemensh): Remove stack_base; use ControlBase::stack_depth.
uint32_t stack_base = 0;
- Register last_spilled_reg = Register::from_code<0>();
-
- // InitMerge: Initialize this CacheState from the {source} cache state, but
- // make sure that other code paths can still jump here (i.e. avoid constants
- // in the locals or the merge region as specified by {arity}).
- // TODO(clemensh): Don't copy the full parent state (this makes us N^2).
- void InitMerge(const CacheState& source, uint32_t num_locals,
- uint32_t arity);
- void Steal(CacheState& source);
+ bool has_unused_register(RegClass rc, LiftoffRegList pinned = {}) const {
+ DCHECK(rc == kGpReg || rc == kFpReg);
+ LiftoffRegList candidates = GetCacheRegList(rc);
+ return has_unused_register(candidates, pinned);
+ }
- void Split(const CacheState& source);
+ bool has_unused_register(LiftoffRegList candidates,
+ LiftoffRegList pinned = {}) const {
+ LiftoffRegList available_regs = candidates & ~used_registers & ~pinned;
+ return !available_regs.is_empty();
+ }
- bool has_unused_register(PinnedRegisterScope pinned_scope = {}) const {
- RegList available_regs =
- kGpCacheRegs & ~used_registers & ~pinned_scope.pinned_regs();
- return available_regs != 0;
+ LiftoffRegister unused_register(RegClass rc,
+ LiftoffRegList pinned = {}) const {
+ DCHECK(rc == kGpReg || rc == kFpReg);
+ LiftoffRegList candidates = GetCacheRegList(rc);
+ return unused_register(candidates);
}
- Register unused_register(PinnedRegisterScope pinned_scope = {}) const {
- RegList available_regs =
- kGpCacheRegs & ~used_registers & ~pinned_scope.pinned_regs();
- Register reg =
- Register::from_code(base::bits::CountTrailingZeros(available_regs));
- DCHECK_EQ(0, used_registers & reg.bit());
- return reg;
+ LiftoffRegister unused_register(LiftoffRegList candidates,
+ LiftoffRegList pinned = {}) const {
+ LiftoffRegList available_regs = candidates & ~used_registers & ~pinned;
+ return available_regs.GetFirstRegSet();
}
- void inc_used(Register reg) {
- used_registers |= reg.bit();
- DCHECK_GE(kMaxRegisterCode, reg.code());
- ++register_use_count[reg.code()];
+ void inc_used(LiftoffRegister reg) {
+ used_registers.set(reg);
+ DCHECK_GT(kMaxInt, register_use_count[reg.liftoff_code()]);
+ ++register_use_count[reg.liftoff_code()];
}
// Returns whether this was the last use.
- bool dec_used(Register reg) {
+ bool dec_used(LiftoffRegister reg) {
DCHECK(is_used(reg));
- DCHECK_GE(kMaxRegisterCode, reg.code());
- if (--register_use_count[reg.code()] == 0) {
- used_registers &= ~reg.bit();
- return true;
- }
- return false;
+ int code = reg.liftoff_code();
+ DCHECK_LT(0, register_use_count[code]);
+ if (--register_use_count[code] != 0) return false;
+ used_registers.clear(reg);
+ return true;
}
- bool is_used(Register reg) const {
- DCHECK_GE(kMaxRegisterCode, reg.code());
- bool used = used_registers & reg.bit();
- DCHECK_EQ(used, register_use_count[reg.code()] != 0);
+ bool is_used(LiftoffRegister reg) const {
+ bool used = used_registers.has(reg);
+ DCHECK_EQ(used, register_use_count[reg.liftoff_code()] != 0);
return used;
}
- bool is_free(Register reg) const { return !is_used(reg); }
+ uint32_t get_use_count(LiftoffRegister reg) const {
+ DCHECK_GT(arraysize(register_use_count), reg.liftoff_code());
+ return register_use_count[reg.liftoff_code()];
+ }
+
+ void clear_used(LiftoffRegister reg) {
+ register_use_count[reg.liftoff_code()] = 0;
+ used_registers.clear(reg);
+ }
- uint32_t stack_height() const {
- return static_cast<uint32_t>(stack_state.size());
+ bool is_free(LiftoffRegister reg) const { return !is_used(reg); }
+
+ void reset_used_registers() {
+ used_registers = {};
+ memset(register_use_count, 0, sizeof(register_use_count));
+ }
+
+ LiftoffRegister GetNextSpillReg(LiftoffRegList candidates,
+ LiftoffRegList pinned = {}) {
+ LiftoffRegList unpinned = candidates.MaskOut(pinned);
+ DCHECK(!unpinned.is_empty());
+ // This method should only be called if none of the candidates is free.
+ DCHECK(unpinned.MaskOut(used_registers).is_empty());
+ LiftoffRegList unspilled = unpinned.MaskOut(last_spilled_regs);
+ if (unspilled.is_empty()) {
+ unspilled = unpinned;
+ last_spilled_regs = {};
+ }
+ LiftoffRegister reg = unspilled.GetFirstRegSet();
+ last_spilled_regs.set(reg);
+ return reg;
}
- Register GetNextSpillReg(PinnedRegisterScope scope = {}) {
- uint32_t mask = (1u << (last_spilled_reg.code() + 1)) - 1;
- RegList unpinned_regs = kGpCacheRegs & ~scope.pinned_regs();
- DCHECK_NE(0, unpinned_regs);
- RegList remaining_regs = unpinned_regs & ~mask;
- if (!remaining_regs) remaining_regs = unpinned_regs;
- last_spilled_reg =
- Register::from_code(base::bits::CountTrailingZeros(remaining_regs));
- return last_spilled_reg;
+ // TODO(clemensh): Don't copy the full parent state (this makes us N^2).
+ void InitMerge(const CacheState& source, uint32_t num_locals,
+ uint32_t arity);
+
+ void Steal(CacheState& source);
+
+ void Split(const CacheState& source);
+
+ uint32_t stack_height() const {
+ return static_cast<uint32_t>(stack_state.size());
}
private:
@@ -233,27 +218,39 @@ class LiftoffAssembler : public TurboAssembler {
explicit LiftoffAssembler(Isolate* isolate);
~LiftoffAssembler();
- Register GetBinaryOpTargetRegister(RegClass, PinnedRegisterScope = {});
+ LiftoffRegister GetBinaryOpTargetRegister(RegClass,
+ LiftoffRegList pinned = {});
+ LiftoffRegister GetUnaryOpTargetRegister(RegClass,
+ LiftoffRegList pinned = {});
- Register PopToRegister(RegClass, PinnedRegisterScope = {});
+ LiftoffRegister PopToRegister(RegClass, LiftoffRegList pinned = {});
- void PushRegister(ValueType type, Register reg) {
+ void PushRegister(ValueType type, LiftoffRegister reg) {
+ DCHECK_EQ(reg_class_for(type), reg.reg_class());
cache_state_.inc_used(reg);
cache_state_.stack_state.emplace_back(type, reg);
}
- uint32_t GetNumUses(Register reg) const {
- DCHECK_GE(CacheState::kMaxRegisterCode, reg.code());
- return cache_state_.register_use_count[reg.code()];
+ void SpillRegister(LiftoffRegister);
+
+ uint32_t GetNumUses(LiftoffRegister reg) {
+ return cache_state_.get_use_count(reg);
}
- Register GetUnusedRegister(RegClass rc,
- PinnedRegisterScope pinned_regs = {}) {
- DCHECK_EQ(kGpReg, rc);
- if (cache_state_.has_unused_register(pinned_regs)) {
- return cache_state_.unused_register(pinned_regs);
+ // Get an unused register for class {rc}, potentially spilling to free one.
+ LiftoffRegister GetUnusedRegister(RegClass rc, LiftoffRegList pinned = {}) {
+ DCHECK(rc == kGpReg || rc == kFpReg);
+ LiftoffRegList candidates = GetCacheRegList(rc);
+ return GetUnusedRegister(candidates, pinned);
+ }
+
+ // Get an unused register of {candidates}, potentially spilling to free one.
+ LiftoffRegister GetUnusedRegister(LiftoffRegList candidates,
+ LiftoffRegList pinned = {}) {
+ if (cache_state_.has_unused_register(candidates, pinned)) {
+ return cache_state_.unused_register(candidates, pinned);
}
- return SpillOneRegister(rc, pinned_regs);
+ return SpillOneRegister(candidates, pinned);
}
void DropStackSlot(VarState* slot) {
@@ -271,40 +268,102 @@ class LiftoffAssembler : public TurboAssembler {
void Spill(uint32_t index);
void SpillLocals();
+ void SpillAllRegisters();
+
+ // Load parameters into the right registers / stack slots for the call.
+ void PrepareCall(wasm::FunctionSig*, compiler::CallDescriptor*);
+ // Process return values of the call.
+ void FinishCall(wasm::FunctionSig*, compiler::CallDescriptor*);
////////////////////////////////////
// Platform-specific part. //
////////////////////////////////////
- inline void ReserveStackSpace(uint32_t);
+ inline void ReserveStackSpace(uint32_t bytes);
- inline void LoadConstant(Register, WasmValue);
+ inline void LoadConstant(LiftoffRegister, WasmValue);
inline void LoadFromContext(Register dst, uint32_t offset, int size);
inline void SpillContext(Register context);
- inline void Load(Register dst, Register src_addr, uint32_t offset_imm,
- int size, PinnedRegisterScope = {});
- inline void Store(Register dst_addr, uint32_t offset_imm, Register src,
- int size, PinnedRegisterScope = {});
- inline void LoadCallerFrameSlot(Register, uint32_t caller_slot_idx);
+ inline void FillContextInto(Register dst);
+ inline void Load(LiftoffRegister dst, Register src_addr, Register offset_reg,
+ uint32_t offset_imm, LoadType type, LiftoffRegList pinned,
+ uint32_t* protected_load_pc = nullptr);
+ inline void Store(Register dst_addr, Register offset_reg, uint32_t offset_imm,
+ LiftoffRegister src, StoreType type, LiftoffRegList pinned,
+ uint32_t* protected_store_pc = nullptr);
+ inline void LoadCallerFrameSlot(LiftoffRegister, uint32_t caller_slot_idx);
inline void MoveStackValue(uint32_t dst_index, uint32_t src_index);
- inline void MoveToReturnRegister(Register);
+ inline void MoveToReturnRegister(LiftoffRegister);
+ // TODO(clemensh): Pass the type to {Move}, to emit more efficient code.
+ inline void Move(LiftoffRegister dst, LiftoffRegister src);
- inline void Spill(uint32_t index, Register);
+ inline void Spill(uint32_t index, LiftoffRegister);
inline void Spill(uint32_t index, WasmValue);
- inline void Fill(Register, uint32_t index);
+ inline void Fill(LiftoffRegister, uint32_t index);
+ // i32 binops.
inline void emit_i32_add(Register dst, Register lhs, Register rhs);
inline void emit_i32_sub(Register dst, Register lhs, Register rhs);
inline void emit_i32_mul(Register dst, Register lhs, Register rhs);
inline void emit_i32_and(Register dst, Register lhs, Register rhs);
inline void emit_i32_or(Register dst, Register lhs, Register rhs);
inline void emit_i32_xor(Register dst, Register lhs, Register rhs);
+ inline void emit_i32_shl(Register dst, Register lhs, Register rhs);
+ inline void emit_i32_sar(Register dst, Register lhs, Register rhs);
+ inline void emit_i32_shr(Register dst, Register lhs, Register rhs);
+
+ // i32 unops.
+ inline bool emit_i32_eqz(Register dst, Register src);
+ inline bool emit_i32_clz(Register dst, Register src);
+ inline bool emit_i32_ctz(Register dst, Register src);
+ inline bool emit_i32_popcnt(Register dst, Register src);
+
+ inline void emit_ptrsize_add(Register dst, Register lhs, Register rhs);
+
+ inline void emit_f32_add(DoubleRegister dst, DoubleRegister lhs,
+ DoubleRegister rhs);
+ inline void emit_f32_sub(DoubleRegister dst, DoubleRegister lhs,
+ DoubleRegister rhs);
+ inline void emit_f32_mul(DoubleRegister dst, DoubleRegister lhs,
+ DoubleRegister rhs);
+
+ inline void emit_i32_test(Register);
+ inline void emit_i32_compare(Register, Register);
+ inline void emit_jump(Label*);
+ inline void emit_cond_jump(Condition, Label*);
- inline void JumpIfZero(Register, Label*);
+ inline void StackCheck(Label* ool_code);
- // Platform-specific constant.
- static constexpr RegList kGpCacheRegs = kLiftoffAssemblerGpCacheRegs;
+ inline void CallTrapCallbackForTesting();
+
+ inline void AssertUnreachable(AbortReason reason);
+
+ // Push a value to the stack (will become a caller frame slot).
+ inline void PushCallerFrameSlot(const VarState& src, uint32_t src_index);
+ inline void PushCallerFrameSlot(LiftoffRegister reg);
+ inline void PushRegisters(LiftoffRegList);
+ inline void PopRegisters(LiftoffRegList);
+
+ inline void DropStackSlotsAndRet(uint32_t num_stack_slots);
+
+ // Push arguments on the stack (in the caller frame), then align the stack.
+ // The address of the last argument will be stored to {arg_addr_dst}. Previous
+ // arguments will be located at pointer sized buckets above that address.
+ inline void PrepareCCall(uint32_t num_params, const Register* args);
+ inline void SetCCallRegParamAddr(Register dst, uint32_t param_idx,
+ uint32_t num_params);
+ inline void SetCCallStackParamAddr(uint32_t stack_param_idx,
+ uint32_t param_idx, uint32_t num_params);
+ inline void CallC(ExternalReference ext_ref, uint32_t num_params);
+
+ inline void CallNativeWasmCode(Address addr);
+
+ inline void CallRuntime(Zone* zone, Runtime::FunctionId fid);
+
+ // Reserve space in the current frame, store address to space in {addr}.
+ inline void AllocateStackSlot(Register addr, uint32_t size);
+ inline void DeallocateStackSlot(uint32_t size);
////////////////////////////////////
// End of platform-specific part. //
@@ -314,7 +373,6 @@ class LiftoffAssembler : public TurboAssembler {
void set_num_locals(uint32_t num_locals);
uint32_t GetTotalFrameSlotCount() const;
- size_t GetSafepointTableOffset() const { return 0; }
ValueType local_type(uint32_t index) {
DCHECK_GT(num_locals_, index);
@@ -332,12 +390,7 @@ class LiftoffAssembler : public TurboAssembler {
CacheState* cache_state() { return &cache_state_; }
private:
- static_assert(
- base::bits::CountPopulation(kGpCacheRegs) >= 2,
- "We need at least two cache registers to execute binary operations");
-
uint32_t num_locals_ = 0;
- uint32_t stack_space_ = 0;
static constexpr uint32_t kInlineLocalTypes = 8;
union {
ValueType local_types_[kInlineLocalTypes];
@@ -347,9 +400,12 @@ class LiftoffAssembler : public TurboAssembler {
"Reconsider this inlining if ValueType gets bigger");
CacheState cache_state_;
- Register SpillOneRegister(RegClass, PinnedRegisterScope = {});
+ LiftoffRegister SpillOneRegister(LiftoffRegList candidates,
+ LiftoffRegList pinned);
};
+std::ostream& operator<<(std::ostream& os, LiftoffAssembler::VarState);
+
} // namespace wasm
} // namespace internal
} // namespace v8