Fixing register allocation bug.

This commit is contained in:
Ben Vanik 2014-01-26 17:38:40 -08:00
parent f55fb17e1b
commit 638d9631af
3 changed files with 35 additions and 11 deletions

View File

@ -1600,7 +1600,6 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
[](X64Emitter& e, Instr& i, const Reg& dest_src, const Operand& src) { [](X64Emitter& e, Instr& i, const Reg& dest_src, const Operand& src) {
// RAX = value, RDX = clobbered // RAX = value, RDX = clobbered
// TODO(benvanik): make the register allocator put dest_src in RAX? // TODO(benvanik): make the register allocator put dest_src in RAX?
e.db(0xCC);
auto Nax = LIKE_REG(e.rax, dest_src); auto Nax = LIKE_REG(e.rax, dest_src);
e.mov(Nax, dest_src); e.mov(Nax, dest_src);
if (i.flags & ARITHMETIC_UNSIGNED) { if (i.flags & ARITHMETIC_UNSIGNED) {
@ -1615,7 +1614,6 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
// TODO(benvanik): make the register allocator put dest_src in RAX? // TODO(benvanik): make the register allocator put dest_src in RAX?
auto Nax = LIKE_REG(e.rax, dest_src); auto Nax = LIKE_REG(e.rax, dest_src);
auto Ndx = LIKE_REG(e.rdx, dest_src); auto Ndx = LIKE_REG(e.rdx, dest_src);
e.db(0xCC);
e.mov(Nax, dest_src); e.mov(Nax, dest_src);
e.mov(Ndx, src); e.mov(Ndx, src);
if (i.flags & ARITHMETIC_UNSIGNED) { if (i.flags & ARITHMETIC_UNSIGNED) {
@ -1635,7 +1633,6 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
[](X64Emitter& e, Instr& i, const Reg& dest_src, const Operand& src) { [](X64Emitter& e, Instr& i, const Reg& dest_src, const Operand& src) {
// RAX = value, RDX = clobbered // RAX = value, RDX = clobbered
// TODO(benvanik): make the register allocator put dest_src in RAX? // TODO(benvanik): make the register allocator put dest_src in RAX?
e.db(0xCC);
auto Nax = LIKE_REG(e.rax, dest_src); auto Nax = LIKE_REG(e.rax, dest_src);
auto Ndx = LIKE_REG(e.rdx, dest_src); auto Ndx = LIKE_REG(e.rdx, dest_src);
e.mov(Nax, dest_src); e.mov(Nax, dest_src);
@ -1651,7 +1648,6 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
// TODO(benvanik): make the register allocator put dest_src in RAX? // TODO(benvanik): make the register allocator put dest_src in RAX?
auto Nax = LIKE_REG(e.rax, dest_src); auto Nax = LIKE_REG(e.rax, dest_src);
auto Ndx = LIKE_REG(e.rdx, dest_src); auto Ndx = LIKE_REG(e.rdx, dest_src);
e.db(0xCC);
e.mov(Nax, dest_src); e.mov(Nax, dest_src);
e.mov(Ndx, src); e.mov(Ndx, src);
if (i.flags & ARITHMETIC_UNSIGNED) { if (i.flags & ARITHMETIC_UNSIGNED) {
@ -1671,7 +1667,6 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
[](X64Emitter& e, Instr& i, const Reg& dest_src, const Operand& src) { [](X64Emitter& e, Instr& i, const Reg& dest_src, const Operand& src) {
// RAX = value, RDX = clobbered // RAX = value, RDX = clobbered
// TODO(benvanik): make the register allocator put dest_src in RAX? // TODO(benvanik): make the register allocator put dest_src in RAX?
e.db(0xCC);
auto Nax = LIKE_REG(e.rax, dest_src); auto Nax = LIKE_REG(e.rax, dest_src);
e.mov(Nax, dest_src); e.mov(Nax, dest_src);
if (i.flags & ARITHMETIC_UNSIGNED) { if (i.flags & ARITHMETIC_UNSIGNED) {
@ -1686,7 +1681,6 @@ void alloy::backend::x64::lowering::RegisterSequences(LoweringTable* table) {
// TODO(benvanik): make the register allocator put dest_src in RAX? // TODO(benvanik): make the register allocator put dest_src in RAX?
auto Nax = LIKE_REG(e.rax, dest_src); auto Nax = LIKE_REG(e.rax, dest_src);
auto Ndx = LIKE_REG(e.rdx, dest_src); auto Ndx = LIKE_REG(e.rdx, dest_src);
e.db(0xCC);
e.mov(Nax, dest_src); e.mov(Nax, dest_src);
e.mov(Ndx, src); e.mov(Ndx, src);
if (i.flags & ARITHMETIC_UNSIGNED) { if (i.flags & ARITHMETIC_UNSIGNED) {

View File

@ -143,6 +143,8 @@ int X64Emitter::Emit(HIRBuilder* builder) {
auto lowering_table = backend_->lowering_table(); auto lowering_table = backend_->lowering_table();
reg_state_.active_regs = reg_state_.live_regs = reserved_regs;
// Body. // Body.
auto block = builder->first_block(); auto block = builder->first_block();
while (block) { while (block) {
@ -156,7 +158,7 @@ int X64Emitter::Emit(HIRBuilder* builder) {
// Reset reg allocation state. // Reset reg allocation state.
// If we start keeping regs across blocks this needs to change. // If we start keeping regs across blocks this needs to change.
// We mark a few active so that the allocator doesn't use them. // We mark a few active so that the allocator doesn't use them.
reg_state_.active_regs = reg_state_.live_regs = reserved_regs; ResetRegisters(reserved_regs);
// Add instructions. // Add instructions.
// The table will process sequences of instructions to (try to) // The table will process sequences of instructions to (try to)
@ -192,11 +194,27 @@ int X64Emitter::Emit(HIRBuilder* builder) {
return 0; return 0;
} }
void X64Emitter::EvictStaleRegs() { void X64Emitter::ResetRegisters(uint32_t reserved_regs) {
// Just need to reset the register for each live value.
uint32_t live_regs = reg_state_.live_regs;
for (size_t n = 0; n < 32; n++, live_regs >>= 1) {
if (live_regs & 0x1) {
auto v = reg_state_.reg_values[n];
if (v) {
v->reg = -1;
}
}
reg_state_.reg_values[n] = 0;
}
reg_state_.active_regs = reg_state_.live_regs = reserved_regs;
}
void X64Emitter::EvictStaleRegisters() {
// NOTE: if we are getting called it's because we *need* a register. // NOTE: if we are getting called it's because we *need* a register.
// We must get rid of something. // We must get rid of something.
uint32_t current_ordinal = current_instr_->ordinal; uint32_t current_ordinal = current_instr_ ?
current_instr_->ordinal : 0xFFFFFFFF;
// Remove any register with no more uses. // Remove any register with no more uses.
uint32_t new_live_regs = 0; uint32_t new_live_regs = 0;
@ -216,7 +234,12 @@ void X64Emitter::EvictStaleRegs() {
auto v = reg_state_.reg_values[n]; auto v = reg_state_.reg_values[n];
if (v->last_use->ordinal < current_ordinal) { if (v->last_use->ordinal < current_ordinal) {
reg_state_.reg_values[n] = NULL; reg_state_.reg_values[n] = NULL;
v->reg = -1;
continue;
} }
// Register still in use.
new_live_regs |= bit;
} }
// Hrm. We have spilled. // Hrm. We have spilled.
@ -225,6 +248,9 @@ void X64Emitter::EvictStaleRegs() {
} }
reg_state_.live_regs = new_live_regs; reg_state_.live_regs = new_live_regs;
// Assert that live is a superset of active.
XEASSERTZERO((reg_state_.live_regs ^ reg_state_.active_regs) & reg_state_.active_regs);
} }
void X64Emitter::FindFreeRegs( void X64Emitter::FindFreeRegs(
@ -234,6 +260,9 @@ void X64Emitter::FindFreeRegs(
// Already in a register. Mark active and return. // Already in a register. Mark active and return.
v0_idx = v0->reg; v0_idx = v0->reg;
reg_state_.active_regs |= 1 << v0_idx; reg_state_.active_regs |= 1 << v0_idx;
// Assert that live is a superset of active.
XEASSERTZERO((reg_state_.live_regs ^ reg_state_.active_regs) & reg_state_.active_regs);
return; return;
} }
@ -250,7 +279,7 @@ void X64Emitter::FindFreeRegs(
uint32_t free_regs = avail_regs & ~reg_state_.live_regs; uint32_t free_regs = avail_regs & ~reg_state_.live_regs;
if (!free_regs) { if (!free_regs) {
// Need to evict something. // Need to evict something.
EvictStaleRegs(); EvictStaleRegisters();
free_regs = avail_regs & ~reg_state_.live_regs; free_regs = avail_regs & ~reg_state_.live_regs;
XEASSERT(free_regs); XEASSERT(free_regs);
} }

View File

@ -112,7 +112,8 @@ public:
GetRegBit(r0) | GetRegBit(r1) | GetRegBit(r2) | GetRegBit(r3)); GetRegBit(r0) | GetRegBit(r1) | GetRegBit(r2) | GetRegBit(r3));
} }
void EvictStaleRegs(); void ResetRegisters(uint32_t reserved_regs);
void EvictStaleRegisters();
void FindFreeRegs(hir::Value* v0, uint32_t& v0_idx, uint32_t v0_flags); void FindFreeRegs(hir::Value* v0, uint32_t& v0_idx, uint32_t v0_flags);
void FindFreeRegs(hir::Value* v0, uint32_t& v0_idx, uint32_t v0_flags, void FindFreeRegs(hir::Value* v0, uint32_t& v0_idx, uint32_t v0_flags,