mirror of
https://git.suyu.dev/suyu/dynarmic.git
synced 2026-03-07 16:42:55 +00:00
Exclusive Monitor: Rework exclusive monitor interface.
This commit is contained in:
committed by
MerryMage
parent
b5d8b24a3c
commit
97b9d3e058
@@ -703,30 +703,6 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(0));
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64SetExclusive(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.global_monitor) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
|
||||
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(1));
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u8 size) {
|
||||
conf.global_monitor->Mark(conf.processor_id, vaddr, size);
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[1].IsImmediate());
|
||||
const Xbyak::Reg64 address = ctx.reg_alloc.UseGpr(args[0]);
|
||||
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(1));
|
||||
code.mov(qword[r15 + offsetof(A64JitState, exclusive_address)], address);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t page_bits = 12;
|
||||
@@ -951,6 +927,89 @@ void A64EmitX64::EmitA64ReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
ctx.reg_alloc.DefineValue(inst, xmm1);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ctx.reg_alloc.HostCall(inst, {}, args[0]);
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(1));
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr) -> u8 {
|
||||
return conf.global_monitor->ReadAndMark<u8>(conf.processor_id, vaddr, [&]() -> u8 {
|
||||
return conf.callbacks->MemoryRead8(vaddr);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ctx.reg_alloc.HostCall(inst, {}, args[0]);
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(1));
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr) -> u16 {
|
||||
return conf.global_monitor->ReadAndMark<u16>(conf.processor_id, vaddr, [&]() -> u16 {
|
||||
return conf.callbacks->MemoryRead16(vaddr);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ctx.reg_alloc.HostCall(inst, {}, args[0]);
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(1));
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr) -> u32 {
|
||||
return conf.global_monitor->ReadAndMark<u32>(conf.processor_id, vaddr, [&]() -> u32 {
|
||||
return conf.callbacks->MemoryRead32(vaddr);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ctx.reg_alloc.HostCall(inst, {}, args[0]);
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(1));
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr) -> u64 {
|
||||
return conf.global_monitor->ReadAndMark<u64>(conf.processor_id, vaddr, [&]() -> u64 {
|
||||
return conf.callbacks->MemoryRead64(vaddr);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm();
|
||||
ctx.reg_alloc.EndOfAllocScope();
|
||||
ctx.reg_alloc.HostCall(nullptr, {}, args[0]);
|
||||
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(1));
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
code.sub(rsp, 16 + ABI_SHADOW_SPACE);
|
||||
code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]);
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, A64::Vector& ret) {
|
||||
ret = conf.global_monitor->ReadAndMark<A64::Vector>(conf.processor_id, vaddr, [&]() -> A64::Vector {
|
||||
return conf.callbacks->MemoryRead128(vaddr);
|
||||
});
|
||||
}
|
||||
);
|
||||
code.movups(result, xword[rsp + ABI_SHADOW_SPACE]);
|
||||
code.add(rsp, 16 + ABI_SHADOW_SPACE);
|
||||
ctx.reg_alloc.DefineValue(inst, result);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64WriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
if (conf.page_table) {
|
||||
EmitDirectPageTableMemoryWrite(ctx, inst, 8);
|
||||
@@ -1024,105 +1083,84 @@ void A64EmitX64::EmitA64WriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitExclusiveWrite(A64EmitContext& ctx, IR::Inst* inst, size_t bitsize) {
|
||||
if (conf.global_monitor) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
if (bitsize != 128) {
|
||||
ctx.reg_alloc.HostCall(inst, {}, args[0], args[1]);
|
||||
} else {
|
||||
ctx.reg_alloc.Use(args[0], ABI_PARAM2);
|
||||
ctx.reg_alloc.Use(args[1], HostLoc::XMM1);
|
||||
ctx.reg_alloc.EndOfAllocScope();
|
||||
ctx.reg_alloc.HostCall(inst);
|
||||
}
|
||||
|
||||
Xbyak::Label end;
|
||||
|
||||
code.mov(code.ABI_RETURN, u32(1));
|
||||
code.cmp(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(0));
|
||||
code.je(end);
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
switch (bitsize) {
|
||||
case 8:
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u8 value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, 1, [&]{
|
||||
conf.callbacks->MemoryWrite8(vaddr, value);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 16:
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u16 value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, 2, [&]{
|
||||
conf.callbacks->MemoryWrite16(vaddr, value);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 32:
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u32 value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, 4, [&]{
|
||||
conf.callbacks->MemoryWrite32(vaddr, value);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 64:
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u64 value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, 8, [&]{
|
||||
conf.callbacks->MemoryWrite64(vaddr, value);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 128:
|
||||
code.sub(rsp, 16 + ABI_SHADOW_SPACE);
|
||||
code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]);
|
||||
code.movaps(xword[code.ABI_PARAM3], xmm1);
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, A64::Vector& value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation(conf.processor_id, vaddr, 16, [&]{
|
||||
conf.callbacks->MemoryWrite128(vaddr, value);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
code.add(rsp, 16 + ABI_SHADOW_SPACE);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
code.L(end);
|
||||
|
||||
return;
|
||||
if (bitsize != 128) {
|
||||
ctx.reg_alloc.HostCall(inst, {}, args[0], args[1]);
|
||||
} else {
|
||||
ctx.reg_alloc.Use(args[0], ABI_PARAM2);
|
||||
ctx.reg_alloc.Use(args[1], HostLoc::XMM1);
|
||||
ctx.reg_alloc.EndOfAllocScope();
|
||||
ctx.reg_alloc.HostCall(inst);
|
||||
}
|
||||
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const Xbyak::Reg64 vaddr = ctx.reg_alloc.UseGpr(args[0]);
|
||||
const int value_idx = bitsize != 128
|
||||
? ctx.reg_alloc.UseGpr(args[1]).getIdx()
|
||||
: ctx.reg_alloc.UseXmm(args[1]).getIdx();
|
||||
|
||||
Xbyak::Label end;
|
||||
const Xbyak::Reg32 passed = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||
const Xbyak::Reg64 tmp = ctx.reg_alloc.ScratchGpr();
|
||||
|
||||
code.mov(passed, u32(1));
|
||||
code.mov(code.ABI_RETURN, u32(1));
|
||||
code.cmp(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(0));
|
||||
code.je(end);
|
||||
code.mov(tmp, vaddr);
|
||||
code.xor_(tmp, qword[r15 + offsetof(A64JitState, exclusive_address)]);
|
||||
code.test(tmp, static_cast<u32>(A64JitState::RESERVATION_GRANULE_MASK & 0xFFFF'FFFF));
|
||||
code.jne(end);
|
||||
code.mov(code.byte[r15 + offsetof(A64JitState, exclusive_state)], u8(0));
|
||||
code.call(write_fallbacks[std::make_tuple(bitsize, vaddr.getIdx(), value_idx)]);
|
||||
code.xor_(passed, passed);
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
switch (bitsize) {
|
||||
case 8:
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u8 value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<u8>(conf.processor_id, vaddr,
|
||||
[&](u8 expected) -> bool {
|
||||
return conf.callbacks->MemoryWriteExclusive8(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 16:
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u16 value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<u16>(conf.processor_id, vaddr,
|
||||
[&](u16 expected) -> bool {
|
||||
return conf.callbacks->MemoryWriteExclusive16(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 32:
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u32 value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<u32>(conf.processor_id, vaddr,
|
||||
[&](u32 expected) -> bool {
|
||||
return conf.callbacks->MemoryWriteExclusive32(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 64:
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, u64 value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<u64>(conf.processor_id, vaddr,
|
||||
[&](u64 expected) -> bool {
|
||||
return conf.callbacks->MemoryWriteExclusive64(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
break;
|
||||
case 128:
|
||||
code.sub(rsp, 16 + ABI_SHADOW_SPACE);
|
||||
code.lea(code.ABI_PARAM3, ptr[rsp + ABI_SHADOW_SPACE]);
|
||||
code.movaps(xword[code.ABI_PARAM3], xmm1);
|
||||
code.CallLambda(
|
||||
[](A64::UserConfig& conf, u64 vaddr, A64::Vector& value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<A64::Vector>(conf.processor_id, vaddr,
|
||||
[&](A64::Vector expected) -> bool {
|
||||
return conf.callbacks->MemoryWriteExclusive128(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
code.add(rsp, 16 + ABI_SHADOW_SPACE);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
code.L(end);
|
||||
|
||||
ctx.reg_alloc.DefineValue(inst, passed);
|
||||
}
|
||||
|
||||
void A64EmitX64::EmitA64ExclusiveWriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
namespace Dynarmic {
|
||||
namespace A64 {
|
||||
|
||||
ExclusiveMonitor::ExclusiveMonitor(size_t processor_count) : exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS) {
|
||||
ExclusiveMonitor::ExclusiveMonitor(size_t processor_count) :
|
||||
exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {
|
||||
Unlock();
|
||||
}
|
||||
|
||||
@@ -19,15 +20,6 @@ size_t ExclusiveMonitor::GetProcessorCount() const {
|
||||
return exclusive_addresses.size();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Mark(size_t processor_id, VAddr address, size_t size) {
|
||||
ASSERT(size <= 16);
|
||||
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
|
||||
|
||||
Lock();
|
||||
exclusive_addresses[processor_id] = masked_address;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Lock() {
|
||||
while (is_locked.test_and_set(std::memory_order_acquire)) {}
|
||||
}
|
||||
@@ -36,8 +28,7 @@ void ExclusiveMonitor::Unlock() {
|
||||
is_locked.clear(std::memory_order_release);
|
||||
}
|
||||
|
||||
bool ExclusiveMonitor::CheckAndClear(size_t processor_id, VAddr address, size_t size) {
|
||||
ASSERT(size <= 16);
|
||||
bool ExclusiveMonitor::CheckAndClear(size_t processor_id, VAddr address) {
|
||||
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
|
||||
|
||||
Lock();
|
||||
@@ -60,7 +51,7 @@ void ExclusiveMonitor::Clear() {
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Clear(size_t processor_id) {
|
||||
void ExclusiveMonitor::ClearProcessor(size_t processor_id) {
|
||||
Lock();
|
||||
exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS;
|
||||
Unlock();
|
||||
|
||||
@@ -59,7 +59,6 @@ struct A64JitState {
|
||||
// Exclusive state
|
||||
static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull;
|
||||
u8 exclusive_state = 0;
|
||||
u64 exclusive_address = 0;
|
||||
|
||||
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
||||
static constexpr size_t RSBPtrMask = RSBSize - 1;
|
||||
|
||||
Reference in New Issue
Block a user