JitArm64: Optimize GPR register push/pop.

This commit is contained in:
degasus 2017-02-09 09:25:31 +01:00
parent 09744db781
commit 6aa54a029e
1 changed files with 36 additions and 84 deletions

View File

@ -2079,106 +2079,58 @@ bool ARM64XEmitter::MOVI2R2(ARM64Reg Rd, u64 imm1, u64 imm2)
void ARM64XEmitter::ABI_PushRegisters(BitSet32 registers) void ARM64XEmitter::ABI_PushRegisters(BitSet32 registers)
{ {
unsigned int num_regs = registers.Count(); int num_regs = registers.Count();
int stack_size = (num_regs + (num_regs & 1)) * 8;
auto it = registers.begin();
if (num_regs % 2) if (!num_regs)
{ return;
bool first = true;
// Stack is required to be quad-word aligned. // 8 byte per register, but 16 byte alignment, so we may have to padd one register.
u32 stack_size = Common::AlignUp(num_regs * 8, 16); // Only update the SP on the last write to avoid the dependency between those stores.
u32 current_offset = 0;
std::vector<ARM64Reg> reg_pair;
for (auto it : registers) // The first push must adjust the SP, else a context switch may invalidate everything below SP.
{ if (num_regs & 1)
if (first) STR(INDEX_PRE, (ARM64Reg)(X0 + *it++), SP, -stack_size);
{
STR(INDEX_PRE, (ARM64Reg)(X0 + it), SP, -(s32)stack_size);
first = false;
current_offset += 16;
}
else
{
reg_pair.push_back((ARM64Reg)(X0 + it));
if (reg_pair.size() == 2)
{
STP(INDEX_SIGNED, reg_pair[0], reg_pair[1], SP, current_offset);
reg_pair.clear();
current_offset += 16;
}
}
}
}
else else
{ STP(INDEX_PRE, (ARM64Reg)(X0 + *it++), (ARM64Reg)(X0 + *it++), SP, -stack_size);
std::vector<ARM64Reg> reg_pair;
for (auto it : registers) // Fast store for all other registers, this is always an even number.
{ for (int i = 0; i < (num_regs - 1) / 2; i++)
reg_pair.push_back((ARM64Reg)(X0 + it)); STP(INDEX_SIGNED, (ARM64Reg)(X0 + *it++), (ARM64Reg)(X0 + *it++), SP, 16 * (i + 1));
if (reg_pair.size() == 2)
{ _assert_msg_(DYNA_REC, it == registers.end(), "%s registers don't match.", __FUNCTION__);
STP(INDEX_PRE, reg_pair[0], reg_pair[1], SP, -16);
reg_pair.clear();
}
}
}
} }
void ARM64XEmitter::ABI_PopRegisters(BitSet32 registers, BitSet32 ignore_mask) void ARM64XEmitter::ABI_PopRegisters(BitSet32 registers, BitSet32 ignore_mask)
{ {
int num_regs = registers.Count(); int num_regs = registers.Count();
int stack_size = (num_regs + (num_regs & 1)) * 8;
auto it = registers.begin();
if (num_regs % 2) if (!num_regs)
{ return;
bool first = true;
std::vector<ARM64Reg> reg_pair; // We must adjust the SP in the end, so load the first (two) registers at least.
ARM64Reg first = (ARM64Reg)(X0 + *it++);
ARM64Reg second;
if (!(num_regs & 1))
second = (ARM64Reg)(X0 + *it++);
for (auto it : registers) // 8 byte per register, but 16 byte alignment, so we may have to padd one register.
{ // Only update the SP on the last load to avoid the dependency between those loads.
if (ignore_mask[it])
it = WSP;
if (first) // Fast load for all but the first (two) registers, this is always an even number.
{ for (int i = 0; i < (num_regs - 1) / 2; i++)
LDR(INDEX_POST, (ARM64Reg)(X0 + it), SP, 16); LDP(INDEX_SIGNED, (ARM64Reg)(X0 + *it++), (ARM64Reg)(X0 + *it++), SP, 16 * (i + 1));
first = false;
} // Post loading the first (two) registers.
else if (num_regs & 1)
{ LDR(INDEX_POST, first, SP, stack_size);
reg_pair.push_back((ARM64Reg)(X0 + it));
if (reg_pair.size() == 2)
{
LDP(INDEX_POST, reg_pair[0], reg_pair[1], SP, 16);
reg_pair.clear();
}
}
}
}
else else
{ LDP(INDEX_POST, first, second, SP, stack_size);
std::vector<ARM64Reg> reg_pair;
for (int i = 31; i >= 0; --i) _assert_msg_(DYNA_REC, it == registers.end(), "%s registers don't match.", __FUNCTION__);
{
if (!registers[i])
continue;
int reg = i;
if (ignore_mask[reg])
reg = WSP;
reg_pair.push_back((ARM64Reg)(X0 + reg));
if (reg_pair.size() == 2)
{
LDP(INDEX_POST, reg_pair[1], reg_pair[0], SP, 16);
reg_pair.clear();
}
}
}
} }
// Float Emitter // Float Emitter