mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-12 15:19:17 +00:00
[dynarmic] flatten terminal variants to not use recursive pointers (#4218)
take If{then, else} for example
100% of the time If{} only has a leaf terminal, supporting If{If{}, If{}} is very dumb and we don't need that
so remove that
this reduces
> The recursive_wrapper class template has an interface similar to a simple value container, but its content is allocated dynamically
aka. uses malloc and spams heap EVERYTIME A CONDITIONAL TERMINAL IS USED
thats bad tbf, i dont like it, removing it is better for general speedup
also we dont REALLY need if{if{if{if{}}}} support, like really
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4218
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
@@ -34,7 +34,8 @@ oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond co
|
|||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
void EmitA32LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||||
|
|
||||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
@@ -125,31 +126,53 @@ void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Fa
|
|||||||
|
|
||||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
oaknut::Label pass = EmitA32Cond(code, ctx, terminal.if_);
|
oaknut::Label pass = EmitA32Cond(code, ctx, terminal.if_);
|
||||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
code.l(pass);
|
code.l(pass);
|
||||||
EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
EmitA32LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
oaknut::Label fail;
|
oaknut::Label fail;
|
||||||
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
||||||
code.CBZ(Wscratch0, fail);
|
code.CBZ(Wscratch0, fail);
|
||||||
EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
EmitA32LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||||
code.l(fail);
|
code.l(fail);
|
||||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
oaknut::Label fail;
|
oaknut::Label fail;
|
||||||
code.LDAR(Wscratch0, Xhalt);
|
code.LDAR(Wscratch0, Xhalt);
|
||||||
code.CBNZ(Wscratch0, fail);
|
code.CBNZ(Wscratch0, fail);
|
||||||
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
code.l(fail);
|
code.l(fail);
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA32LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
boost::apply_visitor([&](const auto& t) { EmitA32Terminal(code, ctx, t, initial_location, is_single_step); }, terminal);
|
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||||
|
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||||
|
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||||
|
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||||
|
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||||
|
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
|
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||||
|
return EmitA32LeafTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||||
|
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||||
|
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||||
|
return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ oaknut::Label EmitA64Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond co
|
|||||||
return pass;
|
return pass;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
void EmitA64LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||||
|
|
||||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
@@ -108,31 +109,53 @@ void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Fa
|
|||||||
|
|
||||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
oaknut::Label pass = EmitA64Cond(code, ctx, terminal.if_);
|
oaknut::Label pass = EmitA64Cond(code, ctx, terminal.if_);
|
||||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
code.l(pass);
|
code.l(pass);
|
||||||
EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
EmitA64LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
oaknut::Label fail;
|
oaknut::Label fail;
|
||||||
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
||||||
code.CBZ(Wscratch0, fail);
|
code.CBZ(Wscratch0, fail);
|
||||||
EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
EmitA64LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||||
code.l(fail);
|
code.l(fail);
|
||||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
oaknut::Label fail;
|
oaknut::Label fail;
|
||||||
code.LDAR(Wscratch0, Xhalt);
|
code.LDAR(Wscratch0, Xhalt);
|
||||||
code.CBNZ(Wscratch0, fail);
|
code.CBNZ(Wscratch0, fail);
|
||||||
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
code.l(fail);
|
code.l(fail);
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA64LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
boost::apply_visitor([&](const auto& t) { EmitA64Terminal(code, ctx, t, initial_location, is_single_step); }, terminal);
|
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||||
|
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||||
|
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||||
|
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||||
|
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||||
|
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
|
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||||
|
return EmitA64LeafTerminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||||
|
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||||
|
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||||
|
return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ void EmitA32Cond(biscuit::Assembler& as, EmitContext&, IR::Cond cond, biscuit::L
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||||
|
|
||||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||||
@@ -170,18 +171,18 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::FastDis
|
|||||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
biscuit::Label pass;
|
biscuit::Label pass;
|
||||||
EmitA32Cond(as, ctx, terminal.if_, &pass);
|
EmitA32Cond(as, ctx, terminal.if_, &pass);
|
||||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
as.Bind(&pass);
|
as.Bind(&pass);
|
||||||
EmitA32Terminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
EmitA32LeafTerminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
biscuit::Label fail;
|
biscuit::Label fail;
|
||||||
as.LBU(Xscratch0, offsetof(StackLayout, check_bit), Xstate);
|
as.LBU(Xscratch0, offsetof(StackLayout, check_bit), Xstate);
|
||||||
as.BEQZ(Xscratch0, &fail);
|
as.BEQZ(Xscratch0, &fail);
|
||||||
EmitA32Terminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
EmitA32LeafTerminal(as, ctx, terminal.then_, initial_location, is_single_step);
|
||||||
as.Bind(&fail);
|
as.Bind(&fail);
|
||||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
@@ -189,13 +190,35 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHa
|
|||||||
as.LWU(Xscratch0, 0, Xhalt);
|
as.LWU(Xscratch0, 0, Xhalt);
|
||||||
as.FENCE(biscuit::FenceOrder::RW, biscuit::FenceOrder::RW);
|
as.FENCE(biscuit::FenceOrder::RW, biscuit::FenceOrder::RW);
|
||||||
as.BNEZ(Xscratch0, &fail);
|
as.BNEZ(Xscratch0, &fail);
|
||||||
EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
as.Bind(&fail);
|
as.Bind(&fail);
|
||||||
EmitRelocation(as, ctx, LinkTarget::ReturnFromRunCode);
|
EmitRelocation(as, ctx, LinkTarget::ReturnFromRunCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
|
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||||
|
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||||
|
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||||
|
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||||
|
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||||
|
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
boost::apply_visitor([&](const auto& t) { EmitA32Terminal(as, ctx, t, initial_location, is_single_step); }, terminal);
|
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||||
|
return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||||
|
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||||
|
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||||
|
return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx) {
|
void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx) {
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ finish_this_inst:
|
|||||||
if (conf.enable_cycle_counting)
|
if (conf.enable_cycle_counting)
|
||||||
EmitAddCycles(block.CycleCount());
|
EmitAddCycles(block.CycleCount());
|
||||||
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
|
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
|
||||||
EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
EmitTerminal(block.terminal, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||||
code.int3();
|
code.int3();
|
||||||
|
|
||||||
for (auto& deferred_emit : ctx.deferred_emits)
|
for (auto& deferred_emit : ctx.deferred_emits)
|
||||||
@@ -219,7 +219,7 @@ void A32EmitX64::EmitCondPrelude(const A32EmitContext& ctx) {
|
|||||||
if (conf.enable_cycle_counting) {
|
if (conf.enable_cycle_counting) {
|
||||||
EmitAddCycles(ctx.block.ConditionFailedCycleCount());
|
EmitAddCycles(ctx.block.ConditionFailedCycleCount());
|
||||||
}
|
}
|
||||||
EmitTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
EmitLeafTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||||
code.L(pass);
|
code.L(pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1155,11 +1155,12 @@ void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_locat
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||||
e.code.ReturnFromRunCode();
|
e.code.ReturnFromRunCode();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
e.EmitSetUpperLocationDescriptor(terminal.next, initial_location);
|
e.EmitSetUpperLocationDescriptor(terminal.next, initial_location);
|
||||||
if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
||||||
e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||||
@@ -1186,9 +1187,10 @@ void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD
|
|||||||
e.PushRSBHelper(rax, rbx, terminal.next);
|
e.PushRSBHelper(rax, rbx, terminal.next);
|
||||||
e.code.ForceReturnFromRunCode();
|
e.code.ForceReturnFromRunCode();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
e.EmitSetUpperLocationDescriptor(terminal.next, initial_location);
|
e.EmitSetUpperLocationDescriptor(terminal.next, initial_location);
|
||||||
if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) {
|
||||||
e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC());
|
||||||
@@ -1201,55 +1203,78 @@ void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat
|
|||||||
e.EmitPatchJmp(terminal.next);
|
e.EmitPatchJmp(terminal.next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||||
if (!e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) || is_single_step) {
|
if (!e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) || is_single_step) {
|
||||||
e.code.ReturnFromRunCode();
|
e.code.ReturnFromRunCode();
|
||||||
} else {
|
} else {
|
||||||
e.code.jmp(e.terminal_handler_pop_rsb_hint);
|
e.code.jmp(e.terminal_handler_pop_rsb_hint);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||||
if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) {
|
if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) {
|
||||||
e.code.ReturnFromRunCode();
|
e.code.ReturnFromRunCode();
|
||||||
} else {
|
} else {
|
||||||
e.code.jmp(e.terminal_handler_fast_dispatch_hint);
|
e.code.jmp(e.terminal_handler_fast_dispatch_hint);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
Xbyak::Label pass = e.EmitCond(terminal.if_);
|
Xbyak::Label pass = e.EmitCond(terminal.if_);
|
||||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||||
e.code.L(pass);
|
e.code.L(pass);
|
||||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
Xbyak::Label fail;
|
Xbyak::Label fail;
|
||||||
e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0));
|
e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0));
|
||||||
e.code.jz(fail);
|
e.code.jz(fail);
|
||||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||||
e.code.L(fail);
|
e.code.L(fail);
|
||||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
bool EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0);
|
e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0);
|
||||||
e.code.jne(e.code.GetForceReturnFromRunCodeAddress());
|
e.code.jne(e.code.GetForceReturnFromRunCodeAddress());
|
||||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A32EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) {
|
}
|
||||||
|
|
||||||
|
bool A32EmitX64::EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||||
|
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void A32EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
bool A32EmitX64::EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||||
boost::apply_visitor([this, initial_location, is_single_step](auto x) {
|
if (auto const e = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||||
EmitTerminalImpl(*this, x, initial_location, is_single_step);
|
return EmitLeafTerminal(*e, initial_location, is_single_step);
|
||||||
}, terminal);
|
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
void A32EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
||||||
|
|||||||
@@ -112,7 +112,8 @@ public:
|
|||||||
|
|
||||||
// Terminal instruction emitters
|
// Terminal instruction emitters
|
||||||
void EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location);
|
void EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location);
|
||||||
void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||||
|
bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||||
|
|
||||||
// Patching
|
// Patching
|
||||||
void Unpatch(const IR::LocationDescriptor& target_desc) override;
|
void Unpatch(const IR::LocationDescriptor& target_desc) override;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <fmt/ostream.h>
|
#include <fmt/ostream.h>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "dynarmic/ir/terminal.h"
|
||||||
#include "dynarmic/mcl/integer_of_size.hpp"
|
#include "dynarmic/mcl/integer_of_size.hpp"
|
||||||
#include <boost/container/static_vector.hpp>
|
#include <boost/container/static_vector.hpp>
|
||||||
|
|
||||||
@@ -147,7 +148,7 @@ finish_this_inst:
|
|||||||
if (conf.enable_cycle_counting)
|
if (conf.enable_cycle_counting)
|
||||||
EmitAddCycles(block.CycleCount());
|
EmitAddCycles(block.CycleCount());
|
||||||
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
|
code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]);
|
||||||
EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
EmitTerminal(block.terminal, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep());
|
||||||
code.int3();
|
code.int3();
|
||||||
for (auto& deferred_emit : ctx.deferred_emits)
|
for (auto& deferred_emit : ctx.deferred_emits)
|
||||||
deferred_emit();
|
deferred_emit();
|
||||||
@@ -617,11 +618,12 @@ std::string A64EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||||
e.code.ReturnFromRunCode();
|
e.code.ReturnFromRunCode();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||||
// Used for patches and linking
|
// Used for patches and linking
|
||||||
if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||||
if (e.conf.enable_cycle_counting) {
|
if (e.conf.enable_cycle_counting) {
|
||||||
@@ -649,9 +651,10 @@ void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD
|
|||||||
e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
e.code.ReturnFromRunCode();
|
e.code.ReturnFromRunCode();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||||
if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||||
e.patch_information[terminal.next].jmp.push_back(e.code.getCurr());
|
e.patch_information[terminal.next].jmp.push_back(e.code.getCurr());
|
||||||
if (auto next_bb = e.GetBasicBlock(terminal.next)) {
|
if (auto next_bb = e.GetBasicBlock(terminal.next)) {
|
||||||
@@ -664,63 +667,86 @@ void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat
|
|||||||
e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax);
|
||||||
e.code.ReturnFromRunCode();
|
e.code.ReturnFromRunCode();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) {
|
||||||
if (e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) && !is_single_step) {
|
if (e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) && !is_single_step) {
|
||||||
e.code.jmp(e.terminal_handler_pop_rsb_hint);
|
e.code.jmp(e.terminal_handler_pop_rsb_hint);
|
||||||
} else {
|
} else {
|
||||||
e.code.ReturnFromRunCode();
|
e.code.ReturnFromRunCode();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) {
|
||||||
if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) {
|
if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) {
|
||||||
e.code.ReturnFromRunCode();
|
e.code.ReturnFromRunCode();
|
||||||
} else {
|
} else {
|
||||||
e.code.jmp(e.terminal_handler_fast_dispatch_hint);
|
e.code.jmp(e.terminal_handler_fast_dispatch_hint);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
switch (terminal.if_) {
|
switch (terminal.if_) {
|
||||||
case IR::Cond::AL:
|
case IR::Cond::AL:
|
||||||
case IR::Cond::NV:
|
case IR::Cond::NV:
|
||||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Xbyak::Label pass = e.EmitCond(terminal.if_);
|
Xbyak::Label pass = e.EmitCond(terminal.if_);
|
||||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||||
e.code.L(pass);
|
e.code.L(pass);
|
||||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
Xbyak::Label fail;
|
Xbyak::Label fail;
|
||||||
e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0));
|
e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0));
|
||||||
e.code.jz(fail);
|
e.code.jz(fail);
|
||||||
e.EmitTerminal(terminal.then_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step);
|
||||||
e.code.L(fail);
|
e.code.L(fail);
|
||||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
bool EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) {
|
||||||
e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0);
|
e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0);
|
||||||
e.code.jne(e.code.GetForceReturnFromRunCodeAddress());
|
e.code.jne(e.code.GetForceReturnFromRunCodeAddress());
|
||||||
e.EmitTerminal(terminal.else_, initial_location, is_single_step);
|
e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitTerminalImpl(A64EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) {
|
}
|
||||||
|
|
||||||
|
bool A64EmitX64::EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||||
|
if (auto const x = std::get_if<IR::Term::ReturnToDispatch>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void A64EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
bool A64EmitX64::EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept {
|
||||||
boost::apply_visitor([this, initial_location, is_single_step](auto x) {
|
if (auto const x = std::get_if<IR::Term::LeafTerminal>(&terminal))
|
||||||
EmitTerminalImpl(*this, x, initial_location, is_single_step);
|
return EmitLeafTerminal(*x, initial_location, is_single_step);
|
||||||
}, terminal);
|
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||||
|
return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) {
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ public:
|
|||||||
void EmitExclusiveWriteMemoryInline(A64EmitContext& ctx, IR::Inst* inst);
|
void EmitExclusiveWriteMemoryInline(A64EmitContext& ctx, IR::Inst* inst);
|
||||||
|
|
||||||
// Terminal instruction emitters
|
// Terminal instruction emitters
|
||||||
void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||||
|
bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override;
|
||||||
|
|
||||||
// Patching
|
// Patching
|
||||||
void Unpatch(const IR::LocationDescriptor& target_desc) override;
|
void Unpatch(const IR::LocationDescriptor& target_desc) override;
|
||||||
|
|||||||
@@ -111,7 +111,8 @@ public:
|
|||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc);
|
void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc);
|
||||||
#endif
|
#endif
|
||||||
virtual void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
virtual bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||||
|
virtual bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0;
|
||||||
|
|
||||||
// Patching
|
// Patching
|
||||||
struct PatchInformation {
|
struct PatchInformation {
|
||||||
|
|||||||
@@ -66,43 +66,44 @@ void Block::Reset(LocationDescriptor location_) noexcept {
|
|||||||
location = location_;
|
location = location_;
|
||||||
end_location = location_;
|
end_location = location_;
|
||||||
cond = Cond::AL;
|
cond = Cond::AL;
|
||||||
terminal = Term::Invalid{};
|
terminal = std::monostate{};
|
||||||
cond_failed_cycle_count = 0;
|
cond_failed_cycle_count = 0;
|
||||||
cycle_count = 0;
|
cycle_count = 0;
|
||||||
ASSERT(instructions.size() == 0);
|
ASSERT(instructions.size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string TerminalToString(const Terminal& terminal_variant) noexcept {
|
static std::string TerminalToString(const Term::Terminal& terminal_variant) noexcept {
|
||||||
struct : boost::static_visitor<std::string> {
|
// struct : boost::static_visitor<std::string> {
|
||||||
std::string operator()(const Term::Invalid&) const {
|
// std::string operator()(const std::monostate&) const {
|
||||||
return "<invalid terminal>";
|
// return "<invalid>";
|
||||||
}
|
// }
|
||||||
std::string operator()(const Term::ReturnToDispatch&) const {
|
// std::string operator()(const Term::ReturnToDispatch&) const {
|
||||||
return "ReturnToDispatch{}";
|
// return "ReturnToDispatch{}";
|
||||||
}
|
// }
|
||||||
std::string operator()(const Term::LinkBlock& terminal) const {
|
// std::string operator()(const Term::LinkBlock& terminal) const {
|
||||||
return fmt::format("LinkBlock{{{}}}", terminal.next);
|
// return fmt::format("LinkBlock{{{}}}", terminal.next);
|
||||||
}
|
// }
|
||||||
std::string operator()(const Term::LinkBlockFast& terminal) const {
|
// std::string operator()(const Term::LinkBlockFast& terminal) const {
|
||||||
return fmt::format("LinkBlockFast{{{}}}", terminal.next);
|
// return fmt::format("LinkBlockFast{{{}}}", terminal.next);
|
||||||
}
|
// }
|
||||||
std::string operator()(const Term::PopRSBHint&) const {
|
// std::string operator()(const Term::PopRSBHint&) const {
|
||||||
return "PopRSBHint{}";
|
// return "PopRSBHint{}";
|
||||||
}
|
// }
|
||||||
std::string operator()(const Term::FastDispatchHint&) const {
|
// std::string operator()(const Term::FastDispatchHint&) const {
|
||||||
return "FastDispatchHint{}";
|
// return "FastDispatchHint{}";
|
||||||
}
|
// }
|
||||||
std::string operator()(const Term::If& terminal) const {
|
// std::string operator()(const Term::If& terminal) const {
|
||||||
return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
// return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||||
}
|
// }
|
||||||
std::string operator()(const Term::CheckBit& terminal) const {
|
// std::string operator()(const Term::CheckBit& terminal) const {
|
||||||
return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
// return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_));
|
||||||
}
|
// }
|
||||||
std::string operator()(const Term::CheckHalt& terminal) const {
|
// std::string operator()(const Term::CheckHalt& terminal) const {
|
||||||
return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
// return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_));
|
||||||
}
|
// }
|
||||||
} visitor;
|
// } visitor;
|
||||||
return boost::apply_visitor(visitor, terminal_variant);
|
// return boost::apply_visitor(visitor, terminal_variant);
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DumpBlock(const IR::Block& block) noexcept {
|
std::string DumpBlock(const IR::Block& block) noexcept {
|
||||||
|
|||||||
@@ -114,22 +114,22 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the terminal instruction for this basic block.
|
/// Gets the terminal instruction for this basic block.
|
||||||
inline Terminal GetTerminal() const noexcept {
|
inline Term::Terminal GetTerminal() const noexcept {
|
||||||
return terminal;
|
return terminal;
|
||||||
}
|
}
|
||||||
/// Sets the terminal instruction for this basic block.
|
/// Sets the terminal instruction for this basic block.
|
||||||
inline void SetTerminal(Terminal term) noexcept {
|
inline void SetTerminal(Term::Terminal term) noexcept {
|
||||||
ASSERT(!HasTerminal() && "Terminal has already been set.");
|
ASSERT(!HasTerminal() && "Terminal has already been set.");
|
||||||
terminal = std::move(term);
|
terminal = std::move(term);
|
||||||
}
|
}
|
||||||
/// Replaces the terminal instruction for this basic block.
|
/// Replaces the terminal instruction for this basic block.
|
||||||
inline void ReplaceTerminal(Terminal term) noexcept {
|
inline void ReplaceTerminal(Term::Terminal term) noexcept {
|
||||||
ASSERT(HasTerminal() && "Terminal has not been set.");
|
ASSERT(HasTerminal() && "Terminal has not been set.");
|
||||||
terminal = std::move(term);
|
terminal = std::move(term);
|
||||||
}
|
}
|
||||||
/// Determines whether or not this basic block has a terminal instruction.
|
/// Determines whether or not this basic block has a terminal instruction.
|
||||||
inline bool HasTerminal() const noexcept {
|
inline bool HasTerminal() const noexcept {
|
||||||
return terminal.which() != 0;
|
return !std::holds_alternative<std::monostate>(terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets a mutable reference to the cycle count for this basic block.
|
/// Gets a mutable reference to the cycle count for this basic block.
|
||||||
@@ -156,7 +156,7 @@ public:
|
|||||||
/// Conditional to pass in order to execute this block
|
/// Conditional to pass in order to execute this block
|
||||||
Cond cond = Cond::AL;
|
Cond cond = Cond::AL;
|
||||||
/// Terminal instruction of this block.
|
/// Terminal instruction of this block.
|
||||||
Terminal terminal = Term::Invalid{};
|
Term::Terminal terminal = std::monostate{};
|
||||||
/// Number of cycles this block takes to execute if the conditional fails.
|
/// Number of cycles this block takes to execute if the conditional fails.
|
||||||
size_t cond_failed_cycle_count = 0;
|
size_t cond_failed_cycle_count = 0;
|
||||||
/// Number of cycles this block takes to execute.
|
/// Number of cycles this block takes to execute.
|
||||||
|
|||||||
@@ -2943,7 +2943,7 @@ public:
|
|||||||
Inst(Opcode::CallHostFunction, Imm64(std::bit_cast<u64>(fn)), arg1, arg2, arg3);
|
Inst(Opcode::CallHostFunction, Imm64(std::bit_cast<u64>(fn)), arg1, arg2, arg3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetTerm(const Terminal& terminal) {
|
void SetTerm(const Term::Terminal& terminal) {
|
||||||
block.SetTerminal(terminal);
|
block.SetTerminal(terminal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <variant>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
#include "dynarmic/ir/cond.h"
|
#include "dynarmic/ir/cond.h"
|
||||||
@@ -17,106 +17,89 @@
|
|||||||
namespace Dynarmic::IR {
|
namespace Dynarmic::IR {
|
||||||
namespace Term {
|
namespace Term {
|
||||||
|
|
||||||
struct Invalid {};
|
/// This terminal instruction returns control to the dispatcher.
|
||||||
|
/// The dispatcher will use the current cpu state to determine what comes next.
|
||||||
/**
|
|
||||||
* This terminal instruction returns control to the dispatcher.
|
|
||||||
* The dispatcher will use the current cpu state to determine what comes next.
|
|
||||||
*/
|
|
||||||
struct ReturnToDispatch {};
|
struct ReturnToDispatch {};
|
||||||
|
|
||||||
/**
|
/// This terminal instruction jumps to the basic block described by `next` if we have enough
|
||||||
* This terminal instruction jumps to the basic block described by `next` if we have enough
|
/// cycles remaining. If we do not have enough cycles remaining, we return to the
|
||||||
* cycles remaining. If we do not have enough cycles remaining, we return to the
|
/// dispatcher, which will return control to the host.
|
||||||
* dispatcher, which will return control to the host.
|
|
||||||
*/
|
|
||||||
struct LinkBlock {
|
struct LinkBlock {
|
||||||
explicit LinkBlock(const LocationDescriptor& next_)
|
explicit LinkBlock(const LocationDescriptor& next_) : next(next_) {}
|
||||||
: next(next_) {}
|
|
||||||
LocationDescriptor next; ///< Location descriptor for next block.
|
LocationDescriptor next; ///< Location descriptor for next block.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/// This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||||
* This terminal instruction jumps to the basic block described by `next` unconditionally.
|
/// This is an optimization and MUST only be emitted when this is guaranteed not to result
|
||||||
* This is an optimization and MUST only be emitted when this is guaranteed not to result
|
/// in hanging, even in the face of other optimizations. (In practice, this means that only
|
||||||
* in hanging, even in the face of other optimizations. (In practice, this means that only
|
/// forward jumps to short-ish blocks would use this instruction.)
|
||||||
* forward jumps to short-ish blocks would use this instruction.)
|
/// A backend that doesn't support this optimization may choose to implement this exactly
|
||||||
* A backend that doesn't support this optimization may choose to implement this exactly
|
/// as LinkBlock.
|
||||||
* as LinkBlock.
|
|
||||||
*/
|
|
||||||
struct LinkBlockFast {
|
struct LinkBlockFast {
|
||||||
explicit LinkBlockFast(const LocationDescriptor& next_)
|
explicit LinkBlockFast(const LocationDescriptor& next_) : next(next_) {}
|
||||||
: next(next_) {}
|
|
||||||
LocationDescriptor next; ///< Location descriptor for next block.
|
LocationDescriptor next; ///< Location descriptor for next block.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/// This terminal instruction checks the top of the Return Stack Buffer against the current
|
||||||
* This terminal instruction checks the top of the Return Stack Buffer against the current
|
/// location descriptor. If RSB lookup fails, control is returned to the dispatcher.
|
||||||
* location descriptor. If RSB lookup fails, control is returned to the dispatcher.
|
/// This is an optimization for faster function calls. A backend that doesn't support
|
||||||
* This is an optimization for faster function calls. A backend that doesn't support
|
/// this optimization or doesn't have a RSB may choose to implement this exactly as
|
||||||
* this optimization or doesn't have a RSB may choose to implement this exactly as
|
/// ReturnToDispatch.
|
||||||
* ReturnToDispatch.
|
|
||||||
*/
|
|
||||||
struct PopRSBHint {};
|
struct PopRSBHint {};
|
||||||
|
|
||||||
/**
|
/// This terminal instruction performs a lookup of the current location descriptor in the
|
||||||
* This terminal instruction performs a lookup of the current location descriptor in the
|
/// fast dispatch lookup table. A backend that doesn't support this optimization may choose
|
||||||
* fast dispatch lookup table. A backend that doesn't support this optimization may choose
|
/// to implement this exactly as ReturnToDispatch.
|
||||||
* to implement this exactly as ReturnToDispatch.
|
|
||||||
*/
|
|
||||||
struct FastDispatchHint {};
|
struct FastDispatchHint {};
|
||||||
|
|
||||||
struct If;
|
struct If;
|
||||||
struct CheckBit;
|
struct CheckBit;
|
||||||
struct CheckHalt;
|
struct CheckHalt;
|
||||||
/// A Terminal is the terminal instruction in a MicroBlock.
|
|
||||||
using Terminal = boost::variant<
|
/// Non recursive kind of terminal
|
||||||
Invalid,
|
using LeafTerminal = std::variant<
|
||||||
|
std::monostate,
|
||||||
ReturnToDispatch,
|
ReturnToDispatch,
|
||||||
LinkBlock,
|
LinkBlock,
|
||||||
LinkBlockFast,
|
LinkBlockFast,
|
||||||
PopRSBHint,
|
PopRSBHint,
|
||||||
FastDispatchHint,
|
FastDispatchHint
|
||||||
boost::recursive_wrapper<If>,
|
>;
|
||||||
boost::recursive_wrapper<CheckBit>,
|
|
||||||
boost::recursive_wrapper<CheckHalt>>;
|
|
||||||
|
|
||||||
/**
|
/// A Terminal is the terminal instruction in a MicroBlock.
|
||||||
* This terminal instruction conditionally executes one terminal or another depending
|
using Terminal = std::variant<
|
||||||
* on the run-time state of the ARM flags.
|
std::monostate,
|
||||||
*/
|
LeafTerminal,
|
||||||
|
If,
|
||||||
|
CheckBit,
|
||||||
|
CheckHalt
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// This terminal instruction conditionally executes one terminal or another depending
|
||||||
|
/// on the run-time state of the ARM flags.
|
||||||
struct If {
|
struct If {
|
||||||
If(Cond if_, Terminal then_, Terminal else_)
|
explicit If(Cond if_, LeafTerminal then_, LeafTerminal else_) : if_(if_), then_(std::move(then_)), else_(std::move(else_)) {}
|
||||||
: if_(if_), then_(std::move(then_)), else_(std::move(else_)) {}
|
|
||||||
Cond if_;
|
Cond if_;
|
||||||
Terminal then_;
|
LeafTerminal then_;
|
||||||
Terminal else_;
|
LeafTerminal else_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/// This terminal instruction conditionally executes one terminal or another depending
|
||||||
* This terminal instruction conditionally executes one terminal or another depending
|
/// on the run-time state of the check bit.
|
||||||
* on the run-time state of the check bit.
|
/// then_ is executed if the check bit is non-zero, otherwise else_ is executed.
|
||||||
* then_ is executed if the check bit is non-zero, otherwise else_ is executed.
|
|
||||||
*/
|
|
||||||
struct CheckBit {
|
struct CheckBit {
|
||||||
CheckBit(Terminal then_, Terminal else_)
|
explicit CheckBit(LeafTerminal then_, LeafTerminal else_) : then_(std::move(then_)), else_(std::move(else_)) {}
|
||||||
: then_(std::move(then_)), else_(std::move(else_)) {}
|
LeafTerminal then_;
|
||||||
Terminal then_;
|
LeafTerminal else_;
|
||||||
Terminal else_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/// This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
||||||
* This terminal instruction checks if a halt was requested. If it wasn't, else_ is
|
/// executed.
|
||||||
* executed.
|
|
||||||
*/
|
|
||||||
struct CheckHalt {
|
struct CheckHalt {
|
||||||
explicit CheckHalt(Terminal else_)
|
explicit CheckHalt(LeafTerminal else_) : else_(std::move(else_)) {}
|
||||||
: else_(std::move(else_)) {}
|
LeafTerminal else_;
|
||||||
Terminal else_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Term
|
} // namespace Term
|
||||||
|
|
||||||
using Term::Terminal;
|
|
||||||
|
|
||||||
} // namespace Dynarmic::IR
|
} // namespace Dynarmic::IR
|
||||||
|
|||||||
@@ -43,32 +43,20 @@ namespace {
|
|||||||
using namespace Dynarmic;
|
using namespace Dynarmic;
|
||||||
|
|
||||||
template<typename Fn>
|
template<typename Fn>
|
||||||
bool AnyLocationDescriptorForTerminalHas(IR::Terminal terminal, Fn fn) {
|
bool AnyLocationDescriptorForTerminalHas(IR::Term::Terminal terminal, Fn fn) {
|
||||||
return boost::apply_visitor([&](auto t) -> bool {
|
if (auto const e = std::get_if<IR::Term::LeafTerminal>(&terminal)) {
|
||||||
using T = std::decay_t<decltype(t)>;
|
if (auto const x = std::get_if<IR::Term::LinkBlock>(e))
|
||||||
if constexpr (std::is_same_v<T, IR::Term::Invalid>) {
|
return fn(x->next);
|
||||||
return false;
|
if (auto const x = std::get_if<IR::Term::LinkBlockFast>(e))
|
||||||
} else if constexpr (std::is_same_v<T, IR::Term::ReturnToDispatch>) {
|
return fn(x->next);
|
||||||
return false;
|
}
|
||||||
} else if constexpr (std::is_same_v<T, IR::Term::LinkBlock>) {
|
if (auto const x = std::get_if<IR::Term::If>(&terminal))
|
||||||
return fn(t.next);
|
return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||||
} else if constexpr (std::is_same_v<T, IR::Term::LinkBlockFast>) {
|
if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
|
||||||
return fn(t.next);
|
return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||||
} else if constexpr (std::is_same_v<T, IR::Term::PopRSBHint>) {
|
if (auto const x = std::get_if<IR::Term::CheckHalt>(&terminal))
|
||||||
return false;
|
return AnyLocationDescriptorForTerminalHas(x->else_, fn);
|
||||||
} else if constexpr (std::is_same_v<T, IR::Term::FastDispatchHint>) {
|
return false;
|
||||||
return false;
|
|
||||||
} else if constexpr (std::is_same_v<T, IR::Term::If>) {
|
|
||||||
return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
|
||||||
} else if constexpr (std::is_same_v<T, IR::Term::CheckBit>) {
|
|
||||||
return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
|
||||||
} else if constexpr (std::is_same_v<T, IR::Term::CheckHalt>) {
|
|
||||||
return AnyLocationDescriptorForTerminalHas(t.else_, fn);
|
|
||||||
} else {
|
|
||||||
ASSERT(false && "Invalid terminal type");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, terminal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
|
bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {
|
||||||
|
|||||||
Reference in New Issue
Block a user