[Liftoff] [cleanup] Make VarState a proper class
VarState was a struct so far, but gained more and more functionality. Even more will be added for supporting floating point operations. Thus, make this a proper class. Drive-by: Order all switch cases to first handle the stack case, then register, then constant. R=titzer@chromium.org Bug: v8:6600 Change-Id: I694613ebc4910bcf74a1617485bd72878f46e987 Reviewed-on: https://chromium-review.googlesource.com/789937 Reviewed-by: Ben Titzer <titzer@chromium.org> Commit-Queue: Clemens Hammacher <clemensh@chromium.org> Cr-Commit-Position: refs/heads/master@{#49632}
This commit is contained in:
parent
4ad9430c39
commit
91ff16193a
@ -16,10 +16,7 @@ namespace v8 {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
namespace wasm {
|
namespace wasm {
|
||||||
|
|
||||||
// Note: "State" suffix added to avoid jumbo conflicts with liftoff-compiler.cc
|
using VarState = LiftoffAssembler::VarState;
|
||||||
constexpr auto kRegisterState = LiftoffAssembler::VarState::kRegister;
|
|
||||||
constexpr auto kConstantState = LiftoffAssembler::VarState::kConstant;
|
|
||||||
constexpr auto kStackState = LiftoffAssembler::VarState::kStack;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -118,41 +115,41 @@ class StackTransferRecipe {
|
|||||||
|
|
||||||
void TransferStackSlot(const LiftoffAssembler::CacheState& dst_state,
|
void TransferStackSlot(const LiftoffAssembler::CacheState& dst_state,
|
||||||
uint32_t dst_index, uint32_t src_index) {
|
uint32_t dst_index, uint32_t src_index) {
|
||||||
const LiftoffAssembler::VarState& dst = dst_state.stack_state[dst_index];
|
const VarState& dst = dst_state.stack_state[dst_index];
|
||||||
const LiftoffAssembler::VarState& src =
|
const VarState& src = __ cache_state()->stack_state[src_index];
|
||||||
__ cache_state()->stack_state[src_index];
|
switch (dst.loc()) {
|
||||||
switch (dst.loc) {
|
case VarState::kStack:
|
||||||
case kConstantState:
|
switch (src.loc()) {
|
||||||
DCHECK_EQ(dst, src);
|
case VarState::kStack:
|
||||||
break;
|
|
||||||
case kRegisterState:
|
|
||||||
switch (src.loc) {
|
|
||||||
case kConstantState:
|
|
||||||
LoadConstant(dst.reg, WasmValue(src.i32_const));
|
|
||||||
break;
|
|
||||||
case kRegisterState:
|
|
||||||
if (dst.reg != src.reg) MoveRegister(dst.reg, src.reg);
|
|
||||||
break;
|
|
||||||
case kStackState:
|
|
||||||
LoadStackSlot(dst.reg, src_index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kStackState:
|
|
||||||
switch (src.loc) {
|
|
||||||
case kConstantState:
|
|
||||||
// TODO(clemensh): Handle other types than i32.
|
|
||||||
asm_->Spill(dst_index, WasmValue(src.i32_const));
|
|
||||||
break;
|
|
||||||
case kRegisterState:
|
|
||||||
asm_->Spill(dst_index, src.reg);
|
|
||||||
break;
|
|
||||||
case kStackState:
|
|
||||||
if (src_index == dst_index) break;
|
if (src_index == dst_index) break;
|
||||||
// TODO(clemensh): Implement other types than i32.
|
// TODO(clemensh): Implement other types than i32.
|
||||||
asm_->MoveStackValue(dst_index, src_index, wasm::kWasmI32);
|
asm_->MoveStackValue(dst_index, src_index, wasm::kWasmI32);
|
||||||
break;
|
break;
|
||||||
|
case VarState::kRegister:
|
||||||
|
asm_->Spill(dst_index, src.reg());
|
||||||
|
break;
|
||||||
|
case VarState::kConstant:
|
||||||
|
// TODO(clemensh): Handle other types than i32.
|
||||||
|
asm_->Spill(dst_index, WasmValue(src.i32_const()));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case VarState::kRegister:
|
||||||
|
switch (src.loc()) {
|
||||||
|
case VarState::kStack:
|
||||||
|
LoadStackSlot(dst.reg(), src_index);
|
||||||
|
break;
|
||||||
|
case VarState::kRegister:
|
||||||
|
if (dst.reg() != src.reg()) MoveRegister(dst.reg(), src.reg());
|
||||||
|
break;
|
||||||
|
case VarState::kConstant:
|
||||||
|
LoadConstant(dst.reg(), WasmValue(src.i32_const()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VarState::kConstant:
|
||||||
|
DCHECK_EQ(dst, src);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,8 +201,8 @@ void LiftoffAssembler::CacheState::InitMerge(const CacheState& source,
|
|||||||
auto& dst = stack_state[dst_idx];
|
auto& dst = stack_state[dst_idx];
|
||||||
auto& src = source.stack_state[src_idx];
|
auto& src = source.stack_state[src_idx];
|
||||||
Register reg = no_reg;
|
Register reg = no_reg;
|
||||||
if (src.is_reg() && is_free(src.reg)) {
|
if (src.is_reg() && is_free(src.reg())) {
|
||||||
reg = src.reg;
|
reg = src.reg();
|
||||||
} else if (has_unused_register()) {
|
} else if (has_unused_register()) {
|
||||||
reg = unused_register();
|
reg = unused_register();
|
||||||
} else {
|
} else {
|
||||||
@ -225,13 +222,13 @@ void LiftoffAssembler::CacheState::InitMerge(const CacheState& source,
|
|||||||
auto& dst = stack_state[i];
|
auto& dst = stack_state[i];
|
||||||
auto& src = source.stack_state[i];
|
auto& src = source.stack_state[i];
|
||||||
if (src.is_reg()) {
|
if (src.is_reg()) {
|
||||||
if (is_used(src.reg)) {
|
if (is_used(src.reg())) {
|
||||||
// Keep this a stack slot (which is the initial value).
|
// Keep this a stack slot (which is the initial value).
|
||||||
DCHECK(dst.is_stack());
|
DCHECK(dst.is_stack());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
dst = VarState(src.reg);
|
dst = VarState(src.reg());
|
||||||
inc_used(src.reg);
|
inc_used(src.reg());
|
||||||
} else if (src.is_const()) {
|
} else if (src.is_const()) {
|
||||||
dst = src;
|
dst = src;
|
||||||
} else {
|
} else {
|
||||||
@ -272,12 +269,12 @@ LiftoffAssembler::~LiftoffAssembler() {
|
|||||||
Register LiftoffAssembler::GetBinaryOpTargetRegister(
|
Register LiftoffAssembler::GetBinaryOpTargetRegister(
|
||||||
ValueType type, PinnedRegisterScope pinned_regs) {
|
ValueType type, PinnedRegisterScope pinned_regs) {
|
||||||
auto& slot_lhs = *(cache_state_.stack_state.end() - 2);
|
auto& slot_lhs = *(cache_state_.stack_state.end() - 2);
|
||||||
if (slot_lhs.loc == kRegisterState && GetNumUses(slot_lhs.reg) == 1) {
|
if (slot_lhs.is_reg() && GetNumUses(slot_lhs.reg()) == 1) {
|
||||||
return slot_lhs.reg;
|
return slot_lhs.reg();
|
||||||
}
|
}
|
||||||
auto& slot_rhs = *(cache_state_.stack_state.end() - 1);
|
auto& slot_rhs = *(cache_state_.stack_state.end() - 1);
|
||||||
if (slot_rhs.loc == kRegisterState && GetNumUses(slot_rhs.reg) == 1) {
|
if (slot_rhs.is_reg() && GetNumUses(slot_rhs.reg()) == 1) {
|
||||||
return slot_rhs.reg;
|
return slot_rhs.reg();
|
||||||
}
|
}
|
||||||
return GetUnusedRegister(type, pinned_regs);
|
return GetUnusedRegister(type, pinned_regs);
|
||||||
}
|
}
|
||||||
@ -313,18 +310,18 @@ void LiftoffAssembler::MergeStackWith(CacheState& target, uint32_t arity) {
|
|||||||
|
|
||||||
void LiftoffAssembler::Spill(uint32_t index) {
|
void LiftoffAssembler::Spill(uint32_t index) {
|
||||||
auto& slot = cache_state_.stack_state[index];
|
auto& slot = cache_state_.stack_state[index];
|
||||||
switch (slot.loc) {
|
switch (slot.loc()) {
|
||||||
case kRegisterState:
|
case VarState::kStack:
|
||||||
Spill(index, slot.reg);
|
|
||||||
cache_state_.dec_used(slot.reg);
|
|
||||||
break;
|
|
||||||
case kConstantState:
|
|
||||||
Spill(index, WasmValue(slot.i32_const));
|
|
||||||
break;
|
|
||||||
case kStackState:
|
|
||||||
return;
|
return;
|
||||||
|
case VarState::kRegister:
|
||||||
|
Spill(index, slot.reg());
|
||||||
|
cache_state_.dec_used(slot.reg());
|
||||||
|
break;
|
||||||
|
case VarState::kConstant:
|
||||||
|
Spill(index, WasmValue(slot.i32_const()));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
slot.loc = kStackState;
|
slot.MakeStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LiftoffAssembler::SpillLocals() {
|
void LiftoffAssembler::SpillLocals() {
|
||||||
@ -338,20 +335,20 @@ Register LiftoffAssembler::PopToRegister(ValueType type,
|
|||||||
DCHECK(!cache_state_.stack_state.empty());
|
DCHECK(!cache_state_.stack_state.empty());
|
||||||
VarState slot = cache_state_.stack_state.back();
|
VarState slot = cache_state_.stack_state.back();
|
||||||
cache_state_.stack_state.pop_back();
|
cache_state_.stack_state.pop_back();
|
||||||
switch (slot.loc) {
|
switch (slot.loc()) {
|
||||||
case kRegisterState:
|
case VarState::kStack: {
|
||||||
cache_state_.dec_used(slot.reg);
|
|
||||||
return slot.reg;
|
|
||||||
case kConstantState: {
|
|
||||||
Register reg = GetUnusedRegister(type, pinned_regs);
|
|
||||||
LoadConstant(reg, WasmValue(slot.i32_const));
|
|
||||||
return reg;
|
|
||||||
}
|
|
||||||
case kStackState: {
|
|
||||||
Register reg = GetUnusedRegister(type, pinned_regs);
|
Register reg = GetUnusedRegister(type, pinned_regs);
|
||||||
Fill(reg, cache_state_.stack_height());
|
Fill(reg, cache_state_.stack_height());
|
||||||
return reg;
|
return reg;
|
||||||
}
|
}
|
||||||
|
case VarState::kRegister:
|
||||||
|
cache_state_.dec_used(slot.reg());
|
||||||
|
return slot.reg();
|
||||||
|
case VarState::kConstant: {
|
||||||
|
Register reg = GetUnusedRegister(type, pinned_regs);
|
||||||
|
LoadConstant(reg, WasmValue(slot.i32_const()));
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -367,9 +364,9 @@ Register LiftoffAssembler::SpillRegister(ValueType type,
|
|||||||
for (uint32_t idx = cache_state_.stack_height() - 1;; --idx) {
|
for (uint32_t idx = cache_state_.stack_height() - 1;; --idx) {
|
||||||
DCHECK_GT(cache_state_.stack_height(), idx);
|
DCHECK_GT(cache_state_.stack_height(), idx);
|
||||||
auto& slot = cache_state_.stack_state[idx];
|
auto& slot = cache_state_.stack_state[idx];
|
||||||
if (!slot.is_reg() || slot.reg != spill_reg) continue;
|
if (!slot.is_reg() || slot.reg() != spill_reg) continue;
|
||||||
Spill(idx, spill_reg);
|
Spill(idx, spill_reg);
|
||||||
slot.loc = kStackState;
|
slot.MakeStack();
|
||||||
if (--remaining_uses == 0) break;
|
if (--remaining_uses == 0) break;
|
||||||
}
|
}
|
||||||
cache_state_.register_use_count[spill_reg.code()] = 0;
|
cache_state_.register_use_count[spill_reg.code()] = 0;
|
||||||
|
@ -76,36 +76,54 @@ class LiftoffAssembler : public TurboAssembler {
|
|||||||
|
|
||||||
Register GetBinaryOpTargetRegister(ValueType, PinnedRegisterScope = {});
|
Register GetBinaryOpTargetRegister(ValueType, PinnedRegisterScope = {});
|
||||||
|
|
||||||
struct VarState {
|
class VarState {
|
||||||
enum Location { kStack, kRegister, kConstant };
|
public:
|
||||||
Location loc;
|
enum Location : uint8_t { kStack, kRegister, kConstant };
|
||||||
|
|
||||||
union {
|
VarState() : loc_(kStack) {}
|
||||||
Register reg;
|
explicit VarState(Register r) : loc_(kRegister), reg_(r) {}
|
||||||
uint32_t i32_const;
|
explicit VarState(uint32_t i32_const)
|
||||||
};
|
: loc_(kConstant), i32_const_(i32_const) {}
|
||||||
VarState() : loc(kStack) {}
|
|
||||||
explicit VarState(Register r) : loc(kRegister), reg(r) {}
|
|
||||||
explicit VarState(uint32_t value) : loc(kConstant), i32_const(value) {}
|
|
||||||
|
|
||||||
bool operator==(const VarState& other) const {
|
bool operator==(const VarState& other) const {
|
||||||
if (loc != other.loc) return false;
|
if (loc_ != other.loc_) return false;
|
||||||
switch (loc) {
|
switch (loc_) {
|
||||||
case kRegister:
|
|
||||||
return reg == other.reg;
|
|
||||||
case kStack:
|
case kStack:
|
||||||
return true;
|
return true;
|
||||||
|
case kRegister:
|
||||||
|
return reg_ == other.reg_;
|
||||||
case kConstant:
|
case kConstant:
|
||||||
return i32_const == other.i32_const;
|
return i32_const_ == other.i32_const_;
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_stack() const { return loc == kStack; }
|
bool is_stack() const { return loc_ == kStack; }
|
||||||
bool is_reg() const { return loc == kRegister; }
|
bool is_reg() const { return loc_ == kRegister; }
|
||||||
bool is_const() const { return loc == kConstant; }
|
bool is_const() const { return loc_ == kConstant; }
|
||||||
};
|
|
||||||
|
|
||||||
|
Location loc() const { return loc_; }
|
||||||
|
|
||||||
|
uint32_t i32_const() const {
|
||||||
|
DCHECK_EQ(loc_, kConstant);
|
||||||
|
return i32_const_;
|
||||||
|
}
|
||||||
|
|
||||||
|
Register reg() const {
|
||||||
|
DCHECK_EQ(loc_, kRegister);
|
||||||
|
return reg_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeStack() { loc_ = kStack; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Location loc_;
|
||||||
|
|
||||||
|
union {
|
||||||
|
Register reg_; // used if loc_ == kRegister
|
||||||
|
uint32_t i32_const_; // used if loc_ == kConstant
|
||||||
|
};
|
||||||
|
};
|
||||||
static_assert(IS_TRIVIALLY_COPYABLE(VarState),
|
static_assert(IS_TRIVIALLY_COPYABLE(VarState),
|
||||||
"VarState should be trivially copyable");
|
"VarState should be trivially copyable");
|
||||||
|
|
||||||
@ -216,11 +234,11 @@ class LiftoffAssembler : public TurboAssembler {
|
|||||||
void DropStackSlot(VarState* slot) {
|
void DropStackSlot(VarState* slot) {
|
||||||
// The only loc we care about is register. Other types don't occupy
|
// The only loc we care about is register. Other types don't occupy
|
||||||
// anything.
|
// anything.
|
||||||
if (slot->loc != VarState::kRegister) return;
|
if (!slot->is_reg()) return;
|
||||||
// Free the register, then set the loc to "stack".
|
// Free the register, then set the loc to "stack".
|
||||||
// No need to write back, the value should be dropped.
|
// No need to write back, the value should be dropped.
|
||||||
cache_state_.dec_used(slot->reg);
|
cache_state_.dec_used(slot->reg());
|
||||||
slot->loc = VarState::kStack;
|
slot->MakeStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergeFullStackWith(CacheState&);
|
void MergeFullStackWith(CacheState&);
|
||||||
|
@ -308,12 +308,12 @@ class LiftoffCompiler {
|
|||||||
void GetLocal(Decoder* decoder, Value* result,
|
void GetLocal(Decoder* decoder, Value* result,
|
||||||
const LocalIndexOperand<validate>& operand) {
|
const LocalIndexOperand<validate>& operand) {
|
||||||
auto& slot = __ cache_state()->stack_state[operand.index];
|
auto& slot = __ cache_state()->stack_state[operand.index];
|
||||||
switch (slot.loc) {
|
switch (slot.loc()) {
|
||||||
case kRegister:
|
case kRegister:
|
||||||
__ PushRegister(slot.reg);
|
__ PushRegister(slot.reg());
|
||||||
break;
|
break;
|
||||||
case kConstant:
|
case kConstant:
|
||||||
__ cache_state()->stack_state.emplace_back(slot.i32_const);
|
__ cache_state()->stack_state.emplace_back(slot.i32_const());
|
||||||
break;
|
break;
|
||||||
case kStack: {
|
case kStack: {
|
||||||
Register reg = __ GetUnusedRegister(__ local_type(operand.index));
|
Register reg = __ GetUnusedRegister(__ local_type(operand.index));
|
||||||
@ -328,24 +328,24 @@ class LiftoffCompiler {
|
|||||||
auto& state = *__ cache_state();
|
auto& state = *__ cache_state();
|
||||||
auto& source_slot = state.stack_state.back();
|
auto& source_slot = state.stack_state.back();
|
||||||
auto& target_slot = state.stack_state[local_index];
|
auto& target_slot = state.stack_state[local_index];
|
||||||
switch (source_slot.loc) {
|
switch (source_slot.loc()) {
|
||||||
case kRegister:
|
case kRegister:
|
||||||
__ DropStackSlot(&target_slot);
|
__ DropStackSlot(&target_slot);
|
||||||
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 kConstant:
|
case kConstant:
|
||||||
__ DropStackSlot(&target_slot);
|
__ DropStackSlot(&target_slot);
|
||||||
target_slot = source_slot;
|
target_slot = source_slot;
|
||||||
break;
|
break;
|
||||||
case kStack: {
|
case kStack: {
|
||||||
switch (target_slot.loc) {
|
switch (target_slot.loc()) {
|
||||||
case kRegister:
|
case kRegister:
|
||||||
if (state.register_use_count[target_slot.reg.code()] == 1) {
|
if (state.register_use_count[target_slot.reg().code()] == 1) {
|
||||||
__ Fill(target_slot.reg, state.stack_height() - 1);
|
__ Fill(target_slot.reg(), state.stack_height() - 1);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
state.dec_used(target_slot.reg);
|
state.dec_used(target_slot.reg());
|
||||||
// and fall through to use a new register.
|
// and fall through to use a new register.
|
||||||
}
|
}
|
||||||
case kConstant:
|
case kConstant:
|
||||||
|
Loading…
Reference in New Issue
Block a user