diff options
| author | JMC47 <JMC4789@gmail.com> | 2024-10-21 12:03:50 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-21 12:03:50 -0400 |
| commit | f412e2488c24fc81a784c198ba9610e54cdd1d9a (patch) | |
| tree | 2433844ec8e6192ed6051edf5460259c4a2cb0c1 /Source/Core/Common/HostDisassembler.cpp | |
| parent | 24e9fc120c1b02b78cbf854c0eae78df0dbb60c1 (diff) | |
| parent | c3bda2e8757b4c37bec504f05ebda39b858e2969 (diff) | |
Merge pull request #12714 from mitaclaw/jit-widget-refresh
DolphinQt: JIT Widget Refresh
Diffstat (limited to 'Source/Core/Common/HostDisassembler.cpp')
| -rw-r--r-- | Source/Core/Common/HostDisassembler.cpp | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/Source/Core/Common/HostDisassembler.cpp b/Source/Core/Common/HostDisassembler.cpp new file mode 100644 index 0000000000..14dd3b5b46 --- /dev/null +++ b/Source/Core/Common/HostDisassembler.cpp @@ -0,0 +1,158 @@ +// Copyright 2008 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Common/HostDisassembler.h" + +#include <span> + +#include <fmt/format.h> +#include <fmt/ostream.h> +#include <fmt/ranges.h> + +#if defined(HAVE_LLVM) +#include <llvm-c/Disassembler.h> +#include <llvm-c/Target.h> +#elif defined(_M_X86_64) +#include <disasm.h> // Bochs +#endif + +#if defined(HAVE_LLVM) +class HostDisassemblerLLVM final : public HostDisassembler +{ +public: + explicit HostDisassemblerLLVM(const char* host_disasm, const char* cpu = "", + std::size_t inst_size = 0); + ~HostDisassemblerLLVM(); + +private: + LLVMDisasmContextRef m_llvm_context; + std::size_t m_instruction_size; + + std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream) override; +}; + +HostDisassemblerLLVM::HostDisassemblerLLVM(const char* host_disasm, const char* cpu, + std::size_t inst_size) + : m_instruction_size(inst_size) +{ + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllDisassemblers(); + + m_llvm_context = LLVMCreateDisasmCPU(host_disasm, cpu, nullptr, 0, nullptr, nullptr); + + // Couldn't create llvm context + if (!m_llvm_context) + return; + + LLVMSetDisasmOptions(m_llvm_context, LLVMDisassembler_Option_AsmPrinterVariant | + LLVMDisassembler_Option_PrintLatency); +} + +HostDisassemblerLLVM::~HostDisassemblerLLVM() +{ + if (m_llvm_context) + LLVMDisasmDispose(m_llvm_context); +} + +std::size_t HostDisassemblerLLVM::Disassemble(const u8* begin, const u8* end, std::ostream& stream) +{ + std::size_t instruction_count = 0; + if (!m_llvm_context) + return instruction_count; + + while (begin < end) + { + char inst_disasm[256]; + + const auto inst_size = + LLVMDisasmInstruction(m_llvm_context, const_cast<u8*>(begin), static_cast<u64>(end - begin), + reinterpret_cast<u64>(begin), inst_disasm, sizeof(inst_disasm)); + if (inst_size == 0) + { + if (m_instruction_size != 0) + { + // If we are on an architecture that has a fixed instruction + // size, we can continue onward past this bad instruction. + fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin), + fmt::join(std::span{begin, m_instruction_size}, "")); + begin += m_instruction_size; + } + else + { + // We can't continue if we are on an architecture that has flexible + // instruction sizes. Dump the rest of the block instead. + fmt::println(stream, "{}\tInvalid inst: {:02x}", fmt::ptr(begin), + fmt::join(std::span{begin, end}, "")); + break; + } + } + else + { + fmt::println(stream, "{}{}", fmt::ptr(begin), inst_disasm); + begin += inst_size; + } + ++instruction_count; + } + return instruction_count; +} +#elif defined(_M_X86_64) +class HostDisassemblerBochs final : public HostDisassembler +{ +public: + explicit HostDisassemblerBochs(); + ~HostDisassemblerBochs() = default; + +private: + disassembler m_disasm; + + std::size_t Disassemble(const u8* begin, const u8* end, std::ostream& stream) override; +}; + +HostDisassemblerBochs::HostDisassemblerBochs() +{ + m_disasm.set_syntax_intel(); +} + +std::size_t HostDisassemblerBochs::Disassemble(const u8* begin, const u8* end, std::ostream& stream) +{ + std::size_t instruction_count = 0; + while (begin < end) + { + char inst_disasm[256]; + + const auto inst_size = + m_disasm.disasm64(0, reinterpret_cast<bx_address>(begin), begin, inst_disasm); + fmt::println(stream, "{}\t{}", fmt::ptr(begin), inst_disasm); + begin += inst_size; + ++instruction_count; + } + return instruction_count; +} +#endif + +std::unique_ptr<HostDisassembler> HostDisassembler::Factory(Platform arch) +{ + switch (arch) + { +#if defined(HAVE_LLVM) + case Platform::x86_64: + return std::make_unique<HostDisassemblerLLVM>("x86_64-none-unknown"); + case Platform::aarch64: + return std::make_unique<HostDisassemblerLLVM>("aarch64-none-unknown", "cortex-a57", 4); +#elif defined(_M_X86_64) + case Platform::x86_64: + return std::make_unique<HostDisassemblerBochs>(); +#else + case Platform{}: // warning C4065: "switch statement contains 'default' but no 'case' labels" +#endif + default: + return std::make_unique<HostDisassembler>(); + } +} + +std::size_t HostDisassembler::Disassemble(const u8* begin, const u8* end, std::ostream& stream) +{ + fmt::println(stream, "{}\t{:02x}", fmt::ptr(begin), fmt::join(std::span{begin, end}, "")); + return 0; +} |
