summaryrefslogtreecommitdiff
path: root/Source/Core/Common/HostDisassembler.cpp
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2024-10-21 12:03:50 -0400
committerGitHub <noreply@github.com>2024-10-21 12:03:50 -0400
commitf412e2488c24fc81a784c198ba9610e54cdd1d9a (patch)
tree2433844ec8e6192ed6051edf5460259c4a2cb0c1 /Source/Core/Common/HostDisassembler.cpp
parent24e9fc120c1b02b78cbf854c0eae78df0dbb60c1 (diff)
parentc3bda2e8757b4c37bec504f05ebda39b858e2969 (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.cpp158
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;
+}