Commit da159ccc authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[Liftoff] Support i64 constants

For local variables of type i64 and also for i64 constants, it makes
sense to store the constant value in the {VarState} instead of loading
the value into a register immediately. This also helps with some
instructions like i64 bitshifts, but also general patterns like
incrementing an i64 local variable by a fixed number.

R=ahaas@chromium.org

Bug: v8:6600
Change-Id: Ibed15228bbc53632dd3e60d7862ff2fbcb9832ca
Reviewed-on: https://chromium-review.googlesource.com/904443
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51179}
parent 1c10c4a2
...@@ -545,8 +545,10 @@ void LiftoffAssembler::PushCallerFrameSlot(const VarState& src, ...@@ -545,8 +545,10 @@ void LiftoffAssembler::PushCallerFrameSlot(const VarState& src,
? (half == kLowWord ? src.reg().low() : src.reg().high()) ? (half == kLowWord ? src.reg().low() : src.reg().high())
: src.reg()); : src.reg());
break; break;
case VarState::kI32Const: case VarState::KIntConst:
push(Immediate(src.i32_const())); // The high word is the sign extension of the low word.
push(Immediate(half == kLowWord ? src.i32_const()
: src.i32_const() >> 31));
break; break;
} }
} }
......
...@@ -42,26 +42,33 @@ class StackTransferRecipe { ...@@ -42,26 +42,33 @@ class StackTransferRecipe {
kStack, // fill a register from a stack slot. kStack, // fill a register from a stack slot.
kHalfStack // fill one half of a register pair from half a stack slot. kHalfStack // fill one half of a register pair from half a stack slot.
}; };
LiftoffRegister dst; LiftoffRegister dst;
LoadKind kind; LoadKind kind;
ValueType type; ValueType type;
uint32_t value; // i32 constant value or stack index, depending on kind. int32_t value; // i32 constant value or stack index, depending on kind.
// Named constructors. // Named constructors.
static RegisterLoad Const(LiftoffRegister dst, WasmValue constant) { static RegisterLoad Const(LiftoffRegister dst, WasmValue constant) {
return {dst, kConstant, kWasmI32, constant.to_u32()}; if (constant.type() == kWasmI32) {
return {dst, kConstant, kWasmI32, constant.to_i32()};
}
DCHECK_EQ(kWasmI64, constant.type());
DCHECK_EQ(constant.to_i32_unchecked(), constant.to_i64_unchecked());
return {dst, kConstant, kWasmI64, constant.to_i32_unchecked()};
} }
static RegisterLoad Stack(LiftoffRegister dst, uint32_t stack_index, static RegisterLoad Stack(LiftoffRegister dst, int32_t stack_index,
ValueType type) { ValueType type) {
return {dst, kStack, type, stack_index}; return {dst, kStack, type, stack_index};
} }
static RegisterLoad HalfStack(LiftoffRegister dst, static RegisterLoad HalfStack(LiftoffRegister dst,
uint32_t half_stack_index) { int32_t half_stack_index) {
return {dst, kHalfStack, kWasmI32, half_stack_index}; return {dst, kHalfStack, kWasmI32, half_stack_index};
} }
private: private:
RegisterLoad(LiftoffRegister dst, LoadKind kind, ValueType type, RegisterLoad(LiftoffRegister dst, LoadKind kind, ValueType type,
uint32_t value) int32_t value)
: dst(dst), kind(kind), type(type), value(value) {} : dst(dst), kind(kind), type(type), value(value) {}
}; };
...@@ -128,7 +135,9 @@ class StackTransferRecipe { ...@@ -128,7 +135,9 @@ class StackTransferRecipe {
for (RegisterLoad& rl : register_loads_) { for (RegisterLoad& rl : register_loads_) {
switch (rl.kind) { switch (rl.kind) {
case RegisterLoad::kConstant: case RegisterLoad::kConstant:
asm_->LoadConstant(rl.dst, WasmValue(rl.value)); asm_->LoadConstant(rl.dst, rl.type == kWasmI64
? WasmValue(int64_t{rl.value})
: WasmValue(int32_t{rl.value}));
break; break;
case RegisterLoad::kStack: case RegisterLoad::kStack:
asm_->Fill(rl.dst, rl.value, rl.type); asm_->Fill(rl.dst, rl.value, rl.type);
...@@ -157,15 +166,15 @@ class StackTransferRecipe { ...@@ -157,15 +166,15 @@ class StackTransferRecipe {
case VarState::kRegister: case VarState::kRegister:
asm_->Spill(dst_index, src.reg(), src.type()); asm_->Spill(dst_index, src.reg(), src.type());
break; break;
case VarState::kI32Const: case VarState::KIntConst:
asm_->Spill(dst_index, WasmValue(src.i32_const())); asm_->Spill(dst_index, src.constant());
break; break;
} }
break; break;
case VarState::kRegister: case VarState::kRegister:
LoadIntoRegister(dst.reg(), src, src_index); LoadIntoRegister(dst.reg(), src, src_index);
break; break;
case VarState::kI32Const: case VarState::KIntConst:
DCHECK_EQ(dst, src); DCHECK_EQ(dst, src);
break; break;
} }
...@@ -182,8 +191,8 @@ class StackTransferRecipe { ...@@ -182,8 +191,8 @@ class StackTransferRecipe {
DCHECK_EQ(dst.reg_class(), src.reg_class()); DCHECK_EQ(dst.reg_class(), src.reg_class());
if (dst != src.reg()) MoveRegister(dst, src.reg(), src.type()); if (dst != src.reg()) MoveRegister(dst, src.reg(), src.type());
break; break;
case VarState::kI32Const: case VarState::KIntConst:
LoadConstant(dst, WasmValue(src.i32_const())); LoadConstant(dst, src.constant());
break; break;
} }
} }
...@@ -205,8 +214,10 @@ class StackTransferRecipe { ...@@ -205,8 +214,10 @@ class StackTransferRecipe {
if (dst != src_half) MoveRegister(dst, src_half, kWasmI32); if (dst != src_half) MoveRegister(dst, src_half, kWasmI32);
break; break;
} }
case VarState::kI32Const: case VarState::KIntConst:
int32_t value = half == kLowWord ? src.i32_const() : 0; int32_t value = src.i32_const();
// The high word is the sign extension of the low word.
if (half == kHighWord) value = value >> 31;
LoadConstant(dst, WasmValue(value)); LoadConstant(dst, WasmValue(value));
break; break;
} }
...@@ -382,9 +393,9 @@ LiftoffRegister LiftoffAssembler::PopToRegister(RegClass rc, ...@@ -382,9 +393,9 @@ LiftoffRegister LiftoffAssembler::PopToRegister(RegClass rc,
DCHECK_EQ(rc, slot.reg_class()); DCHECK_EQ(rc, slot.reg_class());
cache_state_.dec_used(slot.reg()); cache_state_.dec_used(slot.reg());
return slot.reg(); return slot.reg();
case VarState::kI32Const: { case VarState::KIntConst: {
LiftoffRegister reg = GetUnusedRegister(rc, pinned); LiftoffRegister reg = GetUnusedRegister(rc, pinned);
LoadConstant(reg, WasmValue(slot.i32_const())); LoadConstant(reg, slot.constant());
return reg; return reg;
} }
} }
...@@ -429,8 +440,8 @@ void LiftoffAssembler::Spill(uint32_t index) { ...@@ -429,8 +440,8 @@ void LiftoffAssembler::Spill(uint32_t index) {
Spill(index, slot.reg(), slot.type()); Spill(index, slot.reg(), slot.type());
cache_state_.dec_used(slot.reg()); cache_state_.dec_used(slot.reg());
break; break;
case VarState::kI32Const: case VarState::KIntConst:
Spill(index, WasmValue(slot.i32_const())); Spill(index, slot.constant());
break; break;
} }
slot.MakeStack(); slot.MakeStack();
...@@ -619,7 +630,7 @@ std::ostream& operator<<(std::ostream& os, VarState slot) { ...@@ -619,7 +630,7 @@ std::ostream& operator<<(std::ostream& os, VarState slot) {
return os << "s"; return os << "s";
case VarState::kRegister: case VarState::kRegister:
return os << slot.reg(); return os << slot.reg();
case VarState::kI32Const: case VarState::KIntConst:
return os << "c" << slot.i32_const(); return os << "c" << slot.i32_const();
} }
UNREACHABLE(); UNREACHABLE();
......
...@@ -36,26 +36,27 @@ class LiftoffAssembler : public TurboAssembler { ...@@ -36,26 +36,27 @@ class LiftoffAssembler : public TurboAssembler {
class VarState { class VarState {
public: public:
enum Location : uint8_t { kStack, kRegister, kI32Const }; enum Location : uint8_t { kStack, kRegister, KIntConst };
explicit VarState(ValueType type) : loc_(kStack), type_(type) {} explicit VarState(ValueType type) : loc_(kStack), type_(type) {}
explicit VarState(ValueType type, LiftoffRegister r) explicit VarState(ValueType type, LiftoffRegister r)
: loc_(kRegister), type_(type), reg_(r) { : loc_(kRegister), type_(type), reg_(r) {
DCHECK_EQ(r.reg_class(), reg_class_for(type)); DCHECK_EQ(r.reg_class(), reg_class_for(type));
} }
explicit VarState(ValueType type, uint32_t i32_const) explicit VarState(ValueType type, int32_t i32_const)
: loc_(kI32Const), type_(type), i32_const_(i32_const) { : loc_(KIntConst), type_(type), i32_const_(i32_const) {
DCHECK(type_ == kWasmI32 || type_ == kWasmI64); DCHECK(type_ == kWasmI32 || type_ == kWasmI64);
} }
bool operator==(const VarState& other) const { bool operator==(const VarState& other) const {
if (loc_ != other.loc_) return false; if (loc_ != other.loc_) return false;
if (type_ != other.type_) return false;
switch (loc_) { switch (loc_) {
case kStack: case kStack:
return true; return true;
case kRegister: case kRegister:
return reg_ == other.reg_; return reg_ == other.reg_;
case kI32Const: case KIntConst:
return i32_const_ == other.i32_const_; return i32_const_ == other.i32_const_;
} }
UNREACHABLE(); UNREACHABLE();
...@@ -65,16 +66,23 @@ class LiftoffAssembler : public TurboAssembler { ...@@ -65,16 +66,23 @@ class LiftoffAssembler : public TurboAssembler {
bool is_gp_reg() const { return loc_ == kRegister && reg_.is_gp(); } bool is_gp_reg() const { return loc_ == kRegister && reg_.is_gp(); }
bool is_fp_reg() const { return loc_ == kRegister && reg_.is_fp(); } bool is_fp_reg() const { return loc_ == kRegister && reg_.is_fp(); }
bool is_reg() const { return loc_ == kRegister; } bool is_reg() const { return loc_ == kRegister; }
bool is_const() const { return loc_ == kI32Const; } bool is_const() const { return loc_ == KIntConst; }
ValueType type() const { return type_; } ValueType type() const { return type_; }
Location loc() const { return loc_; } Location loc() const { return loc_; }
uint32_t i32_const() const { int32_t i32_const() const {
DCHECK_EQ(loc_, kI32Const); DCHECK_EQ(loc_, KIntConst);
return i32_const_; return i32_const_;
} }
WasmValue constant() const {
DCHECK(type_ == kWasmI32 || type_ == kWasmI64);
DCHECK_EQ(loc_, KIntConst);
return type_ == kWasmI32 ? WasmValue(i32_const_)
: WasmValue(int64_t{i32_const_});
}
Register gp_reg() const { return reg().gp(); } Register gp_reg() const { return reg().gp(); }
DoubleRegister fp_reg() const { return reg().fp(); } DoubleRegister fp_reg() const { return reg().fp(); }
LiftoffRegister reg() const { LiftoffRegister reg() const {
...@@ -93,7 +101,7 @@ class LiftoffAssembler : public TurboAssembler { ...@@ -93,7 +101,7 @@ class LiftoffAssembler : public TurboAssembler {
union { union {
LiftoffRegister reg_; // used if loc_ == kRegister LiftoffRegister reg_; // used if loc_ == kRegister
uint32_t i32_const_; // used if loc_ == kI32Const int32_t i32_const_; // used if loc_ == KIntConst
}; };
}; };
......
...@@ -20,7 +20,7 @@ namespace internal { ...@@ -20,7 +20,7 @@ namespace internal {
namespace wasm { namespace wasm {
constexpr auto kRegister = LiftoffAssembler::VarState::kRegister; constexpr auto kRegister = LiftoffAssembler::VarState::kRegister;
constexpr auto kI32Const = LiftoffAssembler::VarState::kI32Const; constexpr auto KIntConst = LiftoffAssembler::VarState::KIntConst;
constexpr auto kStack = LiftoffAssembler::VarState::kStack; constexpr auto kStack = LiftoffAssembler::VarState::kStack;
namespace { namespace {
...@@ -616,9 +616,18 @@ class LiftoffCompiler { ...@@ -616,9 +616,18 @@ class LiftoffCompiler {
} }
void I64Const(Decoder* decoder, Value* result, int64_t value) { void I64Const(Decoder* decoder, Value* result, int64_t value) {
// The {VarState} stores constant values as int32_t, thus we only store
// 64-bit constants in this field if it fits in an int32_t. Larger values
// cannot be used as immediate value anyway, so we can also just put them in
// a register immediately.
int32_t value_i32 = static_cast<int32_t>(value);
if (value_i32 == value) {
__ cache_state()->stack_state.emplace_back(kWasmI64, value_i32);
} else {
LiftoffRegister reg = __ GetUnusedRegister(reg_class_for(kWasmI64)); LiftoffRegister reg = __ GetUnusedRegister(reg_class_for(kWasmI64));
__ LoadConstant(reg, WasmValue(value)); __ LoadConstant(reg, WasmValue(value));
__ PushRegister(kWasmI64, reg); __ PushRegister(kWasmI64, reg);
}
CheckStackSizeLimit(decoder); CheckStackSizeLimit(decoder);
} }
...@@ -664,7 +673,7 @@ class LiftoffCompiler { ...@@ -664,7 +673,7 @@ class LiftoffCompiler {
case kRegister: case kRegister:
__ PushRegister(slot.type(), slot.reg()); __ PushRegister(slot.type(), slot.reg());
break; break;
case kI32Const: case KIntConst:
__ cache_state()->stack_state.emplace_back(operand.type, __ cache_state()->stack_state.emplace_back(operand.type,
slot.i32_const()); slot.i32_const());
break; break;
...@@ -709,7 +718,7 @@ class LiftoffCompiler { ...@@ -709,7 +718,7 @@ class LiftoffCompiler {
target_slot = source_slot; target_slot = source_slot;
if (is_tee) state.inc_used(target_slot.reg()); if (is_tee) state.inc_used(target_slot.reg());
break; break;
case kI32Const: case KIntConst:
__ DropStackSlot(&target_slot); __ DropStackSlot(&target_slot);
target_slot = source_slot; target_slot = source_slot;
break; break;
......
...@@ -23,8 +23,13 @@ constexpr int32_t kFirstStackSlotOffset = ...@@ -23,8 +23,13 @@ constexpr int32_t kFirstStackSlotOffset =
kConstantStackSpace + LiftoffAssembler::kStackSlotSize; kConstantStackSpace + LiftoffAssembler::kStackSlotSize;
inline Operand GetStackSlot(uint32_t index) { inline Operand GetStackSlot(uint32_t index) {
return Operand( int32_t offset = index * LiftoffAssembler::kStackSlotSize;
rbp, -kFirstStackSlotOffset - index * LiftoffAssembler::kStackSlotSize); return Operand(rbp, -kFirstStackSlotOffset - offset);
}
inline Operand GetHalfStackSlot(uint32_t half_index) {
int32_t offset = half_index * (LiftoffAssembler::kStackSlotSize / 2);
return Operand(rbp, -kFirstStackSlotOffset - offset);
} }
// TODO(clemensh): Make this a constexpr variable once Operand is constexpr. // TODO(clemensh): Make this a constexpr variable once Operand is constexpr.
...@@ -255,6 +260,16 @@ void LiftoffAssembler::Spill(uint32_t index, WasmValue value) { ...@@ -255,6 +260,16 @@ void LiftoffAssembler::Spill(uint32_t index, WasmValue value) {
case kWasmI32: case kWasmI32:
movl(dst, Immediate(value.to_i32())); movl(dst, Immediate(value.to_i32()));
break; break;
case kWasmI64: {
// We could use movq, but this would require a temporary register. For
// simplicity (and to avoid potentially having to spill another register),
// we use two movl instructions.
int32_t low_word = static_cast<int32_t>(value.to_i64());
int32_t high_word = static_cast<int32_t>(value.to_i64() >> 32);
movl(dst, Immediate(low_word));
movl(liftoff::GetHalfStackSlot(2 * index + 1), Immediate(high_word));
break;
}
case kWasmF32: case kWasmF32:
movl(dst, Immediate(value.to_f32_boxed().get_bits())); movl(dst, Immediate(value.to_f32_boxed().get_bits()));
break; break;
...@@ -514,7 +529,7 @@ void LiftoffAssembler::PushCallerFrameSlot(const VarState& src, ...@@ -514,7 +529,7 @@ void LiftoffAssembler::PushCallerFrameSlot(const VarState& src,
case VarState::kRegister: case VarState::kRegister:
PushCallerFrameSlot(src.reg()); PushCallerFrameSlot(src.reg());
break; break;
case VarState::kI32Const: case VarState::KIntConst:
pushq(Immediate(src.i32_const())); pushq(Immediate(src.i32_const()));
break; break;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment