backend/arm64: Initial implementation of terminals
This commit is contained in:
parent
80c89401b9
commit
00ad84b7ab
4 changed files with 134 additions and 14 deletions
|
@ -112,7 +112,10 @@ EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) {
|
||||||
|
|
||||||
mem.unprotect();
|
mem.unprotect();
|
||||||
|
|
||||||
EmittedBlockInfo block_info = EmitArm64(code, std::move(block), {});
|
EmittedBlockInfo block_info = EmitArm64(code, std::move(block), {
|
||||||
|
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||||
|
.always_little_endian = conf.always_little_endian,
|
||||||
|
});
|
||||||
Link(block_info);
|
Link(block_info);
|
||||||
|
|
||||||
mem.protect();
|
mem.protect();
|
||||||
|
|
|
@ -93,19 +93,27 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E
|
||||||
|
|
||||||
reg_alloc.AssertNoMoreUses();
|
reg_alloc.AssertNoMoreUses();
|
||||||
|
|
||||||
// TODO: Add Cycles
|
if (emit_conf.enable_cycle_counting) {
|
||||||
|
const size_t cycles_to_add = block.CycleCount();
|
||||||
|
code.LDR(Xscratch0, SP, offsetof(StackLayout, cycles_remaining));
|
||||||
|
if (oaknut::AddSubImm::is_valid(cycles_to_add)) {
|
||||||
|
code.SUBS(Xscratch0, Xscratch0, cycles_to_add);
|
||||||
|
} else {
|
||||||
|
code.MOV(Xscratch1, cycles_to_add);
|
||||||
|
code.SUBS(Xscratch0, Xscratch0, Xscratch1);
|
||||||
|
}
|
||||||
|
code.STR(Xscratch0, SP, offsetof(StackLayout, cycles_remaining));
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Emit Terminal
|
EmitA32Terminal(code, ctx);
|
||||||
const auto term = block.GetTerminal();
|
|
||||||
const IR::Term::LinkBlock* link_block_term = boost::get<IR::Term::LinkBlock>(&term);
|
|
||||||
ASSERT(link_block_term);
|
|
||||||
code.MOV(Xscratch0, link_block_term->next.Value());
|
|
||||||
code.STUR(Xscratch0, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * 15);
|
|
||||||
ebi.relocations.emplace_back(Relocation{code.ptr<CodePtr>() - ebi.entry_point, LinkTarget::ReturnFromRunCode});
|
|
||||||
code.NOP();
|
|
||||||
|
|
||||||
ebi.size = code.ptr<CodePtr>() - ebi.entry_point;
|
ebi.size = code.ptr<CodePtr>() - ebi.entry_point;
|
||||||
return ebi;
|
return ebi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, LinkTarget link_target) {
|
||||||
|
ctx.ebi.relocations.emplace_back(Relocation{code.ptr<CodePtr>() - ctx.ebi.entry_point, link_target});
|
||||||
|
code.NOP();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::Arm64
|
} // namespace Dynarmic::Backend::Arm64
|
||||||
|
|
|
@ -15,12 +15,14 @@ struct PointerCodeGeneratorPolicy;
|
||||||
template<typename>
|
template<typename>
|
||||||
class BasicCodeGenerator;
|
class BasicCodeGenerator;
|
||||||
using CodeGenerator = BasicCodeGenerator<PointerCodeGeneratorPolicy>;
|
using CodeGenerator = BasicCodeGenerator<PointerCodeGeneratorPolicy>;
|
||||||
|
struct Label;
|
||||||
} // namespace oaknut
|
} // namespace oaknut
|
||||||
|
|
||||||
namespace Dynarmic::IR {
|
namespace Dynarmic::IR {
|
||||||
class Block;
|
class Block;
|
||||||
enum class Opcode;
|
|
||||||
class Inst;
|
class Inst;
|
||||||
|
enum class Cond;
|
||||||
|
enum class Opcode;
|
||||||
} // namespace Dynarmic::IR
|
} // namespace Dynarmic::IR
|
||||||
|
|
||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
@ -43,13 +45,18 @@ struct EmittedBlockInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmitConfig {
|
struct EmitConfig {
|
||||||
|
bool enable_cycle_counting;
|
||||||
|
bool always_little_endian;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmitContext;
|
struct EmitContext;
|
||||||
|
|
||||||
template<IR::Opcode op>
|
|
||||||
void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
|
|
||||||
|
|
||||||
EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf);
|
EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf);
|
||||||
|
|
||||||
|
template<IR::Opcode op>
|
||||||
|
void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
|
||||||
|
void EmitRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, LinkTarget link_target);
|
||||||
|
oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Cond cond);
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx);
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::Arm64
|
} // namespace Dynarmic::Backend::Arm64
|
||||||
|
|
|
@ -19,6 +19,108 @@ namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
|
oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond cond) {
|
||||||
|
oaknut::Label pass;
|
||||||
|
// TODO: Flags in host flags
|
||||||
|
code.LDR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv));
|
||||||
|
code.MSR(static_cast<oaknut::SystemReg>(0b11'011'0100'0010'000), Xscratch0);
|
||||||
|
code.B(static_cast<oaknut::Cond>(cond), pass);
|
||||||
|
return pass;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step);
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator&, EmitContext&, IR::Term::Interpret, IR::LocationDescriptor, bool) {
|
||||||
|
ASSERT_FALSE("Interpret should never be emitted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) {
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitSetUpperLocationDescriptor(oaknut::CodeGenerator& code, EmitContext& ctx, IR::LocationDescriptor new_location, IR::LocationDescriptor old_location) {
|
||||||
|
auto get_upper = [](const IR::LocationDescriptor& desc) -> u32 {
|
||||||
|
return static_cast<u32>(A32::LocationDescriptor{desc}.SetSingleStepping(false).UniqueHash() >> 32);
|
||||||
|
};
|
||||||
|
|
||||||
|
const u32 old_upper = get_upper(old_location);
|
||||||
|
const u32 new_upper = [&] {
|
||||||
|
const u32 mask = ~u32(ctx.emit_conf.always_little_endian ? 0x2 : 0);
|
||||||
|
return get_upper(new_location) & mask;
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (old_upper != new_upper) {
|
||||||
|
code.MOV(Xscratch0, new_upper);
|
||||||
|
code.STR(Xscratch0, Xstate, offsetof(A32JitState, upper_location_descriptor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool) {
|
||||||
|
EmitSetUpperLocationDescriptor(code, ctx, terminal.next, initial_location);
|
||||||
|
|
||||||
|
code.MOV(Xscratch0, terminal.next.Value());
|
||||||
|
code.STUR(Xscratch0, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * 15);
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode);
|
||||||
|
|
||||||
|
// TODO: Implement LinkBlock optimization
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool) {
|
||||||
|
EmitSetUpperLocationDescriptor(code, ctx, terminal.next, initial_location);
|
||||||
|
|
||||||
|
code.MOV(Xscratch0, terminal.next.Value());
|
||||||
|
code.STUR(Xscratch0, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * 15);
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode);
|
||||||
|
|
||||||
|
// TODO: Implement LinkBlockFast optimization
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::PopRSBHint, IR::LocationDescriptor, bool) {
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode);
|
||||||
|
|
||||||
|
// TODO: Implement PopRSBHint optimization
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool) {
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode);
|
||||||
|
|
||||||
|
// TODO: Implement FastDispatchHint optimization
|
||||||
|
}
|
||||||
|
|
||||||
|
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_);
|
||||||
|
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
|
code.l(pass);
|
||||||
|
EmitA32Terminal(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) {
|
||||||
|
oaknut::Label fail;
|
||||||
|
code.LDRB(Wscratch0, Xstate, offsetof(StackLayout, check_bit));
|
||||||
|
code.CBZ(Wscratch0, fail);
|
||||||
|
EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step);
|
||||||
|
code.l(fail);
|
||||||
|
EmitA32Terminal(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) {
|
||||||
|
oaknut::Label fail;
|
||||||
|
code.LDAR(Wscratch0, Xhalt);
|
||||||
|
code.CBNZ(Wscratch0, fail);
|
||||||
|
EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step);
|
||||||
|
code.l(fail);
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) {
|
||||||
|
const A32::LocationDescriptor location{ctx.block.Location()};
|
||||||
|
EmitA32Terminal(code, ctx, ctx.block.GetTerminal(), location.SetSingleStepping(false), location.SingleStepping());
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32GetRegister>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32GetRegister>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
|
const A32::Reg reg = inst->GetArg(0).GetA32RegRef();
|
||||||
|
|
Loading…
Reference in a new issue