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,
? (half == kLowWord ? src.reg().low() : src.reg().high())
: src.reg());
break;
case VarState::kI32Const:
push(Immediate(src.i32_const()));
case VarState::KIntConst:
// The high word is the sign extension of the low word.
push(Immediate(half == kLowWord ? src.i32_const()
: src.i32_const() >> 31));
break;
}
}
......
......@@ -42,26 +42,33 @@ class StackTransferRecipe {
kStack, // fill a register from a stack slot.
kHalfStack // fill one half of a register pair from half a stack slot.
};
LiftoffRegister dst;
LoadKind kind;
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.
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) {
return {dst, kStack, type, stack_index};
}
static RegisterLoad HalfStack(LiftoffRegister dst,
uint32_t half_stack_index) {
int32_t half_stack_index) {
return {dst, kHalfStack, kWasmI32, half_stack_index};
}
private:
RegisterLoad(LiftoffRegister dst, LoadKind kind, ValueType type,
uint32_t value)
int32_t value)
: dst(dst), kind(kind), type(type), value(value) {}
};
......@@ -128,7 +135,9 @@ class StackTransferRecipe {
for (RegisterLoad& rl : register_loads_) {
switch (rl.kind) {
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;
case RegisterLoad::kStack:
asm_->Fill(rl.dst, rl.value, rl.type);
......@@ -157,15 +166,15 @@ class StackTransferRecipe {
case VarState::kRegister:
asm_->Spill(dst_index, src.reg(), src.type());
break;
case VarState::kI32Const:
asm_->Spill(dst_index, WasmValue(src.i32_const()));
case VarState::KIntConst:
asm_->Spill(dst_index, src.constant());
break;
}
break;
case VarState::kRegister:
LoadIntoRegister(dst.reg(), src, src_index);
break;
case VarState::kI32Const:
case VarState::KIntConst:
DCHECK_EQ(dst, src);
break;
}
......@@ -182,8 +191,8 @@ class StackTransferRecipe {
DCHECK_EQ(dst.reg_class(), src.reg_class());
if (dst != src.reg()) MoveRegister(dst, src.reg(), src.type());
break;
case VarState::kI32Const:
LoadConstant(dst, WasmValue(src.i32_const()));
case VarState::KIntConst:
LoadConstant(dst, src.constant());
break;
}
}
......@@ -205,8 +214,10 @@ class StackTransferRecipe {
if (dst != src_half) MoveRegister(dst, src_half, kWasmI32);
break;
}
case VarState::kI32Const:
int32_t value = half == kLowWord ? src.i32_const() : 0;
case VarState::KIntConst:
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));
break;
}
......@@ -382,9 +393,9 @@ LiftoffRegister LiftoffAssembler::PopToRegister(RegClass rc,
DCHECK_EQ(rc, slot.reg_class());
cache_state_.dec_used(slot.reg());
return slot.reg();
case VarState::kI32Const: {
case VarState::KIntConst: {
LiftoffRegister reg = GetUnusedRegister(rc, pinned);
LoadConstant(reg, WasmValue(slot.i32_const()));
LoadConstant(reg, slot.constant());
return reg;
}
}
......@@ -429,8 +440,8 @@ void LiftoffAssembler::Spill(uint32_t index) {
Spill(index, slot.reg(), slot.type());
cache_state_.dec_used(slot.reg());
break;
case VarState::kI32Const:
Spill(index, WasmValue(slot.i32_const()));
case VarState::KIntConst:
Spill(index, slot.constant());
break;
}
slot.MakeStack();
......@@ -619,7 +630,7 @@ std::ostream& operator<<(std::ostream& os, VarState slot) {
return os << "s";
case VarState::kRegister:
return os << slot.reg();
case VarState::kI32Const:
case VarState::KIntConst:
return os << "c" << slot.i32_const();
}
UNREACHABLE();
......
......@@ -36,26 +36,27 @@ class LiftoffAssembler : public TurboAssembler {
class VarState {
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, 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_(kI32Const), type_(type), i32_const_(i32_const) {
explicit VarState(ValueType type, int32_t i32_const)
: loc_(KIntConst), type_(type), i32_const_(i32_const) {
DCHECK(type_ == kWasmI32 || type_ == kWasmI64);
}
bool operator==(const VarState& other) const {
if (loc_ != other.loc_) return false;
if (type_ != other.type_) return false;
switch (loc_) {
case kStack:
return true;
case kRegister:
return reg_ == other.reg_;
case kI32Const:
case KIntConst:
return i32_const_ == other.i32_const_;
}
UNREACHABLE();
......@@ -65,16 +66,23 @@ class LiftoffAssembler : public TurboAssembler {
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_ == kI32Const; }
bool is_const() const { return loc_ == KIntConst; }
ValueType type() const { return type_; }
Location loc() const { return loc_; }
uint32_t i32_const() const {
DCHECK_EQ(loc_, kI32Const);
int32_t i32_const() const {
DCHECK_EQ(loc_, KIntConst);
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(); }
DoubleRegister fp_reg() const { return reg().fp(); }
LiftoffRegister reg() const {
......@@ -93,7 +101,7 @@ class LiftoffAssembler : public TurboAssembler {
union {
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 {
namespace wasm {
constexpr auto kRegister = LiftoffAssembler::VarState::kRegister;
constexpr auto kI32Const = LiftoffAssembler::VarState::kI32Const;
constexpr auto KIntConst = LiftoffAssembler::VarState::KIntConst;
constexpr auto kStack = LiftoffAssembler::VarState::kStack;
namespace {
......@@ -616,9 +616,18 @@ class LiftoffCompiler {
}
void I64Const(Decoder* decoder, Value* result, int64_t value) {
LiftoffRegister reg = __ GetUnusedRegister(reg_class_for(kWasmI64));
__ LoadConstant(reg, WasmValue(value));
__ PushRegister(kWasmI64, reg);
// 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));
__ LoadConstant(reg, WasmValue(value));
__ PushRegister(kWasmI64, reg);
}
CheckStackSizeLimit(decoder);
}
......@@ -664,7 +673,7 @@ class LiftoffCompiler {
case kRegister:
__ PushRegister(slot.type(), slot.reg());
break;
case kI32Const:
case KIntConst:
__ cache_state()->stack_state.emplace_back(operand.type,
slot.i32_const());
break;
......@@ -709,7 +718,7 @@ class LiftoffCompiler {
target_slot = source_slot;
if (is_tee) state.inc_used(target_slot.reg());
break;
case kI32Const:
case KIntConst:
__ DropStackSlot(&target_slot);
target_slot = source_slot;
break;
......
......@@ -23,8 +23,13 @@ constexpr int32_t kFirstStackSlotOffset =
kConstantStackSpace + LiftoffAssembler::kStackSlotSize;
inline Operand GetStackSlot(uint32_t index) {
return Operand(
rbp, -kFirstStackSlotOffset - index * LiftoffAssembler::kStackSlotSize);
int32_t offset = 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.
......@@ -255,6 +260,16 @@ void LiftoffAssembler::Spill(uint32_t index, WasmValue value) {
case kWasmI32:
movl(dst, Immediate(value.to_i32()));
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:
movl(dst, Immediate(value.to_f32_boxed().get_bits()));
break;
......@@ -514,7 +529,7 @@ void LiftoffAssembler::PushCallerFrameSlot(const VarState& src,
case VarState::kRegister:
PushCallerFrameSlot(src.reg());
break;
case VarState::kI32Const:
case VarState::KIntConst:
pushq(Immediate(src.i32_const()));
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