Merge pull request #500 from lioncash/cbz
A32: Implement Thumb-1's CBZ/CBNZ instructions
This commit is contained in:
commit
fd6222f0a1
11 changed files with 61 additions and 3 deletions
|
@ -501,6 +501,12 @@ void A32EmitX64::EmitA32SetZFlag(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void A32EmitX64::EmitA32SetCheckBit(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
const Xbyak::Reg8 to_store = ctx.reg_alloc.UseGpr(args[0]).cvt8();
|
||||||
|
code.mov(code.byte[r15 + offsetof(A32JitState, check_bit)], to_store);
|
||||||
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32GetCFlag(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32GetCFlag(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
const Xbyak::Reg32 result = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||||
code.mov(result, dword[r15 + offsetof(A32JitState, CPSR_nzcv)]);
|
code.mov(result, dword[r15 + offsetof(A32JitState, CPSR_nzcv)]);
|
||||||
|
@ -1320,8 +1326,13 @@ void A32EmitX64::EmitTerminalImpl(IR::Term::If terminal, IR::LocationDescriptor
|
||||||
EmitTerminal(terminal.then_, initial_location);
|
EmitTerminal(terminal.then_, initial_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit, IR::LocationDescriptor) {
|
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location) {
|
||||||
ASSERT_MSG(false, "Term::CheckBit should never be emitted by the A32 frontend");
|
Xbyak::Label fail;
|
||||||
|
code.cmp(code.byte[r15 + offsetof(A32JitState, check_bit)], u8(0));
|
||||||
|
code.jz(fail);
|
||||||
|
EmitTerminal(terminal.then_, initial_location);
|
||||||
|
code.L(fail);
|
||||||
|
EmitTerminal(terminal.else_, initial_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) {
|
void A32EmitX64::EmitTerminalImpl(IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location) {
|
||||||
|
|
|
@ -53,6 +53,7 @@ struct A32JitState {
|
||||||
s64 cycles_to_run = 0;
|
s64 cycles_to_run = 0;
|
||||||
s64 cycles_remaining = 0;
|
s64 cycles_remaining = 0;
|
||||||
bool halt_requested = false;
|
bool halt_requested = false;
|
||||||
|
bool check_bit = false;
|
||||||
|
|
||||||
// Exclusive state
|
// Exclusive state
|
||||||
static constexpr u32 RESERVATION_GRANULE_MASK = 0xFFFFFFF8;
|
static constexpr u32 RESERVATION_GRANULE_MASK = 0xFFFFFFF8;
|
||||||
|
|
|
@ -116,6 +116,7 @@ std::optional<std::reference_wrapper<const Thumb16Matcher<V>>> DecodeThumb16(u16
|
||||||
// Branch instructions
|
// Branch instructions
|
||||||
INST(&V::thumb16_BX, "BX", "010001110mmmm000"), // v4T
|
INST(&V::thumb16_BX, "BX", "010001110mmmm000"), // v4T
|
||||||
INST(&V::thumb16_BLX_reg, "BLX (reg)", "010001111mmmm000"), // v5T
|
INST(&V::thumb16_BLX_reg, "BLX (reg)", "010001111mmmm000"), // v5T
|
||||||
|
INST(&V::thumb16_CBZ_CBNZ, "CBZ/CBNZ", "1011o0i1iiiiinnn"), // v6T2
|
||||||
INST(&V::thumb16_UDF, "UDF", "11011110--------"),
|
INST(&V::thumb16_UDF, "UDF", "11011110--------"),
|
||||||
INST(&V::thumb16_SVC, "SVC", "11011111xxxxxxxx"),
|
INST(&V::thumb16_SVC, "SVC", "11011111xxxxxxxx"),
|
||||||
INST(&V::thumb16_B_t1, "B (T1)", "1101ccccvvvvvvvv"),
|
INST(&V::thumb16_B_t1, "B (T1)", "1101ccccvvvvvvvv"),
|
||||||
|
|
|
@ -337,6 +337,13 @@ public:
|
||||||
return fmt::format("blx {}", m);
|
return fmt::format("blx {}", m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Reg n) {
|
||||||
|
const char* const name = nonzero ? "cbnz" : "cbz";
|
||||||
|
const u32 imm = concatenate(i, imm5, Imm<1>{0}).ZeroExtend();
|
||||||
|
|
||||||
|
return fmt::format("{} {}, #{}", name, n, imm);
|
||||||
|
}
|
||||||
|
|
||||||
std::string thumb16_UDF() {
|
std::string thumb16_UDF() {
|
||||||
return fmt::format("udf");
|
return fmt::format("udf");
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,10 @@ void IREmitter::SetCpsrNZCVQ(const IR::U32& value) {
|
||||||
Inst(Opcode::A32SetCpsrNZCVQ, value);
|
Inst(Opcode::A32SetCpsrNZCVQ, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IREmitter::SetCheckBit(const IR::U1& value) {
|
||||||
|
Inst(Opcode::A32SetCheckBit, value);
|
||||||
|
}
|
||||||
|
|
||||||
IR::U1 IREmitter::GetCFlag() {
|
IR::U1 IREmitter::GetCFlag() {
|
||||||
return Inst<IR::U1>(Opcode::A32GetCFlag);
|
return Inst<IR::U1>(Opcode::A32GetCFlag);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ public:
|
||||||
void SetCpsr(const IR::U32& value);
|
void SetCpsr(const IR::U32& value);
|
||||||
void SetCpsrNZCV(const IR::U32& value);
|
void SetCpsrNZCV(const IR::U32& value);
|
||||||
void SetCpsrNZCVQ(const IR::U32& value);
|
void SetCpsrNZCVQ(const IR::U32& value);
|
||||||
|
void SetCheckBit(const IR::U1& value);
|
||||||
IR::U1 GetCFlag();
|
IR::U1 GetCFlag();
|
||||||
void SetNFlag(const IR::U1& value);
|
void SetNFlag(const IR::U1& value);
|
||||||
void SetZFlag(const IR::U1& value);
|
void SetZFlag(const IR::U1& value);
|
||||||
|
|
|
@ -884,6 +884,29 @@ struct ThumbTranslatorVisitor final {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CB{N}Z <Rn>, <label>
|
||||||
|
bool thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Reg n) {
|
||||||
|
const u32 imm = concatenate(i, imm5, Imm<1>{0}).ZeroExtend();
|
||||||
|
const IR::U32 rn = ir.GetRegister(n);
|
||||||
|
|
||||||
|
ir.SetCheckBit(ir.IsZero(rn));
|
||||||
|
|
||||||
|
const auto [cond_pass, cond_fail] = [this, imm, nonzero] {
|
||||||
|
const u32 target = ir.PC() + imm;
|
||||||
|
const auto skip = IR::Term::LinkBlock{ir.current_location.AdvancePC(2)};
|
||||||
|
const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(target)};
|
||||||
|
|
||||||
|
if (nonzero) {
|
||||||
|
return std::make_pair(skip, branch);
|
||||||
|
} else {
|
||||||
|
return std::make_pair(branch, skip);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool thumb16_UDF() {
|
bool thumb16_UDF() {
|
||||||
return InterpretThisInstruction();
|
return InterpretThisInstruction();
|
||||||
}
|
}
|
||||||
|
|
|
@ -477,10 +477,15 @@ bool Inst::IsCoprocessorInstruction() const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Inst::IsSetCheckBitOperation() const {
|
||||||
|
return op == Opcode::A32SetCheckBit ||
|
||||||
|
op == Opcode::A64SetCheckBit;
|
||||||
|
}
|
||||||
|
|
||||||
bool Inst::MayHaveSideEffects() const {
|
bool Inst::MayHaveSideEffects() const {
|
||||||
return op == Opcode::PushRSB ||
|
return op == Opcode::PushRSB ||
|
||||||
op == Opcode::A64SetCheckBit ||
|
|
||||||
op == Opcode::A64DataCacheOperationRaised ||
|
op == Opcode::A64DataCacheOperationRaised ||
|
||||||
|
IsSetCheckBitOperation() ||
|
||||||
IsBarrier() ||
|
IsBarrier() ||
|
||||||
CausesCPUException() ||
|
CausesCPUException() ||
|
||||||
WritesToCoreRegister() ||
|
WritesToCoreRegister() ||
|
||||||
|
|
|
@ -99,6 +99,9 @@ public:
|
||||||
/// Determines whether or not this instruction causes a CPU exception.
|
/// Determines whether or not this instruction causes a CPU exception.
|
||||||
bool CausesCPUException() const;
|
bool CausesCPUException() const;
|
||||||
|
|
||||||
|
/// Determines whether or not this instruction is a SetCheckBit operation.
|
||||||
|
bool IsSetCheckBitOperation() const;
|
||||||
|
|
||||||
/// Determines whether or not this instruction may have side-effects.
|
/// Determines whether or not this instruction may have side-effects.
|
||||||
bool MayHaveSideEffects() const;
|
bool MayHaveSideEffects() const;
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ OPCODE(Identity, Opaque, Opaq
|
||||||
OPCODE(Breakpoint, Void, )
|
OPCODE(Breakpoint, Void, )
|
||||||
|
|
||||||
// A32 Context getters/setters
|
// A32 Context getters/setters
|
||||||
|
A32OPC(SetCheckBit, Void, U1 )
|
||||||
A32OPC(GetRegister, U32, A32Reg )
|
A32OPC(GetRegister, U32, A32Reg )
|
||||||
A32OPC(GetExtendedRegister32, U32, A32ExtReg )
|
A32OPC(GetExtendedRegister32, U32, A32ExtReg )
|
||||||
A32OPC(GetExtendedRegister64, U64, A32ExtReg )
|
A32OPC(GetExtendedRegister64, U64, A32ExtReg )
|
||||||
|
|
|
@ -291,6 +291,7 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
|
||||||
const u32 c = Dynarmic::Common::Bits<9, 12>(inst);
|
const u32 c = Dynarmic::Common::Bits<9, 12>(inst);
|
||||||
return c < 0b1110; // Don't want SWI or undefined instructions.
|
return c < 0b1110; // Don't want SWI or undefined instructions.
|
||||||
}),
|
}),
|
||||||
|
ThumbInstGen("1011o0i1iiiiinnn"), // CBZ/CBNZ
|
||||||
ThumbInstGen("10110110011x0xxx"), // CPS
|
ThumbInstGen("10110110011x0xxx"), // CPS
|
||||||
|
|
||||||
// TODO: We currently have no control over the generated
|
// TODO: We currently have no control over the generated
|
||||||
|
|
Loading…
Add table
Reference in a new issue