diff --git a/Utilities/JIT.cpp b/Utilities/JIT.cpp index 079b4dc0c4..a58c29a8bb 100644 --- a/Utilities/JIT.cpp +++ b/Utilities/JIT.cpp @@ -268,6 +268,44 @@ asmjit::Runtime& asmjit::get_global_runtime() return g_rt; } +asmjit::Error asmjit::inline_runtime::_add(void** dst, asmjit::CodeHolder* code) noexcept +{ + usz codeSize = code->getCodeSize(); + if (!codeSize) [[unlikely]] + { + *dst = nullptr; + return asmjit::kErrorNoCodeGenerated; + } + + if (utils::align(codeSize, 4096) > m_size) [[unlikely]] + { + *dst = nullptr; + return asmjit::kErrorNoVirtualMemory; + } + + usz relocSize = code->relocate(m_data); + if (!relocSize) [[unlikely]] + { + *dst = nullptr; + return asmjit::kErrorInvalidState; + } + + flush(m_data, relocSize); + *dst = m_data; + + return asmjit::kErrorOk; +} + +asmjit::Error asmjit::inline_runtime::_release(void*) noexcept +{ + return asmjit::kErrorOk; +} + +asmjit::inline_runtime::~inline_runtime() +{ + utils::memory_protect(m_data, m_size, utils::protection::rx); +} + #ifdef LLVM_AVAILABLE #include diff --git a/Utilities/JIT.h b/Utilities/JIT.h index 53e9c08edd..e90b039a67 100644 --- a/Utilities/JIT.h +++ b/Utilities/JIT.h @@ -63,6 +63,30 @@ namespace asmjit // Should only be used to build global functions asmjit::Runtime& get_global_runtime(); + // Don't use directly + class inline_runtime : public HostRuntime + { + uchar* m_data; + usz m_size; + + public: + inline_runtime(const inline_runtime&) = delete; + + inline_runtime& operator=(const inline_runtime&) = delete; + + inline_runtime(uchar* data, usz size) + : m_data(data) + , m_size(size) + { + } + + asmjit::Error _add(void** dst, asmjit::CodeHolder* code) noexcept override; + + asmjit::Error _release(void*) noexcept override; + + ~inline_runtime(); + }; + // Emit xbegin and adjacent loop, return label at xbegin (don't use xabort please) template [[nodiscard]] inline asmjit::Label build_transaction_enter(asmjit::X86Assembler& c, asmjit::Label fallback, F func) @@ -168,6 +192,63 @@ inline FT build_function_asm(F&& builder) return result; } +template +class built_function +{ + alignas(4096) uchar m_data[Size]; + +public: + built_function(const built_function&) = delete; + + built_function& operator=(const built_function&) = delete; + + template + built_function(F&& builder) + { + using namespace asmjit; + + inline_runtime rt(m_data, Size); + + CodeHolder code; + code.init(rt.getCodeInfo()); + code._globalHints = asmjit::CodeEmitter::kHintOptimizedAlign; + + std::array args; + #ifdef _WIN32 + args[0] = x86::rcx; + args[1] = x86::rdx; + args[2] = x86::r8; + args[3] = x86::r9; + #else + args[0] = x86::rdi; + args[1] = x86::rsi; + args[2] = x86::rdx; + args[3] = x86::rcx; + #endif + + X86Assembler compiler(&code); + builder(std::ref(compiler), args); + + FT result; + + if (compiler.getLastError() || rt.add(&result, &code)) + { + ensure(false); + } + } + + operator FT() const noexcept + { + return FT(+m_data); + } + + template + auto operator()(Args&&... args) const noexcept + { + return FT(+m_data)(std::forward(args)...); + } +}; + #ifdef LLVM_AVAILABLE #include diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index d143b86491..b46b21afc0 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -147,7 +147,7 @@ static bool ppu_break(ppu_thread& ppu, ppu_opcode_t op); extern void do_cell_atomic_128_store(u32 addr, const void* to_write); -const auto ppu_gateway = build_function_asm([](asmjit::X86Assembler& c, auto& args) +const auto ppu_gateway = built_function([](asmjit::X86Assembler& c, auto& args) { // Gateway for PPU, converts from native to GHC calling convention, also saves RSP value for escape using namespace asmjit; @@ -1816,7 +1816,7 @@ extern u64 ppu_ldarx(ppu_thread& ppu, u32 addr) return ppu_load_acquire_reservation(ppu, addr); } -const auto ppu_stcx_accurate_tx = build_function_asm([](asmjit::X86Assembler& c, auto& args) +const auto ppu_stcx_accurate_tx = built_function([](asmjit::X86Assembler& c, auto& args) { using namespace asmjit; diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index 5e6a0041c1..550a8103b2 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -160,7 +160,7 @@ DECLARE(spu_runtime::tr_all) = [] return reinterpret_cast(trptr); }(); -DECLARE(spu_runtime::g_gateway) = build_function_asm([](asmjit::X86Assembler& c, auto& args) +DECLARE(spu_runtime::g_gateway) = built_function([](asmjit::X86Assembler& c, auto& args) { // Gateway for SPU dispatcher, converts from native to GHC calling convention, also saves RSP value for spu_escape using namespace asmjit; diff --git a/rpcs3/Emu/Cell/SPURecompiler.h b/rpcs3/Emu/Cell/SPURecompiler.h index 2bec70cb42..031e4d666c 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.h +++ b/rpcs3/Emu/Cell/SPURecompiler.h @@ -133,7 +133,7 @@ public: static std::array, (1 << 20)>* const g_dispatcher; // Recompiler entry point - static const spu_function_t g_gateway; + static const built_function g_gateway; // Longjmp to the end of the gateway function (native CC) static void(*const g_escape)(spu_thread*); diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 19e57d6f0d..360682ccbc 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -405,7 +405,7 @@ std::array op_branch_targets(u32 pc, spu_opcode_t op) return res; } -const auto spu_putllc_tx = build_function_asm([](asmjit::X86Assembler& c, auto& args) +const auto spu_putllc_tx = built_function([](asmjit::X86Assembler& c, auto& args) { using namespace asmjit; @@ -667,7 +667,7 @@ const auto spu_putllc_tx = build_function_asm([](asmjit::X86Assembler& c, auto& args) +const auto spu_putlluc_tx = built_function([](asmjit::X86Assembler& c, auto& args) { using namespace asmjit; @@ -801,7 +801,7 @@ const auto spu_putlluc_tx = build_function_asm([](asmjit::X86Assembler& c, auto& args) +const auto spu_getllar_tx = built_function([](asmjit::X86Assembler& c, auto& args) { using namespace asmjit;