diff --git a/include/dynarmic/A64/config.h b/include/dynarmic/A64/config.h index 6a029d97..9846f70a 100644 --- a/include/dynarmic/A64/config.h +++ b/include/dynarmic/A64/config.h @@ -68,6 +68,15 @@ enum class DataCacheOperation { ZeroByVA, }; +enum class InstructionCacheOperation { + /// IC IVAU + InvalidateByVAToPoU, + /// IC IALLU + InvalidateAllToPoU, + /// IC IALLUIS + InvalidateAllToPoUInnerSharable +}; + struct UserCallbacks { virtual ~UserCallbacks() = default; @@ -110,6 +119,7 @@ struct UserCallbacks { virtual void ExceptionRaised(VAddr pc, Exception exception) = 0; virtual void DataCacheOperationRaised(DataCacheOperation /*op*/, VAddr /*value*/) {} + virtual void InstructionCacheOperationRaised(InstructionCacheOperation /*op*/, VAddr /*value*/) {} virtual void InstructionSynchronizationBarrierRaised() {} // Timing-related callbacks diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a12f2e1..c9983f5a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -234,6 +234,7 @@ if ("A64" IN_LIST DYNARMIC_FRONTENDS) frontend/A64/translate/impl/simd_two_register_misc.cpp frontend/A64/translate/impl/simd_vector_x_indexed_element.cpp frontend/A64/translate/impl/sys_dc.cpp + frontend/A64/translate/impl/sys_ic.cpp frontend/A64/translate/impl/system.cpp frontend/A64/translate/impl/system_flag_format.cpp frontend/A64/translate/impl/system_flag_manipulation.cpp diff --git a/src/backend/x64/a64_emit_x64.cpp b/src/backend/x64/a64_emit_x64.cpp index c89f4e91..20514599 100644 --- a/src/backend/x64/a64_emit_x64.cpp +++ b/src/backend/x64/a64_emit_x64.cpp @@ -647,10 +647,16 @@ void A64EmitX64::EmitA64ExceptionRaised(A64EmitContext& ctx, IR::Inst* inst) { void A64EmitX64::EmitA64DataCacheOperationRaised(A64EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst); - ctx.reg_alloc.HostCall(nullptr, args[0], args[1]); + ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]); Devirtualize<&A64::UserCallbacks::DataCacheOperationRaised>(conf.callbacks).EmitCall(code); } +void A64EmitX64::EmitA64InstructionCacheOperationRaised(A64EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + ctx.reg_alloc.HostCall(nullptr, {}, args[0], args[1]); + Devirtualize<&A64::UserCallbacks::InstructionCacheOperationRaised>(conf.callbacks).EmitCall(code); +} + void A64EmitX64::EmitA64DataSynchronizationBarrier(A64EmitContext&, IR::Inst*) { code.mfence(); } diff --git a/src/frontend/A64/decoder/a64.inc b/src/frontend/A64/decoder/a64.inc index 7d68ccec..b9d83af6 100644 --- a/src/frontend/A64/decoder/a64.inc +++ b/src/frontend/A64/decoder/a64.inc @@ -108,6 +108,11 @@ INST(DC_CVAU, "DC CVAU", "11010 INST(DC_CVAP, "DC CVAP", "110101010000101101111100001ttttt") INST(DC_CIVAC, "DC CIVAC", "110101010000101101111110001ttttt") +// SYS: Instruction Cache +INST(IC_IALLU, "IC IALLU", "11010101000010000111010100011111") +INST(IC_IALLUIS, "IC IALLUIS", "11010101000010000111000100011111") +INST(IC_IVAU, "IC IVAU", "110101010000101101110101001ttttt") + // Unconditional branch (Register) INST(BLR, "BLR", "1101011000111111000000nnnnn00000") INST(BR, "BR", "1101011000011111000000nnnnn00000") diff --git a/src/frontend/A64/ir_emitter.cpp b/src/frontend/A64/ir_emitter.cpp index cfd0acaa..a8506b64 100644 --- a/src/frontend/A64/ir_emitter.cpp +++ b/src/frontend/A64/ir_emitter.cpp @@ -56,6 +56,10 @@ void IREmitter::DataCacheOperationRaised(DataCacheOperation op, const IR::U64& v Inst(Opcode::A64DataCacheOperationRaised, Imm64(static_cast(op)), value); } +void IREmitter::InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value) { + Inst(Opcode::A64InstructionCacheOperationRaised, Imm64(static_cast(op)), value); +} + void IREmitter::DataSynchronizationBarrier() { Inst(Opcode::A64DataSynchronizationBarrier); } diff --git a/src/frontend/A64/ir_emitter.h b/src/frontend/A64/ir_emitter.h index 8d463b7b..b6fcf523 100644 --- a/src/frontend/A64/ir_emitter.h +++ b/src/frontend/A64/ir_emitter.h @@ -42,6 +42,7 @@ public: void CallSupervisor(u32 imm); void ExceptionRaised(Exception exception); void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value); + void InstructionCacheOperationRaised(InstructionCacheOperation op, const IR::U64& value); void DataSynchronizationBarrier(); void DataMemoryBarrier(); void InstructionSynchronizationBarrier(); diff --git a/src/frontend/A64/translate/impl/impl.h b/src/frontend/A64/translate/impl/impl.h index d6ed6a59..e7f22136 100644 --- a/src/frontend/A64/translate/impl/impl.h +++ b/src/frontend/A64/translate/impl/impl.h @@ -174,6 +174,11 @@ struct TranslatorVisitor final { bool DC_CVAP(Reg Rt); bool DC_CIVAC(Reg Rt); + // SYS: Instruction Cache + bool IC_IALLU(); + bool IC_IALLUIS(); + bool IC_IVAU(Reg Rt); + // Unconditional branch (Register) bool BR(Reg Rn); bool BRA(bool Z, bool M, Reg Rn, Reg Rm); diff --git a/src/frontend/A64/translate/impl/sys_ic.cpp b/src/frontend/A64/translate/impl/sys_ic.cpp new file mode 100644 index 00000000..de54121e --- /dev/null +++ b/src/frontend/A64/translate/impl/sys_ic.cpp @@ -0,0 +1,25 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2018 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include "frontend/A64/translate/impl/impl.h" + +namespace Dynarmic::A64 { + +bool TranslatorVisitor::IC_IALLU() { + ir.InstructionCacheOperationRaised(InstructionCacheOperation::InvalidateAllToPoU, ir.Imm64(0)); + return true; +} + +bool TranslatorVisitor::IC_IALLUIS() { + ir.InstructionCacheOperationRaised(InstructionCacheOperation::InvalidateAllToPoUInnerSharable, ir.Imm64(0)); + return true; +} + +bool TranslatorVisitor::IC_IVAU(Reg Rt) { + ir.InstructionCacheOperationRaised(InstructionCacheOperation::InvalidateByVAToPoU, X(64, Rt)); + return true; +} + +} // namespace Dynarmic::A64 diff --git a/src/frontend/ir/microinstruction.cpp b/src/frontend/ir/microinstruction.cpp index 3658aa74..f1f381df 100644 --- a/src/frontend/ir/microinstruction.cpp +++ b/src/frontend/ir/microinstruction.cpp @@ -520,18 +520,19 @@ bool Inst::IsSetCheckBitOperation() const { } bool Inst::MayHaveSideEffects() const { - return op == Opcode::PushRSB || - op == Opcode::A64DataCacheOperationRaised || - IsSetCheckBitOperation() || - IsBarrier() || - CausesCPUException() || - WritesToCoreRegister() || - WritesToSystemRegister() || - WritesToCPSR() || - WritesToFPCR() || - WritesToFPSR() || - AltersExclusiveState() || - IsMemoryWrite() || + return op == Opcode::PushRSB || + op == Opcode::A64DataCacheOperationRaised || + op == Opcode::A64InstructionCacheOperationRaised || + IsSetCheckBitOperation() || + IsBarrier() || + CausesCPUException() || + WritesToCoreRegister() || + WritesToSystemRegister() || + WritesToCPSR() || + WritesToFPCR() || + WritesToFPSR() || + AltersExclusiveState() || + IsMemoryWrite() || IsCoprocessorInstruction(); } diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index d68a5f5c..d8a0fd6e 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -69,6 +69,7 @@ A64OPC(SetPC, Void, U64 A64OPC(CallSupervisor, Void, U32 ) A64OPC(ExceptionRaised, Void, U64, U64 ) A64OPC(DataCacheOperationRaised, Void, U64, U64 ) +A64OPC(InstructionCacheOperationRaised, Void, U64, U64 ) A64OPC(DataSynchronizationBarrier, Void, ) A64OPC(DataMemoryBarrier, Void, ) A64OPC(InstructionSynchronizationBarrier, Void, ) diff --git a/tests/A64/a64.cpp b/tests/A64/a64.cpp index 1cdb7628..524be36e 100644 --- a/tests/A64/a64.cpp +++ b/tests/A64/a64.cpp @@ -634,3 +634,31 @@ TEST_CASE("A64: Optimization failure when folding ADD", "[a64]") { REQUIRE(jit.GetPstate() == 0x20000000); REQUIRE(jit.GetVector(30) == Vector{0xf7f6f5f4, 0}); } + +TEST_CASE("A64: Cache Maintenance Instructions", "[a64]") { + class CacheMaintenanceTestEnv final : public A64TestEnv { + void InstructionCacheOperationRaised(A64::InstructionCacheOperation op, VAddr value) override { + REQUIRE(op == A64::InstructionCacheOperation::InvalidateByVAToPoU); + REQUIRE(value == 0xcafed00d); + } + void DataCacheOperationRaised(A64::DataCacheOperation op, VAddr value) override { + REQUIRE(op == A64::DataCacheOperation::InvalidateByVAToPoC); + REQUIRE(value == 0xcafebabe); + } + }; + + CacheMaintenanceTestEnv env; + A64::UserConfig conf{&env}; + conf.hook_data_cache_operations = true; + A64::Jit jit{conf}; + + jit.SetRegister(0, 0xcafed00d); + jit.SetRegister(1, 0xcafebabe); + + env.code_mem.emplace_back(0xd50b7520); // ic ivau, x0 + env.code_mem.emplace_back(0xd5087621); // dc ivac, x1 + env.code_mem.emplace_back(0x14000000); // B . + + env.ticks_left = 3; + jit.Run(); +} diff --git a/tests/A64/testenv.h b/tests/A64/testenv.h index 6f416888..0eaf68ed 100644 --- a/tests/A64/testenv.h +++ b/tests/A64/testenv.h @@ -15,7 +15,7 @@ using Vector = Dynarmic::A64::Vector; -class A64TestEnv final : public Dynarmic::A64::UserCallbacks { +class A64TestEnv : public Dynarmic::A64::UserCallbacks { public: u64 ticks_left = 0;