backend/arm64: Update for new C flag representation
This commit is contained in:
parent
d69582f548
commit
67df13f886
5 changed files with 67 additions and 36 deletions
|
@ -83,7 +83,7 @@ void EmitIR<IR::Opcode::GetNZFromOp>(oaknut::CodeGenerator& code, EmitContext& c
|
||||||
auto flags = ctx.reg_alloc.WriteFlags(inst);
|
auto flags = ctx.reg_alloc.WriteFlags(inst);
|
||||||
RegAlloc::Realize(Wvalue, flags);
|
RegAlloc::Realize(Wvalue, flags);
|
||||||
|
|
||||||
code.CMP(*Wvalue, WZR);
|
code.TST(*Wvalue, *Wvalue);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -98,6 +98,17 @@ void EmitIR<IR::Opcode::GetLowerFromOp>(oaknut::CodeGenerator&, EmitContext& ctx
|
||||||
ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
ASSERT(ctx.reg_alloc.IsValueLive(inst));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
void EmitIR<IR::Opcode::GetCFlagFromNZCV>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
|
auto Wc = ctx.reg_alloc.WriteW(inst);
|
||||||
|
auto Wnzcv = ctx.reg_alloc.ReadW(args[0]);
|
||||||
|
RegAlloc::Realize(Wc, Wnzcv);
|
||||||
|
|
||||||
|
code.AND(Wc, Wnzcv, 1 << 29);
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::NZCVFromPackedFlags>(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::NZCVFromPackedFlags>(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
|
@ -275,11 +275,12 @@ void EmitIR<IR::Opcode::A32SetCpsrNZC>(oaknut::CodeGenerator& code, EmitContext&
|
||||||
auto Wc = ctx.reg_alloc.ReadW(args[1]);
|
auto Wc = ctx.reg_alloc.ReadW(args[1]);
|
||||||
RegAlloc::Realize(Wnz, Wc);
|
RegAlloc::Realize(Wnz, Wc);
|
||||||
|
|
||||||
// TODO: Store in Flags
|
// TODO: Track latent value
|
||||||
|
|
||||||
code.LDR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv));
|
code.LDR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv));
|
||||||
code.AND(Wscratch0, Wscratch0, 0x10000000);
|
code.AND(Wscratch0, Wscratch0, 0x10000000);
|
||||||
code.ORR(Wscratch0, Wscratch0, Wnz);
|
code.ORR(Wscratch0, Wscratch0, Wnz);
|
||||||
code.SBFX(Wscratch0, Wc, 29, 1);
|
code.ORR(Wscratch0, Wscratch0, Wc);
|
||||||
code.STR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv));
|
code.STR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,11 +289,8 @@ void EmitIR<IR::Opcode::A32GetCFlag>(oaknut::CodeGenerator& code, EmitContext& c
|
||||||
auto Wflag = ctx.reg_alloc.WriteW(inst);
|
auto Wflag = ctx.reg_alloc.WriteW(inst);
|
||||||
RegAlloc::Realize(Wflag);
|
RegAlloc::Realize(Wflag);
|
||||||
|
|
||||||
// TODO: Store in Flags
|
code.LDR(Wflag, Xstate, offsetof(A32JitState, cpsr_nzcv));
|
||||||
|
code.AND(Wflag, Wflag, 1 << 29);
|
||||||
code.LDR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv));
|
|
||||||
code.LSR(Wflag, Wscratch0, 29);
|
|
||||||
code.AND(Wflag, Wflag, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
|
@ -117,7 +117,6 @@ void EmitIR<IR::Opcode::MostSignificantWord>(oaknut::CodeGenerator& code, EmitCo
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::MostSignificantBit>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::MostSignificantBit>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
// TODO: Use host flags
|
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
|
@ -129,12 +128,12 @@ void EmitIR<IR::Opcode::MostSignificantBit>(oaknut::CodeGenerator& code, EmitCon
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::IsZero32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::IsZero32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
// TODO: Use host flags
|
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
auto Woperand = ctx.reg_alloc.ReadW(args[0]);
|
auto Woperand = ctx.reg_alloc.ReadW(args[0]);
|
||||||
RegAlloc::Realize(Wresult, Woperand);
|
RegAlloc::Realize(Wresult, Woperand);
|
||||||
|
ctx.reg_alloc.SpillFlags();
|
||||||
|
|
||||||
code.CMP(Woperand, 0);
|
code.CMP(Woperand, 0);
|
||||||
code.CSET(Wresult, EQ);
|
code.CSET(Wresult, EQ);
|
||||||
|
@ -142,12 +141,12 @@ void EmitIR<IR::Opcode::IsZero32>(oaknut::CodeGenerator& code, EmitContext& ctx,
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::IsZero64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::IsZero64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
// TODO: Use host flags
|
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
||||||
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
auto Xoperand = ctx.reg_alloc.ReadX(args[0]);
|
auto Xoperand = ctx.reg_alloc.ReadX(args[0]);
|
||||||
RegAlloc::Realize(Wresult, Xoperand);
|
RegAlloc::Realize(Wresult, Xoperand);
|
||||||
|
ctx.reg_alloc.SpillFlags();
|
||||||
|
|
||||||
code.CMP(Xoperand, 0);
|
code.CMP(Xoperand, 0);
|
||||||
code.CSET(Wresult, EQ);
|
code.CSET(Wresult, EQ);
|
||||||
|
@ -221,26 +220,37 @@ void EmitIR<IR::Opcode::LogicalShiftLeft32>(oaknut::CodeGenerator& code, EmitCon
|
||||||
code.CSEL(Wresult, Wresult, WZR, LT);
|
code.CSEL(Wresult, Wresult, WZR, LT);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (shift_arg.IsImmediate()) {
|
if (shift_arg.IsImmediate() && shift_arg.GetImmediateU8() == 0) {
|
||||||
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
ctx.reg_alloc.DefineAsExisting(inst, operand_arg);
|
||||||
auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst);
|
ctx.reg_alloc.DefineAsExisting(carry_inst, carry_arg);
|
||||||
auto Woperand = ctx.reg_alloc.ReadW(operand_arg);
|
} else if (shift_arg.IsImmediate()) {
|
||||||
auto Wcarry_in = ctx.reg_alloc.ReadW(carry_arg);
|
// TODO: Use RMIF
|
||||||
RegAlloc::Realize(Wresult, Wcarry_out, Woperand, Wcarry_in);
|
|
||||||
|
|
||||||
const u8 shift = shift_arg.GetImmediateU8();
|
const u8 shift = shift_arg.GetImmediateU8();
|
||||||
|
|
||||||
if (shift == 0) {
|
if (shift < 32) {
|
||||||
code.MOV(*Wresult, Woperand);
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
code.MOV(*Wcarry_out, Wcarry_in);
|
auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst);
|
||||||
} else if (shift < 32) {
|
auto Woperand = ctx.reg_alloc.ReadW(operand_arg);
|
||||||
|
RegAlloc::Realize(Wresult, Wcarry_out, Woperand);
|
||||||
|
|
||||||
code.UBFX(Wcarry_out, Woperand, 32 - shift, 1);
|
code.UBFX(Wcarry_out, Woperand, 32 - shift, 1);
|
||||||
|
code.LSL(Wcarry_out, Wcarry_out, 29);
|
||||||
code.LSL(Wresult, Woperand, shift);
|
code.LSL(Wresult, Woperand, shift);
|
||||||
} else if (shift > 32) {
|
} else if (shift > 32) {
|
||||||
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
|
auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst);
|
||||||
|
RegAlloc::Realize(Wresult, Wcarry_out);
|
||||||
|
|
||||||
code.MOV(Wresult, WZR);
|
code.MOV(Wresult, WZR);
|
||||||
code.MOV(Wcarry_out, WZR);
|
code.MOV(Wcarry_out, WZR);
|
||||||
} else {
|
} else {
|
||||||
code.AND(Wcarry_out, Wresult, 1);
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
|
auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst);
|
||||||
|
auto Woperand = ctx.reg_alloc.ReadW(operand_arg);
|
||||||
|
RegAlloc::Realize(Wresult, Wcarry_out, Woperand);
|
||||||
|
|
||||||
|
code.UBFIZ(Wcarry_out, Woperand, 29, 1);
|
||||||
code.MOV(Wresult, WZR);
|
code.MOV(Wresult, WZR);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -262,7 +272,7 @@ void EmitIR<IR::Opcode::LogicalShiftLeft32>(oaknut::CodeGenerator& code, EmitCon
|
||||||
code.NEG(Wscratch0, Wshift);
|
code.NEG(Wscratch0, Wshift);
|
||||||
code.LSR(Wcarry_out, Woperand, Wscratch0);
|
code.LSR(Wcarry_out, Woperand, Wscratch0);
|
||||||
code.LSL(Wresult, Woperand, Wshift);
|
code.LSL(Wresult, Woperand, Wshift);
|
||||||
code.AND(Wcarry_out, Wcarry_out, 1);
|
code.UBFIZ(Wcarry_out, Wcarry_out, 29, 1);
|
||||||
code.CMP(Wscratch1, 32);
|
code.CMP(Wscratch1, 32);
|
||||||
code.CSEL(Wresult, Wresult, WZR, LT);
|
code.CSEL(Wresult, Wresult, WZR, LT);
|
||||||
code.CSEL(Wcarry_out, Wcarry_out, WZR, LE);
|
code.CSEL(Wcarry_out, Wcarry_out, WZR, LE);
|
||||||
|
|
|
@ -27,6 +27,14 @@ static bool IsValuelessType(IR::Type type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Argument::~Argument() {
|
||||||
|
if (!IsImmediate()) {
|
||||||
|
if (auto host_loc = reg_alloc.ValueLocation(value.GetInst())) {
|
||||||
|
reg_alloc.ValueInfo(*host_loc).UpdateUses();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IR::Type Argument::GetType() const {
|
IR::Type Argument::GetType() const {
|
||||||
return value.GetType();
|
return value.GetType();
|
||||||
}
|
}
|
||||||
|
@ -101,6 +109,18 @@ bool HostLocInfo::IsOneRemainingUse() const {
|
||||||
return accumulated_uses + 1 == expected_uses && uses_this_inst == 1;
|
return accumulated_uses + 1 == expected_uses && uses_this_inst == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HostLocInfo::UpdateUses() {
|
||||||
|
accumulated_uses += uses_this_inst;
|
||||||
|
uses_this_inst = 0;
|
||||||
|
|
||||||
|
if (accumulated_uses == expected_uses) {
|
||||||
|
*this = {};
|
||||||
|
} else {
|
||||||
|
realized = false;
|
||||||
|
locked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
|
RegAlloc::ArgumentInfo RegAlloc::GetArgumentInfo(IR::Inst* inst) {
|
||||||
ArgumentInfo ret = {Argument{*this}, Argument{*this}, Argument{*this}, Argument{*this}};
|
ArgumentInfo ret = {Argument{*this}, Argument{*this}, Argument{*this}, Argument{*this}};
|
||||||
for (size_t i = 0; i < inst->NumArgs(); i++) {
|
for (size_t i = 0; i < inst->NumArgs(); i++) {
|
||||||
|
@ -229,19 +249,8 @@ template int RegAlloc::RealizeWriteImpl<HostLoc::Kind::Flags>(const IR::Inst* va
|
||||||
|
|
||||||
void RegAlloc::Unlock(HostLoc host_loc) {
|
void RegAlloc::Unlock(HostLoc host_loc) {
|
||||||
HostLocInfo& info = ValueInfo(host_loc);
|
HostLocInfo& info = ValueInfo(host_loc);
|
||||||
if (!info.realized) {
|
ASSERT(info.locked && info.realized);
|
||||||
return;
|
info.UpdateUses();
|
||||||
}
|
|
||||||
|
|
||||||
info.accumulated_uses += info.uses_this_inst;
|
|
||||||
info.uses_this_inst = 0;
|
|
||||||
|
|
||||||
if (info.accumulated_uses == info.expected_uses) {
|
|
||||||
info = {};
|
|
||||||
} else {
|
|
||||||
info.realized = false;
|
|
||||||
info.locked = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int RegAlloc::AllocateRegister(const std::array<HostLocInfo, 32>& regs, const std::vector<int>& order) const {
|
int RegAlloc::AllocateRegister(const std::array<HostLocInfo, 32>& regs, const std::vector<int>& order) const {
|
||||||
|
|
|
@ -39,6 +39,8 @@ struct Argument {
|
||||||
public:
|
public:
|
||||||
using copyable_reference = std::reference_wrapper<Argument>;
|
using copyable_reference = std::reference_wrapper<Argument>;
|
||||||
|
|
||||||
|
~Argument();
|
||||||
|
|
||||||
IR::Type GetType() const;
|
IR::Type GetType() const;
|
||||||
bool IsImmediate() const;
|
bool IsImmediate() const;
|
||||||
|
|
||||||
|
@ -124,6 +126,7 @@ struct HostLocInfo {
|
||||||
bool IsCompletelyEmpty() const;
|
bool IsCompletelyEmpty() const;
|
||||||
bool IsImmediatelyAllocatable() const;
|
bool IsImmediatelyAllocatable() const;
|
||||||
bool IsOneRemainingUse() const;
|
bool IsOneRemainingUse() const;
|
||||||
|
void UpdateUses();
|
||||||
};
|
};
|
||||||
|
|
||||||
class RegAlloc {
|
class RegAlloc {
|
||||||
|
|
Loading…
Reference in a new issue