From 4428aea5eeba671883b5a0d7756c6e65f1590743 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 28 Mar 2026 17:53:33 -0700 Subject: [PATCH] IR: Adds support for printing strings I utilize this functionality quite heavily when debugging and I need bread crumbs spread around. Instead of reimplementing it a dozen times, just have it upstreamed. --- FEXCore/Source/Interface/Core/JIT/JIT.cpp | 6 ++++++ FEXCore/Source/Interface/Core/JIT/MiscOps.cpp | 19 +++++++++++++++++++ FEXCore/Source/Interface/IR/IR.json | 7 +++++++ FEXCore/Source/Interface/IR/IRDumper.cpp | 4 ++++ FEXCore/include/FEXCore/Core/CoreState.h | 1 + 5 files changed, 37 insertions(+) diff --git a/FEXCore/Source/Interface/Core/JIT/JIT.cpp b/FEXCore/Source/Interface/Core/JIT/JIT.cpp index 9ae6d8bde..dd9c27e29 100644 --- a/FEXCore/Source/Interface/Core/JIT/JIT.cpp +++ b/FEXCore/Source/Interface/Core/JIT/JIT.cpp @@ -68,6 +68,10 @@ PrintValue(uint64_t Value) { LogMan::Msg::DFmt("Value: 0x{:x}", Value); } +static void PrintMsg(const char* Value) { + LogMan::Msg::DFmt("{}", Value); +} + static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) { LogMan::Msg::DFmt("Value: 0x{:016x}'{:016x}", ValueUpper, Value); } @@ -636,6 +640,8 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::In Ptrs.PrintValue = reinterpret_cast(PrintValue); Ptrs.PrintVectorValue = reinterpret_cast(PrintVectorValue); + Ptrs.PrintMsgValue = reinterpret_cast(PrintMsg); + Ptrs.ThreadRemoveCodeEntryFromJIT = reinterpret_cast(&Context::ContextImpl::ThreadRemoveCodeEntryFromJit); Ptrs.MonoBackpatcherWrite = reinterpret_cast(&Context::ContextImpl::MonoBackpatcherWrite); Ptrs.CPUIDObj = reinterpret_cast(&CTX->CPUID); diff --git a/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp b/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp index 4eed49b71..ff8e916e7 100644 --- a/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/MiscOps.cpp @@ -210,6 +210,25 @@ DEF_OP(Print) { PopDynamicRegs(); } +DEF_OP(PrintMsg) { + auto Op = IROp->C(); + + PushDynamicRegs(TMP1); + SpillStaticRegs(TMP1); + + LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, reinterpret_cast(Op->Value)); + ldr(ARMEmitter::XReg::x1, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.PrintMsgValue)); + + if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] { + GenerateIndirectRuntimeCall(ARMEmitter::Reg::r1); + } else { + blr(ARMEmitter::Reg::r1); + } + + FillStaticRegs(); + PopDynamicRegs(); +} + DEF_OP(ProcessorID) { if (CTX->HostFeatures.SupportsCPUIndexInTPIDRRO) { mrs(GetReg(Node), ARMEmitter::SystemRegister::TPIDRRO_EL0); diff --git a/FEXCore/Source/Interface/IR/IR.json b/FEXCore/Source/Interface/IR/IR.json index 01e3e16b6..17c966842 100644 --- a/FEXCore/Source/Interface/IR/IR.json +++ b/FEXCore/Source/Interface/IR/IR.json @@ -136,6 +136,7 @@ "u16": "uint16_t", "u32": "uint32_t", "u64": "uint64_t", + "c_str": "const char*", "OpSize": "FEXCore::IR::OpSize", "SSA": "OrderedNode*", "GPR": "OrderedNode*", @@ -240,6 +241,12 @@ "Desc": ["Debug operation that prints an SSA value to the console", "May only print 64bits of the value"] }, + "PrintMsg c_str:$Value": { + "HasSideEffects": true, + "Desc": ["Debug operation that prints an string to the console.", + "This is for debug only! Will break code caching!" + ] + }, "GPR = AllocateGPR i1:$ForPair": { "Desc": ["Silly pseudo-instruction to allocate a register for a future destination", "Note: if an instruction uses allocated destinations-as-sources,", diff --git a/FEXCore/Source/Interface/IR/IRDumper.cpp b/FEXCore/Source/Interface/IR/IRDumper.cpp index 335fa830a..11daa1929 100644 --- a/FEXCore/Source/Interface/IR/IRDumper.cpp +++ b/FEXCore/Source/Interface/IR/IRDumper.cpp @@ -38,6 +38,10 @@ static void PrintArg(fextl::stringstream* out, const IRListView*, uint64_t Arg) *out << fextl::fmt::format("#{:#x}", Arg); } +static void PrintArg(fextl::stringstream* out, const IRListView*, const char* const Arg) { + *out << fextl::fmt::format("'{}'", Arg); +} + static void PrintArg(fextl::stringstream* out, const IRListView*, CondClass Arg) { if (Arg == CondClass::AL) { *out << "ALWAYS"; diff --git a/FEXCore/include/FEXCore/Core/CoreState.h b/FEXCore/include/FEXCore/Core/CoreState.h index e4bd6353e..aa53af9c0 100644 --- a/FEXCore/include/FEXCore/Core/CoreState.h +++ b/FEXCore/include/FEXCore/Core/CoreState.h @@ -337,6 +337,7 @@ struct JITPointers { // Process specific uint64_t PrintValue {}; uint64_t PrintVectorValue {}; + uint64_t PrintMsgValue {}; uint64_t ThreadRemoveCodeEntryFromJIT {}; uint64_t CPUIDObj {}; uint64_t CPUIDFunction {};