A32 global exlcusive monitor

This commit is contained in:
MerryMage
2020-06-16 15:46:47 +01:00
parent 58abdcce5b
commit 2c1a4843ad
12 changed files with 260 additions and 56 deletions

View File

@@ -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() {

View File

@@ -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

View 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

View File

@@ -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();