Terminals
This commit is contained in:
parent
cdd658935c
commit
e6949a86a2
6 changed files with 124 additions and 5 deletions
|
@ -308,6 +308,7 @@ EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) {
|
||||||
.cntfreq_el0{},
|
.cntfreq_el0{},
|
||||||
.dczid_el0{},
|
.dczid_el0{},
|
||||||
.ctr_el0{},
|
.ctr_el0{},
|
||||||
|
.is_a64 = false,
|
||||||
.hook_isb = conf.hook_isb,
|
.hook_isb = conf.hook_isb,
|
||||||
.enable_cycle_counting = conf.enable_cycle_counting,
|
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||||
.always_little_endian = conf.always_little_endian,
|
.always_little_endian = conf.always_little_endian,
|
||||||
|
|
|
@ -321,6 +321,7 @@ EmittedBlockInfo A64AddressSpace::Emit(IR::Block block) {
|
||||||
.cntfreq_el0 = conf.cntfrq_el0,
|
.cntfreq_el0 = conf.cntfrq_el0,
|
||||||
.dczid_el0 = conf.dczid_el0,
|
.dczid_el0 = conf.dczid_el0,
|
||||||
.ctr_el0 = conf.ctr_el0,
|
.ctr_el0 = conf.ctr_el0,
|
||||||
|
.is_a64 = true,
|
||||||
.hook_isb = conf.hook_isb,
|
.hook_isb = conf.hook_isb,
|
||||||
.enable_cycle_counting = conf.enable_cycle_counting,
|
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||||
.always_little_endian = true,
|
.always_little_endian = true,
|
||||||
|
|
|
@ -164,10 +164,18 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E
|
||||||
ASSERT(!ctx.block.HasConditionFailedLocation());
|
ASSERT(!ctx.block.HasConditionFailedLocation());
|
||||||
} else {
|
} else {
|
||||||
ASSERT(ctx.block.HasConditionFailedLocation());
|
ASSERT(ctx.block.HasConditionFailedLocation());
|
||||||
|
oaknut::Label pass;
|
||||||
|
|
||||||
|
if (conf.is_a64) {
|
||||||
|
pass = EmitA64Cond(code, ctx, ctx.block.GetCondition());
|
||||||
|
EmitAddCycles(code, ctx, ctx.block.ConditionFailedCycleCount());
|
||||||
|
EmitA64ConditionFailedTerminal(code, ctx);
|
||||||
|
} else {
|
||||||
|
pass = EmitA32Cond(code, ctx, ctx.block.GetCondition());
|
||||||
|
EmitAddCycles(code, ctx, ctx.block.ConditionFailedCycleCount());
|
||||||
|
EmitA32ConditionFailedTerminal(code, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
oaknut::Label pass = EmitA32Cond(code, ctx, ctx.block.GetCondition());
|
|
||||||
EmitAddCycles(code, ctx, ctx.block.ConditionFailedCycleCount());
|
|
||||||
EmitA32ConditionFailedTerminal(code, ctx);
|
|
||||||
code.l(pass);
|
code.l(pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +213,11 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E
|
||||||
reg_alloc.AssertNoMoreUses();
|
reg_alloc.AssertNoMoreUses();
|
||||||
|
|
||||||
EmitAddCycles(code, ctx, block.CycleCount());
|
EmitAddCycles(code, ctx, block.CycleCount());
|
||||||
EmitA32Terminal(code, ctx);
|
if (conf.is_a64) {
|
||||||
|
EmitA64Terminal(code, ctx);
|
||||||
|
} else {
|
||||||
|
EmitA32Terminal(code, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
ebi.size = code.ptr<CodePtr>() - ebi.entry_point;
|
ebi.size = code.ptr<CodePtr>() - ebi.entry_point;
|
||||||
return ebi;
|
return ebi;
|
||||||
|
|
|
@ -95,6 +95,7 @@ struct EmitConfig {
|
||||||
u64 cntfreq_el0;
|
u64 cntfreq_el0;
|
||||||
u32 dczid_el0;
|
u32 dczid_el0;
|
||||||
u32 ctr_el0;
|
u32 ctr_el0;
|
||||||
|
bool is_a64;
|
||||||
bool hook_isb;
|
bool hook_isb;
|
||||||
bool enable_cycle_counting;
|
bool enable_cycle_counting;
|
||||||
bool always_little_endian;
|
bool always_little_endian;
|
||||||
|
@ -120,7 +121,10 @@ void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
|
||||||
void EmitRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, LinkTarget link_target);
|
void EmitRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, LinkTarget link_target);
|
||||||
void EmitBlockLinkRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, const IR::LocationDescriptor& descriptor);
|
void EmitBlockLinkRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, const IR::LocationDescriptor& descriptor);
|
||||||
oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Cond cond);
|
oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Cond cond);
|
||||||
|
oaknut::Label EmitA64Cond(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Cond cond);
|
||||||
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx);
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx);
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx);
|
||||||
void EmitA32ConditionFailedTerminal(oaknut::CodeGenerator& code, EmitContext& ctx);
|
void EmitA32ConditionFailedTerminal(oaknut::CodeGenerator& code, EmitContext& ctx);
|
||||||
|
void EmitA64ConditionFailedTerminal(oaknut::CodeGenerator& code, EmitContext& ctx);
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::Arm64
|
} // namespace Dynarmic::Backend::Arm64
|
||||||
|
|
|
@ -41,7 +41,7 @@ void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Re
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitSetUpperLocationDescriptor(oaknut::CodeGenerator& code, EmitContext& ctx, IR::LocationDescriptor new_location, IR::LocationDescriptor old_location) {
|
static void EmitSetUpperLocationDescriptor(oaknut::CodeGenerator& code, EmitContext& ctx, IR::LocationDescriptor new_location, IR::LocationDescriptor old_location) {
|
||||||
auto get_upper = [](const IR::LocationDescriptor& desc) -> u32 {
|
auto get_upper = [](const IR::LocationDescriptor& desc) -> u32 {
|
||||||
return static_cast<u32>(A32::LocationDescriptor{desc}.SetSingleStepping(false).UniqueHash() >> 32);
|
return static_cast<u32>(A32::LocationDescriptor{desc}.SetSingleStepping(false).UniqueHash() >> 32);
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,6 +21,107 @@ using namespace oaknut::util;
|
||||||
|
|
||||||
static constexpr int nzcv_c_flag_shift = 29;
|
static constexpr int nzcv_c_flag_shift = 29;
|
||||||
|
|
||||||
|
oaknut::Label EmitA64Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond cond) {
|
||||||
|
oaknut::Label pass;
|
||||||
|
// TODO: Flags in host flags
|
||||||
|
code.LDR(Wscratch0, Xstate, offsetof(A64JitState, cpsr_nzcv));
|
||||||
|
code.MSR(oaknut::SystemReg::NZCV, Xscratch0);
|
||||||
|
code.B(static_cast<oaknut::Cond>(cond), pass);
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator&, EmitContext&, IR::Term::Interpret, IR::LocationDescriptor, bool) {
|
||||||
|
ASSERT_FALSE("Interpret should never be emitted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||||
|
oaknut::Label fail;
|
||||||
|
|
||||||
|
if (ctx.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||||
|
if (ctx.conf.enable_cycle_counting) {
|
||||||
|
code.CMP(Xticks, 0);
|
||||||
|
code.B(LE, fail);
|
||||||
|
EmitBlockLinkRelocation(code, ctx, terminal.next);
|
||||||
|
} else {
|
||||||
|
code.LDAR(Wscratch0, Xhalt);
|
||||||
|
code.CBNZ(Wscratch0, fail);
|
||||||
|
EmitBlockLinkRelocation(code, ctx, terminal.next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code.l(fail);
|
||||||
|
code.MOV(Xscratch0, A64::LocationDescriptor{terminal.next}.PC());
|
||||||
|
code.STR(Xscratch0, Xstate, offsetof(A64JitState, pc));
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) {
|
||||||
|
if (ctx.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) {
|
||||||
|
EmitBlockLinkRelocation(code, ctx, terminal.next);
|
||||||
|
}
|
||||||
|
|
||||||
|
code.MOV(Wscratch0, A64::LocationDescriptor{terminal.next}.PC());
|
||||||
|
code.STR(Wscratch0, Xstate, offsetof(A64JitState, pc));
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::PopRSBHint, IR::LocationDescriptor, bool) {
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
|
|
||||||
|
// TODO: Implement PopRSBHint optimization
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool) {
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
|
|
||||||
|
// TODO: Implement FastDispatchHint optimization
|
||||||
|
}
|
||||||
|
|
||||||
|
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_);
|
||||||
|
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
|
code.l(pass);
|
||||||
|
EmitA64Terminal(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) {
|
||||||
|
oaknut::Label fail;
|
||||||
|
code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
||||||
|
code.CBZ(Wscratch0, fail);
|
||||||
|
EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||||
|
code.l(fail);
|
||||||
|
EmitA64Terminal(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) {
|
||||||
|
oaknut::Label fail;
|
||||||
|
code.LDAR(Wscratch0, Xhalt);
|
||||||
|
code.CBNZ(Wscratch0, fail);
|
||||||
|
EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
|
code.l(fail);
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||||
|
const A64::LocationDescriptor location{ctx.block.Location()};
|
||||||
|
EmitA64Terminal(code, ctx, ctx.block.GetTerminal(), location.SetSingleStepping(false), location.SingleStepping());
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA64ConditionFailedTerminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||||
|
const A64::LocationDescriptor location{ctx.block.Location()};
|
||||||
|
EmitA64Terminal(code, ctx, IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, location.SetSingleStepping(false), location.SingleStepping());
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetCheckBit>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetCheckBit>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
|
Loading…
Reference in a new issue