Optionally disassemble x86_64 code using LLVM
This commit is contained in:
parent
39563c8ea8
commit
af27ef8d6c
7 changed files with 91 additions and 20 deletions
|
@ -3,6 +3,7 @@ project(dynarmic)
|
||||||
|
|
||||||
# Dynarmic project options
|
# Dynarmic project options
|
||||||
option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON)
|
option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON)
|
||||||
|
option(DYNARMIC_USE_LLVM "Support disassembly of jitted x86_64 code using LLVM" OFF)
|
||||||
|
|
||||||
# Compiler flags
|
# Compiler flags
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
|
@ -81,6 +82,14 @@ include_directories(${Boost_INCLUDE_DIRS})
|
||||||
include_directories(externals/catch)
|
include_directories(externals/catch)
|
||||||
enable_testing(true) # Enables unit-testing.
|
enable_testing(true) # Enables unit-testing.
|
||||||
|
|
||||||
|
# Include LLVM
|
||||||
|
if (DYNARMIC_USE_LLVM)
|
||||||
|
find_package(LLVM REQUIRED CONFIG)
|
||||||
|
include_directories(${LLVM_INCLUDE_DIRS})
|
||||||
|
add_definitions(-DDYNARMIC_USE_LLVM ${LLVM_DEFINITIONS})
|
||||||
|
llvm_map_components_to_libnames(llvm_libs x86desc x86disassembler)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Dynarmic project files
|
# Dynarmic project files
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
|
|
|
@ -48,13 +48,13 @@ static void EraseInstruction(IR::Block& block, IR::Inst* inst) {
|
||||||
block.instructions.erase(block.instructions.iterator_to(*inst));
|
block.instructions.erase(block.instructions.iterator_to(*inst));
|
||||||
}
|
}
|
||||||
|
|
||||||
CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Block& block) {
|
EmitX64::BlockDescriptor* EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Block& block) {
|
||||||
inhibit_emission.clear();
|
inhibit_emission.clear();
|
||||||
reg_alloc.Reset();
|
reg_alloc.Reset();
|
||||||
|
|
||||||
code->INT3();
|
code->INT3();
|
||||||
CodePtr code_ptr = code->GetCodePtr();
|
CodePtr code_ptr = code->GetCodePtr();
|
||||||
basic_blocks[descriptor] = code_ptr;
|
basic_blocks[descriptor].code_ptr = code_ptr;
|
||||||
|
|
||||||
EmitCondPrelude(block.cond, block.cond_failed, block.location);
|
EmitCondPrelude(block.cond, block.cond_failed, block.location);
|
||||||
|
|
||||||
|
@ -84,7 +84,8 @@ CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Bl
|
||||||
|
|
||||||
reg_alloc.AssertNoMoreUses();
|
reg_alloc.AssertNoMoreUses();
|
||||||
|
|
||||||
return code_ptr;
|
basic_blocks[descriptor].size = code->GetCodePtr() - code_ptr;
|
||||||
|
return &basic_blocks[descriptor];
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitIdentity(IR::Block& block, IR::Inst* inst) {
|
void EmitX64::EmitIdentity(IR::Block& block, IR::Inst* inst) {
|
||||||
|
|
|
@ -23,11 +23,15 @@ public:
|
||||||
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface)
|
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface)
|
||||||
: reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {}
|
: reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {}
|
||||||
|
|
||||||
CodePtr Emit(const Arm::LocationDescriptor descriptor, IR::Block& ir);
|
struct BlockDescriptor {
|
||||||
|
CodePtr code_ptr;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
BlockDescriptor* Emit(const Arm::LocationDescriptor descriptor, IR::Block& ir);
|
||||||
|
|
||||||
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
BlockDescriptor* GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
||||||
auto iter = basic_blocks.find(descriptor);
|
auto iter = basic_blocks.find(descriptor);
|
||||||
return iter != basic_blocks.end() ? iter->second : nullptr;
|
return iter != basic_blocks.end() ? &iter->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClearCache();
|
void ClearCache();
|
||||||
|
@ -62,7 +66,7 @@ private:
|
||||||
Routines* routines;
|
Routines* routines;
|
||||||
UserCallbacks cb;
|
UserCallbacks cb;
|
||||||
Jit* jit_interface;
|
Jit* jit_interface;
|
||||||
std::unordered_map<Arm::LocationDescriptor, CodePtr, Arm::LocationDescriptorHash> basic_blocks;
|
std::unordered_map<Arm::LocationDescriptor, BlockDescriptor, Arm::LocationDescriptorHash> basic_blocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef DYNARMIC_USE_LLVM
|
||||||
|
#include <llvm-c/Disassembler.h>
|
||||||
|
#include <llvm-c/Target.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "backend_x64/emit_x64.h"
|
#include "backend_x64/emit_x64.h"
|
||||||
#include "backend_x64/jitstate.h"
|
#include "backend_x64/jitstate.h"
|
||||||
#include "backend_x64/routines.h"
|
#include "backend_x64/routines.h"
|
||||||
|
@ -13,6 +18,7 @@
|
||||||
#include "common/bit_util.h"
|
#include "common/bit_util.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
|
#include "common/string_util.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/translate/translate.h"
|
#include "frontend/translate/translate.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
|
@ -44,14 +50,52 @@ struct Jit::Impl {
|
||||||
|
|
||||||
Arm::LocationDescriptor descriptor{pc, TFlag, EFlag, jit_state.Fpscr};
|
Arm::LocationDescriptor descriptor{pc, TFlag, EFlag, jit_state.Fpscr};
|
||||||
|
|
||||||
CodePtr code_ptr = GetBasicBlock(descriptor);
|
CodePtr code_ptr = GetBasicBlock(descriptor)->code_ptr;
|
||||||
return routines.RunCode(&jit_state, code_ptr, cycle_count);
|
return routines.RunCode(&jit_state, code_ptr, cycle_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Disassemble(Arm::LocationDescriptor descriptor) {
|
||||||
|
auto block = GetBasicBlock(descriptor);
|
||||||
|
std::string result = Common::StringFromFormat("address: %p\nsize: %zu bytes\n", block->code_ptr, block->size);
|
||||||
|
|
||||||
|
#ifdef DYNARMIC_USE_LLVM
|
||||||
|
CodePtr end = block->code_ptr + block->size;
|
||||||
|
size_t remaining = block->size;
|
||||||
|
|
||||||
|
LLVMInitializeX86TargetInfo();
|
||||||
|
LLVMInitializeX86TargetMC();
|
||||||
|
LLVMInitializeX86Disassembler();
|
||||||
|
LLVMDisasmContextRef llvm_ctx = LLVMCreateDisasm("x86_64", nullptr, 0, nullptr, nullptr);
|
||||||
|
LLVMSetDisasmOptions(llvm_ctx, LLVMDisassembler_Option_AsmPrinterVariant);
|
||||||
|
|
||||||
|
for (CodePtr pos = block->code_ptr; pos < end;) {
|
||||||
|
char buffer[80];
|
||||||
|
size_t inst_size = LLVMDisasmInstruction(llvm_ctx, const_cast<u8*>(pos), remaining, (u64)pos, buffer, sizeof(buffer));
|
||||||
|
assert(inst_size);
|
||||||
|
for (CodePtr i = pos; i < pos + inst_size; i++)
|
||||||
|
result.append(Common::StringFromFormat("%02x ", *i));
|
||||||
|
for (size_t i = inst_size; i < 10; i++)
|
||||||
|
result.append(" ");
|
||||||
|
result.append(buffer);
|
||||||
|
result.append("\n");
|
||||||
|
|
||||||
|
pos += inst_size;
|
||||||
|
remaining -= inst_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLVMDisasmDispose(llvm_ctx);
|
||||||
|
#else
|
||||||
|
result.append("(recompile with DYNARMIC_USE_LLVM=ON to disassemble the generated x86_64 code)\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
EmitX64::BlockDescriptor* GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
||||||
CodePtr code_ptr = emitter.GetBasicBlock(descriptor);
|
auto block = emitter.GetBasicBlock(descriptor);
|
||||||
if (code_ptr)
|
if (block)
|
||||||
return code_ptr;
|
return block;
|
||||||
|
|
||||||
IR::Block ir_block = Arm::Translate(descriptor, callbacks.MemoryRead32);
|
IR::Block ir_block = Arm::Translate(descriptor, callbacks.MemoryRead32);
|
||||||
Optimization::GetSetElimination(ir_block);
|
Optimization::GetSetElimination(ir_block);
|
||||||
|
@ -113,4 +157,8 @@ u32 Jit::Cpsr() const {
|
||||||
return impl->jit_state.Cpsr;
|
return impl->jit_state.Cpsr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Jit::Disassemble(Arm::LocationDescriptor descriptor) {
|
||||||
|
return impl->Disassemble(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
|
@ -75,6 +76,8 @@ public:
|
||||||
return is_executing;
|
return is_executing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Disassemble(Arm::LocationDescriptor descriptor);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool halt_requested = false;
|
bool halt_requested = false;
|
||||||
bool is_executing = false;
|
bool is_executing = false;
|
||||||
|
|
|
@ -33,5 +33,5 @@ set(HEADERS
|
||||||
|
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
add_executable(dynarmic_tests ${SRCS})
|
add_executable(dynarmic_tests ${SRCS})
|
||||||
target_link_libraries(dynarmic_tests dynarmic)
|
target_link_libraries(dynarmic_tests dynarmic ${llvm_libs})
|
||||||
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)
|
set_target_properties(dynarmic_tests PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
|
|
|
@ -7,17 +7,18 @@
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
#include <frontend/ir/ir.h>
|
|
||||||
#include <ir_opt/passes.h>
|
|
||||||
#include <frontend/translate/translate.h>
|
|
||||||
|
|
||||||
#include "common/bit_util.h"
|
#include "common/bit_util.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/disassembler/disassembler.h"
|
#include "frontend/disassembler/disassembler.h"
|
||||||
|
#include "frontend/ir/ir.h"
|
||||||
|
#include "frontend/translate/translate.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
|
#include "ir_opt/passes.h"
|
||||||
#include "rand_int.h"
|
#include "rand_int.h"
|
||||||
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||||
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
|
@ -219,22 +220,27 @@ void FuzzJitArm(const size_t instruction_count, const size_t instructions_to_exe
|
||||||
|
|
||||||
printf("\nInitial Register Listing: \n");
|
printf("\nInitial Register Listing: \n");
|
||||||
for (int i = 0; i <= 15; i++) {
|
for (int i = 0; i <= 15; i++) {
|
||||||
printf("%4i: %08x\n", i, initial_regs[i]);
|
auto reg = Dynarmic::Arm::RegToString(static_cast<Dynarmic::Arm::Reg>(i));
|
||||||
|
printf("%4s: %08x\n", reg, initial_regs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\nFinal Register Listing: \n");
|
printf("\nFinal Register Listing: \n");
|
||||||
printf(" interp jit\n");
|
printf(" interp jit\n");
|
||||||
for (int i = 0; i <= 15; i++) {
|
for (int i = 0; i <= 15; i++) {
|
||||||
printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
|
auto reg = Dynarmic::Arm::RegToString(static_cast<Dynarmic::Arm::Reg>(i));
|
||||||
|
printf("%4s: %08x %08x %s\n", reg, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
|
||||||
}
|
}
|
||||||
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
|
||||||
|
|
||||||
Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate({0, false, false, 0}, &MemoryRead32);
|
Dynarmic::Arm::LocationDescriptor descriptor = {0, false, false, 0};
|
||||||
|
Dynarmic::IR::Block ir_block = Dynarmic::Arm::Translate(descriptor, &MemoryRead32);
|
||||||
Dynarmic::Optimization::GetSetElimination(ir_block);
|
Dynarmic::Optimization::GetSetElimination(ir_block);
|
||||||
Dynarmic::Optimization::DeadCodeElimination(ir_block);
|
Dynarmic::Optimization::DeadCodeElimination(ir_block);
|
||||||
Dynarmic::Optimization::VerificationPass(ir_block);
|
Dynarmic::Optimization::VerificationPass(ir_block);
|
||||||
printf("\n\nIR:\n%s", Dynarmic::IR::DumpBlock(ir_block).c_str());
|
printf("\n\nIR:\n%s", Dynarmic::IR::DumpBlock(ir_block).c_str());
|
||||||
|
|
||||||
|
printf("\n\nx86_64:\n%s", jit.Disassemble(descriptor).c_str());
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
__debugbreak();
|
__debugbreak();
|
||||||
#else
|
#else
|
||||||
|
|
Loading…
Reference in a new issue