mirror of
https://git.suyu.dev/suyu/dynarmic.git
synced 2026-03-26 14:48:43 +00:00
A32 global exlcusive monitor
This commit is contained in:
@@ -9,8 +9,10 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <mp/traits/integer_of_size.h>
|
||||
|
||||
#include <dynarmic/A32/coprocessor.h>
|
||||
#include <dynarmic/A32/exclusive_monitor.h>
|
||||
|
||||
#include "backend/x64/a32_emit_x64.h"
|
||||
#include "backend/x64/a32_jitstate.h"
|
||||
@@ -848,15 +850,6 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
|
||||
code.mov(code.byte[r15 + offsetof(A32JitState, exclusive_state)], u8(0));
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32SetExclusive(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ASSERT(args[1].IsImmediate());
|
||||
const Xbyak::Reg32 address = ctx.reg_alloc.UseGpr(args[0]).cvt32();
|
||||
|
||||
code.mov(code.byte[r15 + offsetof(A32JitState, exclusive_state)], u8(1));
|
||||
code.mov(dword[r15 + offsetof(A32JitState, exclusive_address)], address);
|
||||
}
|
||||
|
||||
std::optional<A32EmitX64::DoNotFastmemMarker> A32EmitX64::ShouldFastmem(A32EmitContext& ctx, IR::Inst* inst) const {
|
||||
if (!conf.fastmem_pointer || !exception_handler.SupportsFastmem()) {
|
||||
return std::nullopt;
|
||||
@@ -1062,43 +1055,82 @@ void A32EmitX64::EmitA32WriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
}
|
||||
|
||||
template <size_t bitsize, auto callback>
|
||||
void A32EmitX64::ExclusiveWriteMemory(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
void A32EmitX64::ExclusiveReadMemory(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
using T = mp::unsigned_integer_of_size<bitsize>;
|
||||
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]);
|
||||
const Xbyak::Reg32 passed = ctx.reg_alloc.ScratchGpr().cvt32();
|
||||
const Xbyak::Reg32 tmp = code.ABI_RETURN.cvt32(); // Use one of the unused HostCall registers.
|
||||
|
||||
ctx.reg_alloc.HostCall(inst, {}, args[0]);
|
||||
|
||||
code.mov(code.byte[r15 + offsetof(A32JitState, exclusive_state)], u8(1));
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
code.CallLambda(
|
||||
[](A32::UserConfig& conf, u32 vaddr) -> T {
|
||||
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
|
||||
return (conf.callbacks->*callback)(vaddr);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
template <size_t bitsize, auto callback>
|
||||
void A32EmitX64::ExclusiveWriteMemory(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
using T = mp::unsigned_integer_of_size<bitsize>;
|
||||
|
||||
ASSERT(conf.global_monitor != nullptr);
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
ctx.reg_alloc.HostCall(inst, {}, args[0], args[1]);
|
||||
|
||||
Xbyak::Label end;
|
||||
|
||||
code.mov(passed, u32(1));
|
||||
code.mov(code.ABI_RETURN, u32(1));
|
||||
code.cmp(code.byte[r15 + offsetof(A32JitState, exclusive_state)], u8(0));
|
||||
code.je(end);
|
||||
code.mov(tmp, code.ABI_PARAM2);
|
||||
code.xor_(tmp, dword[r15 + offsetof(A32JitState, exclusive_address)]);
|
||||
code.test(tmp, A32JitState::RESERVATION_GRANULE_MASK);
|
||||
code.jne(end);
|
||||
code.mov(code.byte[r15 + offsetof(A32JitState, exclusive_state)], u8(0));
|
||||
Devirtualize<callback>(conf.callbacks).EmitCall(code);
|
||||
code.xor_(passed, passed);
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(&conf));
|
||||
code.CallLambda(
|
||||
[](A32::UserConfig& conf, u32 vaddr, T value) -> u32 {
|
||||
return conf.global_monitor->DoExclusiveOperation<u8>(conf.processor_id, vaddr,
|
||||
[&](T expected) -> bool {
|
||||
return (conf.callbacks->*callback)(vaddr, value, expected);
|
||||
}) ? 0 : 1;
|
||||
}
|
||||
);
|
||||
code.L(end);
|
||||
}
|
||||
|
||||
ctx.reg_alloc.DefineValue(inst, passed);
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveWriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ExclusiveWriteMemory<8, &A32::UserCallbacks::MemoryWrite8>(ctx, inst);
|
||||
ExclusiveWriteMemory<8, &A32::UserCallbacks::MemoryWriteExclusive8>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveWriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ExclusiveWriteMemory<16, &A32::UserCallbacks::MemoryWrite16>(ctx, inst);
|
||||
ExclusiveWriteMemory<16, &A32::UserCallbacks::MemoryWriteExclusive16>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveWriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ExclusiveWriteMemory<32, &A32::UserCallbacks::MemoryWrite32>(ctx, inst);
|
||||
ExclusiveWriteMemory<32, &A32::UserCallbacks::MemoryWriteExclusive32>(ctx, inst);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32ExclusiveWriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
ExclusiveWriteMemory<64, &A32::UserCallbacks::MemoryWrite64>(ctx, inst);
|
||||
ExclusiveWriteMemory<64, &A32::UserCallbacks::MemoryWriteExclusive64>(ctx, inst);
|
||||
}
|
||||
|
||||
static void EmitCoprocessorException() {
|
||||
|
||||
@@ -103,6 +103,8 @@ protected:
|
||||
template<std::size_t bitsize, auto callback>
|
||||
void WriteMemory(A32EmitContext& ctx, IR::Inst* inst);
|
||||
template<std::size_t bitsize, auto callback>
|
||||
void ExclusiveReadMemory(A32EmitContext& ctx, IR::Inst* inst);
|
||||
template<std::size_t bitsize, auto callback>
|
||||
void ExclusiveWriteMemory(A32EmitContext& ctx, IR::Inst* inst);
|
||||
|
||||
// Terminal instruction emitters
|
||||
|
||||
62
src/backend/x64/a32_exclusive_monitor.cpp
Normal file
62
src/backend/x64/a32_exclusive_monitor.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2018 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <dynarmic/A32/exclusive_monitor.h>
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Dynarmic {
|
||||
namespace A32 {
|
||||
|
||||
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() {
|
||||
while (is_locked.test_and_set(std::memory_order_acquire)) {}
|
||||
}
|
||||
|
||||
void ExclusiveMonitor::Unlock() {
|
||||
is_locked.clear(std::memory_order_release);
|
||||
}
|
||||
|
||||
bool ExclusiveMonitor::CheckAndClear(size_t processor_id, VAddr address) {
|
||||
const VAddr masked_address = address;
|
||||
|
||||
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 A32
|
||||
} // namespace Dynarmic
|
||||
@@ -55,9 +55,7 @@ struct A32JitState {
|
||||
bool check_bit = false;
|
||||
|
||||
// Exclusive state
|
||||
static constexpr u32 RESERVATION_GRANULE_MASK = 0xFFFFFFF8;
|
||||
u32 exclusive_state = 0;
|
||||
u32 exclusive_address = 0;
|
||||
|
||||
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
|
||||
static constexpr size_t RSBPtrMask = RSBSize - 1;
|
||||
@@ -90,7 +88,6 @@ struct A32JitState {
|
||||
fpsr_nzcv = src.fpsr_nzcv;
|
||||
|
||||
exclusive_state = 0;
|
||||
exclusive_address = 0;
|
||||
|
||||
if (reset_rsb) {
|
||||
ResetRSB();
|
||||
|
||||
Reference in New Issue
Block a user