A32: Implement barrier instructions introduced in ARMv7
Provides basic implementations of the barrier instruction introduced within ARMv7. Currently these simply mirror the behavior of the AArch64 equivalents.
This commit is contained in:
parent
07218df353
commit
8316d231e9
11 changed files with 139 additions and 14 deletions
|
@ -95,6 +95,7 @@ add_library(dynarmic
|
||||||
frontend/A32/translate/translate.cpp
|
frontend/A32/translate/translate.cpp
|
||||||
frontend/A32/translate/translate.h
|
frontend/A32/translate/translate.h
|
||||||
frontend/A32/translate/translate_arm.cpp
|
frontend/A32/translate/translate_arm.cpp
|
||||||
|
frontend/A32/translate/translate_arm/barrier.cpp
|
||||||
frontend/A32/translate/translate_arm/branch.cpp
|
frontend/A32/translate/translate_arm/branch.cpp
|
||||||
frontend/A32/translate/translate_arm/coprocessor.cpp
|
frontend/A32/translate/translate_arm/coprocessor.cpp
|
||||||
frontend/A32/translate/translate_arm/data_processing.cpp
|
frontend/A32/translate/translate_arm/data_processing.cpp
|
||||||
|
|
|
@ -615,6 +615,23 @@ void A32EmitX64::EmitA32SetGEFlagsCompressed(A32EmitContext& ctx, IR::Inst* inst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void A32EmitX64::EmitA32DataSynchronizationBarrier(A32EmitContext&, IR::Inst*) {
|
||||||
|
code.mfence();
|
||||||
|
}
|
||||||
|
|
||||||
|
void A32EmitX64::EmitA32DataMemoryBarrier(A32EmitContext&, IR::Inst*) {
|
||||||
|
code.lfence();
|
||||||
|
}
|
||||||
|
|
||||||
|
void A32EmitX64::EmitA32InstructionSynchronizationBarrier(A32EmitContext& ctx, IR::Inst*) {
|
||||||
|
ctx.reg_alloc.HostCall(nullptr);
|
||||||
|
|
||||||
|
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(jit_interface));
|
||||||
|
code.CallFunction(static_cast<void(*)(A32::Jit*)>([](A32::Jit* jit) {
|
||||||
|
jit->ClearCache();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
|
void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) {
|
||||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
auto& arg = args[0];
|
auto& arg = args[0];
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
// Barrier instructions
|
||||||
|
INST(arm_DMB, "DMB", "1111010101111111111100000101oooo") // v7
|
||||||
|
INST(arm_DSB, "DSB", "1111010101111111111100000100oooo") // v7
|
||||||
|
INST(arm_ISB, "ISB", "1111010101111111111100000110oooo") // v7
|
||||||
|
|
||||||
// Branch instructions
|
// Branch instructions
|
||||||
INST(arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv") // v5
|
INST(arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv") // v5
|
||||||
INST(arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm") // v5
|
INST(arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm") // v5
|
||||||
|
|
|
@ -76,6 +76,29 @@ public:
|
||||||
return "<internal error>";
|
return "<internal error>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* BarrierOptionStr(Imm4 option) {
|
||||||
|
switch (option) {
|
||||||
|
case 0b0010:
|
||||||
|
return " oshst";
|
||||||
|
case 0b0011:
|
||||||
|
return " osh";
|
||||||
|
case 0b0110:
|
||||||
|
return " nshst";
|
||||||
|
case 0b0111:
|
||||||
|
return " nsh";
|
||||||
|
case 0b1010:
|
||||||
|
return " ishst";
|
||||||
|
case 0b1011:
|
||||||
|
return " ish";
|
||||||
|
case 0b1110:
|
||||||
|
return " st";
|
||||||
|
case 0b1111: // SY can be omitted.
|
||||||
|
return "";
|
||||||
|
default:
|
||||||
|
return " unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::string FPRegStr(bool dp_operation, size_t base, bool bit) {
|
std::string FPRegStr(bool dp_operation, size_t base, bool bit) {
|
||||||
size_t reg_num;
|
size_t reg_num;
|
||||||
if (dp_operation) {
|
if (dp_operation) {
|
||||||
|
@ -100,6 +123,17 @@ public:
|
||||||
return cond == Cond::NV ? "2" : CondToString(cond);
|
return cond == Cond::NV ? "2" : CondToString(cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Barrier instructions
|
||||||
|
std::string arm_DMB(Imm4 option) {
|
||||||
|
return fmt::format("dmb{}", BarrierOptionStr(option));
|
||||||
|
}
|
||||||
|
std::string arm_DSB(Imm4 option) {
|
||||||
|
return fmt::format("dsb{}", BarrierOptionStr(option));
|
||||||
|
}
|
||||||
|
std::string arm_ISB([[maybe_unused]] Imm4 option) {
|
||||||
|
return "isb";
|
||||||
|
}
|
||||||
|
|
||||||
// Branch instructions
|
// Branch instructions
|
||||||
std::string arm_B(Cond cond, Imm24 imm24) {
|
std::string arm_B(Cond cond, Imm24 imm24) {
|
||||||
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8;
|
s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8;
|
||||||
|
|
|
@ -142,6 +142,18 @@ void IREmitter::SetGEFlagsCompressed(const IR::U32& value) {
|
||||||
Inst(Opcode::A32SetGEFlagsCompressed, value);
|
Inst(Opcode::A32SetGEFlagsCompressed, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IREmitter::DataSynchronizationBarrier() {
|
||||||
|
Inst(Opcode::A32DataSynchronizationBarrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IREmitter::DataMemoryBarrier() {
|
||||||
|
Inst(Opcode::A32DataMemoryBarrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IREmitter::InstructionSynchronizationBarrier() {
|
||||||
|
Inst(Opcode::A32InstructionSynchronizationBarrier);
|
||||||
|
}
|
||||||
|
|
||||||
IR::U32 IREmitter::GetFpscr() {
|
IR::U32 IREmitter::GetFpscr() {
|
||||||
return Inst<IR::U32>(Opcode::A32GetFpscr);
|
return Inst<IR::U32>(Opcode::A32GetFpscr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,10 @@ public:
|
||||||
void SetGEFlags(const IR::U32& value);
|
void SetGEFlags(const IR::U32& value);
|
||||||
void SetGEFlagsCompressed(const IR::U32& value);
|
void SetGEFlagsCompressed(const IR::U32& value);
|
||||||
|
|
||||||
|
void DataSynchronizationBarrier();
|
||||||
|
void DataMemoryBarrier();
|
||||||
|
void InstructionSynchronizationBarrier();
|
||||||
|
|
||||||
IR::U32 GetFpscr();
|
IR::U32 GetFpscr();
|
||||||
void SetFpscr(const IR::U32& new_fpscr);
|
void SetFpscr(const IR::U32& new_fpscr);
|
||||||
IR::U32 GetFpscrNZCV();
|
IR::U32 GetFpscrNZCV();
|
||||||
|
|
28
src/frontend/A32/translate/translate_arm/barrier.cpp
Normal file
28
src/frontend/A32/translate/translate_arm/barrier.cpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/* This file is part of the dynarmic project.
|
||||||
|
* Copyright (c) 2019 MerryMage
|
||||||
|
* This software may be used and distributed according to the terms of the GNU
|
||||||
|
* General Public License version 2 or any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "translate_arm.h"
|
||||||
|
|
||||||
|
namespace Dynarmic::A32 {
|
||||||
|
|
||||||
|
bool ArmTranslatorVisitor::arm_DMB([[maybe_unused]] Imm4 option) {
|
||||||
|
ir.DataMemoryBarrier();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArmTranslatorVisitor::arm_DSB([[maybe_unused]] Imm4 option) {
|
||||||
|
ir.DataSynchronizationBarrier();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArmTranslatorVisitor::arm_ISB([[maybe_unused]] Imm4 option) {
|
||||||
|
ir.InstructionSynchronizationBarrier();
|
||||||
|
ir.SetRegister(Reg::PC, ir.Imm32(ir.current_location.PC() + 4));
|
||||||
|
ir.SetTerm(IR::Term::ReturnToDispatch{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dynarmic::A32
|
|
@ -64,6 +64,11 @@ struct ArmTranslatorVisitor final {
|
||||||
template <typename FnT> bool EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg n, ExtReg m, const FnT& fn);
|
template <typename FnT> bool EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg n, ExtReg m, const FnT& fn);
|
||||||
template <typename FnT> bool EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg m, const FnT& fn);
|
template <typename FnT> bool EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg m, const FnT& fn);
|
||||||
|
|
||||||
|
// Barrier instructions
|
||||||
|
bool arm_DMB(Imm4 option);
|
||||||
|
bool arm_DSB(Imm4 option);
|
||||||
|
bool arm_ISB(Imm4 option);
|
||||||
|
|
||||||
// Branch instructions
|
// Branch instructions
|
||||||
bool arm_B(Cond cond, Imm24 imm24);
|
bool arm_B(Cond cond, Imm24 imm24);
|
||||||
bool arm_BL(Cond cond, Imm24 imm24);
|
bool arm_BL(Cond cond, Imm24 imm24);
|
||||||
|
|
|
@ -45,6 +45,21 @@ bool Inst::IsShift() const {
|
||||||
IsLogicalShift();
|
IsLogicalShift();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Inst::IsBarrier() const {
|
||||||
|
switch (op) {
|
||||||
|
case Opcode::A32DataMemoryBarrier:
|
||||||
|
case Opcode::A32DataSynchronizationBarrier:
|
||||||
|
case Opcode::A32InstructionSynchronizationBarrier:
|
||||||
|
case Opcode::A64DataMemoryBarrier:
|
||||||
|
case Opcode::A64DataSynchronizationBarrier:
|
||||||
|
case Opcode::A64InstructionSynchronizationBarrier:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Inst::IsSharedMemoryRead() const {
|
bool Inst::IsSharedMemoryRead() const {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Opcode::A32ReadMemory8:
|
case Opcode::A32ReadMemory8:
|
||||||
|
@ -463,20 +478,18 @@ bool Inst::IsCoprocessorInstruction() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Inst::MayHaveSideEffects() const {
|
bool Inst::MayHaveSideEffects() const {
|
||||||
return op == Opcode::PushRSB ||
|
return op == Opcode::PushRSB ||
|
||||||
op == Opcode::A64SetCheckBit ||
|
op == Opcode::A64SetCheckBit ||
|
||||||
op == Opcode::A64DataCacheOperationRaised ||
|
op == Opcode::A64DataCacheOperationRaised ||
|
||||||
op == Opcode::A64DataSynchronizationBarrier ||
|
IsBarrier() ||
|
||||||
op == Opcode::A64DataMemoryBarrier ||
|
CausesCPUException() ||
|
||||||
op == Opcode::A64InstructionSynchronizationBarrier ||
|
WritesToCoreRegister() ||
|
||||||
CausesCPUException() ||
|
WritesToSystemRegister() ||
|
||||||
WritesToCoreRegister() ||
|
WritesToCPSR() ||
|
||||||
WritesToSystemRegister() ||
|
WritesToFPCR() ||
|
||||||
WritesToCPSR() ||
|
WritesToFPSR() ||
|
||||||
WritesToFPCR() ||
|
AltersExclusiveState() ||
|
||||||
WritesToFPSR() ||
|
IsMemoryWrite() ||
|
||||||
AltersExclusiveState() ||
|
|
||||||
IsMemoryWrite() ||
|
|
||||||
IsCoprocessorInstruction();
|
IsCoprocessorInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,9 @@ public:
|
||||||
/// Determines whether or not this instruction performs any kind of shift.
|
/// Determines whether or not this instruction performs any kind of shift.
|
||||||
bool IsShift() const;
|
bool IsShift() const;
|
||||||
|
|
||||||
|
/// Determines whether or not this instruction is a form of barrier.
|
||||||
|
bool IsBarrier() const;
|
||||||
|
|
||||||
/// Determines whether or not this instruction performs a shared memory read.
|
/// Determines whether or not this instruction performs a shared memory read.
|
||||||
bool IsSharedMemoryRead() const;
|
bool IsSharedMemoryRead() const;
|
||||||
/// Determines whether or not this instruction performs a shared memory write.
|
/// Determines whether or not this instruction performs a shared memory write.
|
||||||
|
|
|
@ -30,6 +30,9 @@ A32OPC(SetGEFlagsCompressed, Void, U32
|
||||||
A32OPC(BXWritePC, Void, U32 )
|
A32OPC(BXWritePC, Void, U32 )
|
||||||
A32OPC(CallSupervisor, Void, U32 )
|
A32OPC(CallSupervisor, Void, U32 )
|
||||||
A32OPC(ExceptionRaised, Void, U32, U64 )
|
A32OPC(ExceptionRaised, Void, U32, U64 )
|
||||||
|
A32OPC(DataSynchronizationBarrier, Void, )
|
||||||
|
A32OPC(DataMemoryBarrier, Void, )
|
||||||
|
A32OPC(InstructionSynchronizationBarrier, Void, )
|
||||||
A32OPC(GetFpscr, U32, )
|
A32OPC(GetFpscr, U32, )
|
||||||
A32OPC(SetFpscr, Void, U32, )
|
A32OPC(SetFpscr, Void, U32, )
|
||||||
A32OPC(GetFpscrNZCV, U32, )
|
A32OPC(GetFpscrNZCV, U32, )
|
||||||
|
|
Loading…
Reference in a new issue