emit_arm64_a32_memory: Implement all callbacks
This commit is contained in:
parent
d2deb496da
commit
cf47ab3b42
8 changed files with 358 additions and 72 deletions
|
@ -390,11 +390,14 @@ elseif(ARCHITECTURE STREQUAL "arm64")
|
||||||
backend/arm64/emit_arm64_vector_floating_point.cpp
|
backend/arm64/emit_arm64_vector_floating_point.cpp
|
||||||
backend/arm64/emit_arm64_vector_saturation.cpp
|
backend/arm64/emit_arm64_vector_saturation.cpp
|
||||||
backend/arm64/emit_context.h
|
backend/arm64/emit_context.h
|
||||||
|
backend/arm64/exclusive_monitor.cpp
|
||||||
backend/arm64/fpsr_manager.cpp
|
backend/arm64/fpsr_manager.cpp
|
||||||
backend/arm64/fpsr_manager.h
|
backend/arm64/fpsr_manager.h
|
||||||
backend/arm64/reg_alloc.cpp
|
backend/arm64/reg_alloc.cpp
|
||||||
backend/arm64/reg_alloc.h
|
backend/arm64/reg_alloc.h
|
||||||
backend/arm64/stack_layout.h
|
backend/arm64/stack_layout.h
|
||||||
|
common/spin_lock_arm64.cpp
|
||||||
|
common/spin_lock_arm64.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
if ("A32" IN_LIST DYNARMIC_FRONTENDS)
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
#include "dynarmic/backend/arm64/devirtualize.h"
|
#include "dynarmic/backend/arm64/devirtualize.h"
|
||||||
#include "dynarmic/backend/arm64/emit_arm64.h"
|
#include "dynarmic/backend/arm64/emit_arm64.h"
|
||||||
#include "dynarmic/backend/arm64/stack_layout.h"
|
#include "dynarmic/backend/arm64/stack_layout.h"
|
||||||
|
#include "dynarmic/common/cast_util.h"
|
||||||
#include "dynarmic/common/fp/fpcr.h"
|
#include "dynarmic/common/fp/fpcr.h"
|
||||||
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
#include "dynarmic/frontend/A32/a32_location_descriptor.h"
|
||||||
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
#include "dynarmic/frontend/A32/translate/a32_translate.h"
|
||||||
|
#include "dynarmic/interface/exclusive_monitor.h"
|
||||||
#include "dynarmic/ir/opt/passes.h"
|
#include "dynarmic/ir/opt/passes.h"
|
||||||
|
|
||||||
namespace Dynarmic::Backend::Arm64 {
|
namespace Dynarmic::Backend::Arm64 {
|
||||||
|
@ -39,6 +41,61 @@ static void* EmitCallTrampoline(oaknut::CodeGenerator& code, T* this_) {
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<auto callback, typename T>
|
||||||
|
static void* EmitExclusiveReadCallTrampoline(oaknut::CodeGenerator& code, const A32::UserConfig& conf) {
|
||||||
|
using namespace oaknut::util;
|
||||||
|
|
||||||
|
oaknut::Label l_addr, l_this;
|
||||||
|
|
||||||
|
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr) -> T {
|
||||||
|
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||||
|
return (conf.callbacks->*callback)(vaddr);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
void* target = code.ptr<void*>();
|
||||||
|
code.LDR(X0, l_this);
|
||||||
|
code.LDR(Xscratch0, l_addr);
|
||||||
|
code.BR(Xscratch0);
|
||||||
|
|
||||||
|
code.align(8);
|
||||||
|
code.l(l_this);
|
||||||
|
code.dx(mcl::bit_cast<u64>(&conf));
|
||||||
|
code.l(l_addr);
|
||||||
|
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<auto callback, typename T>
|
||||||
|
static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const A32::UserConfig& conf) {
|
||||||
|
using namespace oaknut::util;
|
||||||
|
|
||||||
|
oaknut::Label l_addr, l_this;
|
||||||
|
|
||||||
|
auto fn = [](const A32::UserConfig& conf, A32::VAddr vaddr, T value) -> u32 {
|
||||||
|
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
|
||||||
|
[&](T expected) -> bool {
|
||||||
|
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||||
|
})
|
||||||
|
? 0
|
||||||
|
: 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
void* target = code.ptr<void*>();
|
||||||
|
code.LDR(X0, l_this);
|
||||||
|
code.LDR(Xscratch0, l_addr);
|
||||||
|
code.BR(Xscratch0);
|
||||||
|
|
||||||
|
code.align(8);
|
||||||
|
code.l(l_this);
|
||||||
|
code.dx(mcl::bit_cast<u64>(&conf));
|
||||||
|
code.l(l_addr);
|
||||||
|
code.dx(mcl::bit_cast<u64>(Common::FptrCast(fn)));
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
A32AddressSpace::A32AddressSpace(const A32::UserConfig& conf)
|
||||||
: conf(conf)
|
: conf(conf)
|
||||||
, mem(conf.code_cache_size)
|
, mem(conf.code_cache_size)
|
||||||
|
@ -121,11 +178,23 @@ void A32AddressSpace::EmitPrelude() {
|
||||||
prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
prelude_info.read_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead16>(code, conf.callbacks);
|
||||||
prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
prelude_info.read_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead32>(code, conf.callbacks);
|
||||||
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
prelude_info.read_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryRead64>(code, conf.callbacks);
|
||||||
|
prelude_info.exclusive_read_memory_8 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead8, u8>(code, conf);
|
||||||
|
prelude_info.exclusive_read_memory_16 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead16, u16>(code, conf);
|
||||||
|
prelude_info.exclusive_read_memory_32 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead32, u32>(code, conf);
|
||||||
|
prelude_info.exclusive_read_memory_64 = EmitExclusiveReadCallTrampoline<&A32::UserCallbacks::MemoryRead64, u64>(code, conf);
|
||||||
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
prelude_info.write_memory_8 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite8>(code, conf.callbacks);
|
||||||
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
prelude_info.write_memory_16 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite16>(code, conf.callbacks);
|
||||||
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
prelude_info.write_memory_32 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite32>(code, conf.callbacks);
|
||||||
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
prelude_info.write_memory_64 = EmitCallTrampoline<&A32::UserCallbacks::MemoryWrite64>(code, conf.callbacks);
|
||||||
|
prelude_info.exclusive_write_memory_8 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive8, u8>(code, conf);
|
||||||
|
prelude_info.exclusive_write_memory_16 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive16, u16>(code, conf);
|
||||||
|
prelude_info.exclusive_write_memory_32 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive32, u32>(code, conf);
|
||||||
|
prelude_info.exclusive_write_memory_64 = EmitExclusiveWriteCallTrampoline<&A32::UserCallbacks::MemoryWriteExclusive64, u64>(code, conf);
|
||||||
|
prelude_info.call_svc = EmitCallTrampoline<&A32::UserCallbacks::CallSVC>(code, conf.callbacks);
|
||||||
|
prelude_info.exception_raised = EmitCallTrampoline<&A32::UserCallbacks::ExceptionRaised>(code, conf.callbacks);
|
||||||
prelude_info.isb_raised = EmitCallTrampoline<&A32::UserCallbacks::InstructionSynchronizationBarrierRaised>(code, conf.callbacks);
|
prelude_info.isb_raised = EmitCallTrampoline<&A32::UserCallbacks::InstructionSynchronizationBarrierRaised>(code, conf.callbacks);
|
||||||
|
prelude_info.add_ticks = EmitCallTrampoline<&A32::UserCallbacks::AddTicks>(code, conf.callbacks);
|
||||||
|
prelude_info.get_ticks_remaining = EmitCallTrampoline<&A32::UserCallbacks::GetTicksRemaining>(code, conf.callbacks);
|
||||||
|
|
||||||
prelude_info.end_of_prelude = code.ptr<u32*>();
|
prelude_info.end_of_prelude = code.ptr<u32*>();
|
||||||
|
|
||||||
|
@ -185,6 +254,18 @@ void A32AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||||
case LinkTarget::ReadMemory64:
|
case LinkTarget::ReadMemory64:
|
||||||
c.BL(prelude_info.read_memory_64);
|
c.BL(prelude_info.read_memory_64);
|
||||||
break;
|
break;
|
||||||
|
case LinkTarget::ExclusiveReadMemory8:
|
||||||
|
c.BL(prelude_info.exclusive_read_memory_8);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ExclusiveReadMemory16:
|
||||||
|
c.BL(prelude_info.exclusive_read_memory_16);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ExclusiveReadMemory32:
|
||||||
|
c.BL(prelude_info.exclusive_read_memory_32);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ExclusiveReadMemory64:
|
||||||
|
c.BL(prelude_info.exclusive_read_memory_64);
|
||||||
|
break;
|
||||||
case LinkTarget::WriteMemory8:
|
case LinkTarget::WriteMemory8:
|
||||||
c.BL(prelude_info.write_memory_8);
|
c.BL(prelude_info.write_memory_8);
|
||||||
break;
|
break;
|
||||||
|
@ -197,9 +278,33 @@ void A32AddressSpace::Link(EmittedBlockInfo& block_info) {
|
||||||
case LinkTarget::WriteMemory64:
|
case LinkTarget::WriteMemory64:
|
||||||
c.BL(prelude_info.write_memory_64);
|
c.BL(prelude_info.write_memory_64);
|
||||||
break;
|
break;
|
||||||
|
case LinkTarget::ExclusiveWriteMemory8:
|
||||||
|
c.BL(prelude_info.exclusive_write_memory_8);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ExclusiveWriteMemory16:
|
||||||
|
c.BL(prelude_info.exclusive_write_memory_16);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ExclusiveWriteMemory32:
|
||||||
|
c.BL(prelude_info.exclusive_write_memory_32);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ExclusiveWriteMemory64:
|
||||||
|
c.BL(prelude_info.exclusive_write_memory_64);
|
||||||
|
break;
|
||||||
|
case LinkTarget::CallSVC:
|
||||||
|
c.BL(prelude_info.call_svc);
|
||||||
|
break;
|
||||||
|
case LinkTarget::ExceptionRaised:
|
||||||
|
c.BL(prelude_info.exception_raised);
|
||||||
|
break;
|
||||||
case LinkTarget::InstructionSynchronizationBarrierRaised:
|
case LinkTarget::InstructionSynchronizationBarrierRaised:
|
||||||
c.BL(prelude_info.isb_raised);
|
c.BL(prelude_info.isb_raised);
|
||||||
break;
|
break;
|
||||||
|
case LinkTarget::AddTicks:
|
||||||
|
c.BL(prelude_info.add_ticks);
|
||||||
|
break;
|
||||||
|
case LinkTarget::GetTicksRemaining:
|
||||||
|
c.BL(prelude_info.get_ticks_remaining);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT_FALSE("Invalid relocation target");
|
ASSERT_FALSE("Invalid relocation target");
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,17 +54,30 @@ private:
|
||||||
|
|
||||||
using RunCodeFuncType = HaltReason (*)(CodePtr entry_point, A32JitState* context, volatile u32* halt_reason);
|
using RunCodeFuncType = HaltReason (*)(CodePtr entry_point, A32JitState* context, volatile u32* halt_reason);
|
||||||
RunCodeFuncType run_code;
|
RunCodeFuncType run_code;
|
||||||
|
RunCodeFuncType step_code;
|
||||||
void* return_from_run_code;
|
void* return_from_run_code;
|
||||||
|
|
||||||
void* read_memory_8;
|
void* read_memory_8;
|
||||||
void* read_memory_16;
|
void* read_memory_16;
|
||||||
void* read_memory_32;
|
void* read_memory_32;
|
||||||
void* read_memory_64;
|
void* read_memory_64;
|
||||||
|
void* exclusive_read_memory_8;
|
||||||
|
void* exclusive_read_memory_16;
|
||||||
|
void* exclusive_read_memory_32;
|
||||||
|
void* exclusive_read_memory_64;
|
||||||
void* write_memory_8;
|
void* write_memory_8;
|
||||||
void* write_memory_16;
|
void* write_memory_16;
|
||||||
void* write_memory_32;
|
void* write_memory_32;
|
||||||
void* write_memory_64;
|
void* write_memory_64;
|
||||||
|
void* exclusive_write_memory_8;
|
||||||
|
void* exclusive_write_memory_16;
|
||||||
|
void* exclusive_write_memory_32;
|
||||||
|
void* exclusive_write_memory_64;
|
||||||
|
void* call_svc;
|
||||||
|
void* exception_raised;
|
||||||
void* isb_raised;
|
void* isb_raised;
|
||||||
|
void* add_ticks;
|
||||||
|
void* get_ticks_remaining;
|
||||||
} prelude_info;
|
} prelude_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,18 @@ enum class LinkTarget {
|
||||||
ReadMemory16,
|
ReadMemory16,
|
||||||
ReadMemory32,
|
ReadMemory32,
|
||||||
ReadMemory64,
|
ReadMemory64,
|
||||||
|
ExclusiveReadMemory8,
|
||||||
|
ExclusiveReadMemory16,
|
||||||
|
ExclusiveReadMemory32,
|
||||||
|
ExclusiveReadMemory64,
|
||||||
WriteMemory8,
|
WriteMemory8,
|
||||||
WriteMemory16,
|
WriteMemory16,
|
||||||
WriteMemory32,
|
WriteMemory32,
|
||||||
WriteMemory64,
|
WriteMemory64,
|
||||||
|
ExclusiveWriteMemory8,
|
||||||
|
ExclusiveWriteMemory16,
|
||||||
|
ExclusiveWriteMemory32,
|
||||||
|
ExclusiveWriteMemory64,
|
||||||
CallSVC,
|
CallSVC,
|
||||||
ExceptionRaised,
|
ExceptionRaised,
|
||||||
InstructionSynchronizationBarrierRaised,
|
InstructionSynchronizationBarrierRaised,
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "dynarmic/backend/arm64/emit_arm64.h"
|
#include "dynarmic/backend/arm64/emit_arm64.h"
|
||||||
#include "dynarmic/backend/arm64/emit_context.h"
|
#include "dynarmic/backend/arm64/emit_context.h"
|
||||||
#include "dynarmic/backend/arm64/reg_alloc.h"
|
#include "dynarmic/backend/arm64/reg_alloc.h"
|
||||||
|
#include "dynarmic/ir/acc_type.h"
|
||||||
#include "dynarmic/ir/basic_block.h"
|
#include "dynarmic/ir/basic_block.h"
|
||||||
#include "dynarmic/ir/microinstruction.h"
|
#include "dynarmic/ir/microinstruction.h"
|
||||||
#include "dynarmic/ir/opcodes.h"
|
#include "dynarmic/ir/opcodes.h"
|
||||||
|
@ -18,6 +19,68 @@ namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
|
static bool IsOrdered(IR::AccType acctype) {
|
||||||
|
return acctype == IR::AccType::ORDERED || acctype == IR::AccType::ORDEREDRW || acctype == IR::AccType::LIMITEDORDERED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EmitReadMemory(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst, LinkTarget fn) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
||||||
|
const bool ordered = IsOrdered(args[2].GetImmediateAccType());
|
||||||
|
|
||||||
|
EmitRelocation(code, ctx, fn);
|
||||||
|
if (ordered) {
|
||||||
|
code.DMB(oaknut::BarrierOp::ISH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EmitExclusiveReadMemory(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst, LinkTarget fn) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
||||||
|
const bool ordered = IsOrdered(args[2].GetImmediateAccType());
|
||||||
|
|
||||||
|
code.MOV(Wscratch0, 1);
|
||||||
|
code.STRB(Wscratch0, Xstate, offsetof(A32JitState, exclusive_state));
|
||||||
|
EmitRelocation(code, ctx, fn);
|
||||||
|
if (ordered) {
|
||||||
|
code.DMB(oaknut::BarrierOp::ISH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EmitWriteMemory(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst, LinkTarget fn) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
ctx.reg_alloc.PrepareForCall(inst, {}, args[1], args[2]);
|
||||||
|
const bool ordered = IsOrdered(args[3].GetImmediateAccType());
|
||||||
|
|
||||||
|
if (ordered) {
|
||||||
|
code.DMB(oaknut::BarrierOp::ISH);
|
||||||
|
}
|
||||||
|
EmitRelocation(code, ctx, fn);
|
||||||
|
if (ordered) {
|
||||||
|
code.DMB(oaknut::BarrierOp::ISH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EmitExclusiveWriteMemory(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst, LinkTarget fn) {
|
||||||
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
|
ctx.reg_alloc.PrepareForCall(inst, {}, args[1], args[2]);
|
||||||
|
const bool ordered = IsOrdered(args[3].GetImmediateAccType());
|
||||||
|
|
||||||
|
oaknut::Label end;
|
||||||
|
|
||||||
|
if (ordered) {
|
||||||
|
code.DMB(oaknut::BarrierOp::ISH);
|
||||||
|
}
|
||||||
|
code.LDRB(Wscratch0, Xstate, offsetof(A32JitState, exclusive_state));
|
||||||
|
code.CBZ(Wscratch0, end);
|
||||||
|
code.STRB(WZR, Xstate, offsetof(A32JitState, exclusive_state));
|
||||||
|
EmitRelocation(code, ctx, fn);
|
||||||
|
if (ordered) {
|
||||||
|
code.DMB(oaknut::BarrierOp::ISH);
|
||||||
|
}
|
||||||
|
code.l(end);
|
||||||
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ClearExclusive>(oaknut::CodeGenerator& code, EmitContext&, IR::Inst*) {
|
void EmitIR<IR::Opcode::A32ClearExclusive>(oaknut::CodeGenerator& code, EmitContext&, IR::Inst*) {
|
||||||
code.STR(WZR, Xstate, offsetof(A32JitState, exclusive_state));
|
code.STR(WZR, Xstate, offsetof(A32JitState, exclusive_state));
|
||||||
|
@ -25,138 +88,82 @@ void EmitIR<IR::Opcode::A32ClearExclusive>(oaknut::CodeGenerator& code, EmitCont
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ReadMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ReadMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
EmitReadMemory(code, ctx, inst, LinkTarget::ReadMemory8);
|
||||||
|
|
||||||
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
|
||||||
|
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReadMemory8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ReadMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ReadMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
EmitReadMemory(code, ctx, inst, LinkTarget::ReadMemory16);
|
||||||
|
|
||||||
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
|
||||||
|
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReadMemory16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ReadMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ReadMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
EmitReadMemory(code, ctx, inst, LinkTarget::ReadMemory32);
|
||||||
|
|
||||||
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
|
||||||
|
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReadMemory32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ReadMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ReadMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
EmitReadMemory(code, ctx, inst, LinkTarget::ReadMemory64);
|
||||||
|
|
||||||
ctx.reg_alloc.PrepareForCall(inst, {}, args[1]);
|
|
||||||
|
|
||||||
EmitRelocation(code, ctx, LinkTarget::ReadMemory64);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ExclusiveReadMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
EmitExclusiveReadMemory(code, ctx, inst, LinkTarget::ExclusiveReadMemory8);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ExclusiveReadMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
EmitExclusiveReadMemory(code, ctx, inst, LinkTarget::ExclusiveReadMemory16);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ExclusiveReadMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
EmitExclusiveReadMemory(code, ctx, inst, LinkTarget::ExclusiveReadMemory32);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ExclusiveReadMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ExclusiveReadMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
EmitExclusiveReadMemory(code, ctx, inst, LinkTarget::ExclusiveReadMemory64);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32WriteMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32WriteMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
EmitWriteMemory(code, ctx, inst, LinkTarget::WriteMemory8);
|
||||||
|
|
||||||
ctx.reg_alloc.PrepareForCall(nullptr, {}, args[1], args[2]);
|
|
||||||
|
|
||||||
EmitRelocation(code, ctx, LinkTarget::WriteMemory8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32WriteMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32WriteMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
EmitWriteMemory(code, ctx, inst, LinkTarget::WriteMemory16);
|
||||||
|
|
||||||
ctx.reg_alloc.PrepareForCall(nullptr, {}, args[1], args[2]);
|
|
||||||
|
|
||||||
EmitRelocation(code, ctx, LinkTarget::WriteMemory16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32WriteMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32WriteMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
EmitWriteMemory(code, ctx, inst, LinkTarget::WriteMemory32);
|
||||||
|
|
||||||
ctx.reg_alloc.PrepareForCall(nullptr, {}, args[1], args[2]);
|
|
||||||
|
|
||||||
EmitRelocation(code, ctx, LinkTarget::WriteMemory32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32WriteMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32WriteMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
EmitWriteMemory(code, ctx, inst, LinkTarget::WriteMemory64);
|
||||||
|
|
||||||
ctx.reg_alloc.PrepareForCall(nullptr, {}, args[1], args[2]);
|
|
||||||
|
|
||||||
EmitRelocation(code, ctx, LinkTarget::WriteMemory64);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory8>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
EmitExclusiveWriteMemory(code, ctx, inst, LinkTarget::ExclusiveWriteMemory8);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory16>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
EmitExclusiveWriteMemory(code, ctx, inst, LinkTarget::ExclusiveWriteMemory16);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
EmitExclusiveWriteMemory(code, ctx, inst, LinkTarget::ExclusiveWriteMemory32);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A32ExclusiveWriteMemory64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
EmitExclusiveWriteMemory(code, ctx, inst, LinkTarget::ExclusiveWriteMemory64);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::Arm64
|
} // namespace Dynarmic::Backend::Arm64
|
||||||
|
|
60
src/dynarmic/backend/arm64/exclusive_monitor.cpp
Normal file
60
src/dynarmic/backend/arm64/exclusive_monitor.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2022 MerryMage
|
||||||
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "dynarmic/interface/exclusive_monitor.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <mcl/assert.hpp>
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
|
||||||
|
ExclusiveMonitor::ExclusiveMonitor(size_t processor_count)
|
||||||
|
: exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {
|
||||||
|
Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ExclusiveMonitor::GetProcessorCount() const {
|
||||||
|
return exclusive_addresses.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExclusiveMonitor::Lock() {
|
||||||
|
lock.Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExclusiveMonitor::Unlock() {
|
||||||
|
lock.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExclusiveMonitor::CheckAndClear(size_t processor_id, VAddr address) {
|
||||||
|
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
|
||||||
|
|
||||||
|
Lock();
|
||||||
|
if (exclusive_addresses[processor_id] != masked_address) {
|
||||||
|
Unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (VAddr& other_address : exclusive_addresses) {
|
||||||
|
if (other_address == masked_address) {
|
||||||
|
other_address = INVALID_EXCLUSIVE_ADDRESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExclusiveMonitor::Clear() {
|
||||||
|
Lock();
|
||||||
|
std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS);
|
||||||
|
Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExclusiveMonitor::ClearProcessor(size_t processor_id) {
|
||||||
|
Lock();
|
||||||
|
exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS;
|
||||||
|
Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dynarmic
|
75
src/dynarmic/common/spin_lock_arm64.cpp
Normal file
75
src/dynarmic/common/spin_lock_arm64.cpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2022 MerryMage
|
||||||
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <oaknut/code_block.hpp>
|
||||||
|
#include <oaknut/oaknut.hpp>
|
||||||
|
|
||||||
|
#include "dynarmic/backend/arm64/abi.h"
|
||||||
|
#include "dynarmic/common/spin_lock.h"
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
|
||||||
|
using Backend::Arm64::Wscratch0;
|
||||||
|
using Backend::Arm64::Wscratch1;
|
||||||
|
using namespace oaknut::util;
|
||||||
|
|
||||||
|
void EmitSpinLockLock(oaknut::CodeGenerator& code, oaknut::XReg ptr) {
|
||||||
|
oaknut::Label start, loop;
|
||||||
|
|
||||||
|
code.MOV(Wscratch1, 1);
|
||||||
|
code.SEVL();
|
||||||
|
code.l(start);
|
||||||
|
code.WFE();
|
||||||
|
code.l(loop);
|
||||||
|
code.LDAXR(Wscratch0, ptr);
|
||||||
|
code.CBNZ(Wscratch0, start);
|
||||||
|
code.STXR(Wscratch0, Wscratch1, ptr);
|
||||||
|
code.CBNZ(Wscratch0, loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitSpinLockUnlock(oaknut::CodeGenerator& code, oaknut::XReg ptr) {
|
||||||
|
code.STLR(WZR, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct SpinLockImpl {
|
||||||
|
SpinLockImpl();
|
||||||
|
|
||||||
|
oaknut::CodeBlock mem;
|
||||||
|
oaknut::CodeGenerator code;
|
||||||
|
void (*lock)(volatile int*);
|
||||||
|
void (*unlock)(volatile int*);
|
||||||
|
};
|
||||||
|
|
||||||
|
SpinLockImpl impl;
|
||||||
|
|
||||||
|
SpinLockImpl::SpinLockImpl()
|
||||||
|
: mem{4096}
|
||||||
|
, code{mem.ptr()} {
|
||||||
|
mem.unprotect();
|
||||||
|
|
||||||
|
lock = code.ptr<void (*)(volatile int*)>();
|
||||||
|
EmitSpinLockLock(code, X0);
|
||||||
|
code.RET();
|
||||||
|
|
||||||
|
unlock = code.ptr<void (*)(volatile int*)>();
|
||||||
|
EmitSpinLockUnlock(code, X0);
|
||||||
|
code.RET();
|
||||||
|
|
||||||
|
mem.protect();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void SpinLock::Lock() {
|
||||||
|
impl.lock(&storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpinLock::Unlock() {
|
||||||
|
impl.unlock(&storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dynarmic
|
15
src/dynarmic/common/spin_lock_arm64.h
Normal file
15
src/dynarmic/common/spin_lock_arm64.h
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2022 MerryMage
|
||||||
|
* SPDX-License-Identifier: 0BSD
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <oaknut/oaknut.hpp>
|
||||||
|
|
||||||
|
namespace Dynarmic {
|
||||||
|
|
||||||
|
void EmitSpinLockLock(oaknut::CodeGenerator& code, oaknut::XReg ptr);
|
||||||
|
void EmitSpinLockUnlock(oaknut::CodeGenerator& code, oaknut::XReg ptr);
|
||||||
|
|
||||||
|
} // namespace Dynarmic
|
Loading…
Reference in a new issue