[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:
Clemens Hammacher 2017-11-27 11:35:34 +01:00 committed by Commit Bot
parent 4ad9430c39
commit 91ff16193a
3 changed files with 111 additions and 96 deletions

View File

@ -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;

View File

@ -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&);

View File

@ -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: