diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3948ca17..ab6bf6b7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -95,6 +95,7 @@ add_library(dynarmic frontend/A32/translate/translate.cpp frontend/A32/translate/translate.h 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/coprocessor.cpp frontend/A32/translate/translate_arm/data_processing.cpp diff --git a/src/backend/x64/a32_emit_x64.cpp b/src/backend/x64/a32_emit_x64.cpp index 5517c2cb..1922398d 100644 --- a/src/backend/x64/a32_emit_x64.cpp +++ b/src/backend/x64/a32_emit_x64.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(jit_interface)); + code.CallFunction(static_cast([](A32::Jit* jit) { + jit->ClearCache(); + })); +} + void A32EmitX64::EmitA32BXWritePC(A32EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto& arg = args[0]; diff --git a/src/frontend/A32/decoder/arm.inc b/src/frontend/A32/decoder/arm.inc index 8aa57043..ea3a96b5 100644 --- a/src/frontend/A32/decoder/arm.inc +++ b/src/frontend/A32/decoder/arm.inc @@ -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 INST(arm_BLX_imm, "BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv") // v5 INST(arm_BLX_reg, "BLX (reg)", "cccc000100101111111111110011mmmm") // v5 diff --git a/src/frontend/A32/disassembler/disassembler_arm.cpp b/src/frontend/A32/disassembler/disassembler_arm.cpp index 598c9ae0..a87a0825 100644 --- a/src/frontend/A32/disassembler/disassembler_arm.cpp +++ b/src/frontend/A32/disassembler/disassembler_arm.cpp @@ -76,6 +76,29 @@ public: return ""; } + 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) { size_t reg_num; if (dp_operation) { @@ -100,6 +123,17 @@ public: 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 std::string arm_B(Cond cond, Imm24 imm24) { s32 offset = Common::SignExtend<26, s32>(imm24 << 2) + 8; diff --git a/src/frontend/A32/ir_emitter.cpp b/src/frontend/A32/ir_emitter.cpp index 24566532..f20b0c78 100644 --- a/src/frontend/A32/ir_emitter.cpp +++ b/src/frontend/A32/ir_emitter.cpp @@ -142,6 +142,18 @@ void IREmitter::SetGEFlagsCompressed(const IR::U32& 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() { return Inst(Opcode::A32GetFpscr); } diff --git a/src/frontend/A32/ir_emitter.h b/src/frontend/A32/ir_emitter.h index ef9d958f..3b4c9067 100644 --- a/src/frontend/A32/ir_emitter.h +++ b/src/frontend/A32/ir_emitter.h @@ -61,6 +61,10 @@ public: void SetGEFlags(const IR::U32& value); void SetGEFlagsCompressed(const IR::U32& value); + void DataSynchronizationBarrier(); + void DataMemoryBarrier(); + void InstructionSynchronizationBarrier(); + IR::U32 GetFpscr(); void SetFpscr(const IR::U32& new_fpscr); IR::U32 GetFpscrNZCV(); diff --git a/src/frontend/A32/translate/translate_arm/barrier.cpp b/src/frontend/A32/translate/translate_arm/barrier.cpp new file mode 100644 index 00000000..2f4a53a4 --- /dev/null +++ b/src/frontend/A32/translate/translate_arm/barrier.cpp @@ -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 diff --git a/src/frontend/A32/translate/translate_arm/translate_arm.h b/src/frontend/A32/translate/translate_arm/translate_arm.h index dd8f3cb2..9b798ed0 100644 --- a/src/frontend/A32/translate/translate_arm/translate_arm.h +++ b/src/frontend/A32/translate/translate_arm/translate_arm.h @@ -64,6 +64,11 @@ struct ArmTranslatorVisitor final { template bool EmitVfpVectorOperation(bool sz, ExtReg d, ExtReg n, ExtReg m, const FnT& fn); template 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 bool arm_B(Cond cond, Imm24 imm24); bool arm_BL(Cond cond, Imm24 imm24); diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index 52fc24e1..db1368b6 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -45,6 +45,21 @@ bool Inst::IsShift() const { 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 { switch (op) { case Opcode::A32ReadMemory8: @@ -463,20 +478,18 @@ bool Inst::IsCoprocessorInstruction() const { } bool Inst::MayHaveSideEffects() const { - return op == Opcode::PushRSB || - op == Opcode::A64SetCheckBit || - op == Opcode::A64DataCacheOperationRaised || - op == Opcode::A64DataSynchronizationBarrier || - op == Opcode::A64DataMemoryBarrier || - op == Opcode::A64InstructionSynchronizationBarrier || - CausesCPUException() || - WritesToCoreRegister() || - WritesToSystemRegister() || - WritesToCPSR() || - WritesToFPCR() || - WritesToFPSR() || - AltersExclusiveState() || - IsMemoryWrite() || + return op == Opcode::PushRSB || + op == Opcode::A64SetCheckBit || + op == Opcode::A64DataCacheOperationRaised || + IsBarrier() || + CausesCPUException() || + WritesToCoreRegister() || + WritesToSystemRegister() || + WritesToCPSR() || + WritesToFPCR() || + WritesToFPSR() || + AltersExclusiveState() || + IsMemoryWrite() || IsCoprocessorInstruction(); } diff --git a/src/frontend/ir/microinstruction.h b/src/frontend/ir/microinstruction.h index 089884a2..f1650f98 100644 --- a/src/frontend/ir/microinstruction.h +++ b/src/frontend/ir/microinstruction.h @@ -36,6 +36,9 @@ public: /// Determines whether or not this instruction performs any kind of shift. 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. bool IsSharedMemoryRead() const; /// Determines whether or not this instruction performs a shared memory write. diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index 814df3da..cfbf0015 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -30,6 +30,9 @@ A32OPC(SetGEFlagsCompressed, Void, U32 A32OPC(BXWritePC, Void, U32 ) A32OPC(CallSupervisor, Void, U32 ) A32OPC(ExceptionRaised, Void, U32, U64 ) +A32OPC(DataSynchronizationBarrier, Void, ) +A32OPC(DataMemoryBarrier, Void, ) +A32OPC(InstructionSynchronizationBarrier, Void, ) A32OPC(GetFpscr, U32, ) A32OPC(SetFpscr, Void, U32, ) A32OPC(GetFpscrNZCV, U32, )