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:
Lioncash 2019-04-26 16:41:41 -04:00 committed by MerryMage
parent 07218df353
commit 8316d231e9
11 changed files with 139 additions and 14 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

@ -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:
@ -466,9 +481,7 @@ 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 ||
op == Opcode::A64InstructionSynchronizationBarrier ||
CausesCPUException() || CausesCPUException() ||
WritesToCoreRegister() || WritesToCoreRegister() ||
WritesToSystemRegister() || WritesToSystemRegister() ||

View file

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

View file

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