mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 09:21:05 +00:00
Object generation skeleton
This commit is contained in:
parent
ba0bfe7d82
commit
5c27161b23
@ -192,4 +192,12 @@ if(ENABLE_QT)
|
||||
include_directories(externals/qhexedit)
|
||||
add_subdirectory(externals/qhexedit)
|
||||
endif()
|
||||
|
||||
option(ENABLE_BINARY_TRANSLATION "Enable binary translation. Requires LLVM" OFF)
|
||||
if(ENABLE_BINARY_TRANSLATION)
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
llvm_map_components_to_libnames(llvm_libs Core Support X86 Arm ArmCodeGen ArmAsmParser ArmAsmPrinter ArmDisassembler ExecutionEngine MCJIT BitWriter ipo)
|
||||
endif()
|
||||
add_subdirectory(src)
|
||||
|
2
externals/nihstro
vendored
2
externals/nihstro
vendored
@ -1 +1 @@
|
||||
Subproject commit 81f1804a43f625e3a1a20752c0db70a413410380
|
||||
Subproject commit 4a78588b308564f7ebae193e0ae00d9a0d5741d5
|
@ -10,3 +10,6 @@ endif()
|
||||
if (ENABLE_QT)
|
||||
add_subdirectory(citra_qt)
|
||||
endif()
|
||||
if(ENABLE_BINARY_TRANSLATION)
|
||||
add_subdirectory(binary_translation)
|
||||
endif()
|
15
src/binary_translation/CMakeLists.txt
Normal file
15
src/binary_translation/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
set(SRCS
|
||||
main.cpp
|
||||
CodeGen.cpp
|
||||
)
|
||||
set(HEADERS
|
||||
CodeGen.h
|
||||
)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS})
|
||||
|
||||
add_executable(binary_translate ${SRCS} ${HEADERS})
|
||||
target_link_libraries(binary_translate ${llvm_libs})
|
||||
target_link_libraries(binary_translate core common video_core)
|
||||
target_link_libraries(binary_translate ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih)
|
||||
target_link_libraries(binary_translate ${PLATFORM_LIBRARIES})
|
162
src/binary_translation/CodeGen.cpp
Normal file
162
src/binary_translation/CodeGen.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
#include "CodeGen.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
#include <llvm/Target/TargetSubtargetInfo.h>
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/Support/raw_os_ostream.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/IR/Verifier.h>
|
||||
#include <llvm/Target/TargetLibraryInfo.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
CodeGen::CodeGen(const char* output_object_filename, const char* output_debug_filename)
|
||||
: output_object_filename(output_object_filename),
|
||||
output_debug_filename(output_debug_filename)
|
||||
{
|
||||
}
|
||||
|
||||
CodeGen::~CodeGen()
|
||||
{
|
||||
}
|
||||
|
||||
void CodeGen::Run()
|
||||
{
|
||||
if (!Loader::ROMCodeStart)
|
||||
{
|
||||
LOG_CRITICAL(BinaryTranslator, "No information from the loader about ROM file.");
|
||||
return;
|
||||
}
|
||||
|
||||
IntializeLLVM();
|
||||
GenerateModule();
|
||||
GenerateDebugFiles();
|
||||
if (!Verify()) return;
|
||||
OptimizeAndGenerate();
|
||||
}
|
||||
|
||||
void CodeGen::IntializeLLVM()
|
||||
{
|
||||
InitializeNativeTarget();
|
||||
InitializeNativeTargetAsmPrinter();
|
||||
|
||||
auto triple_string = sys::getProcessTriple();
|
||||
#ifdef _WIN32
|
||||
// LLVM doesn't know how to load coff files
|
||||
// It can handle elf files in every platform
|
||||
triple_string += "-elf";
|
||||
#endif
|
||||
|
||||
triple = llvm::make_unique<Triple>(triple_string);
|
||||
|
||||
// This engine builder is needed to get the target machine. It requires a module
|
||||
// but takes ownership of it so the main module cannot be passed here.
|
||||
EngineBuilder engine_builder(make_unique<Module>("", getGlobalContext()));
|
||||
target_machine.reset(engine_builder.selectTarget(*triple, "", "", SmallVector<std::string, 0>()));
|
||||
|
||||
module = make_unique<Module>("Module", getGlobalContext());
|
||||
module->setTargetTriple(triple_string);
|
||||
ir_builder = make_unique<IRBuilder<>>(getGlobalContext());
|
||||
}
|
||||
|
||||
void CodeGen::GenerateModule()
|
||||
{
|
||||
// TODO:
|
||||
}
|
||||
|
||||
void CodeGen::GenerateDebugFiles()
|
||||
{
|
||||
if (!output_debug_filename) return;
|
||||
|
||||
LOG_INFO(BinaryTranslator, "Writing debug file");
|
||||
std::ofstream file(output_debug_filename);
|
||||
if (!file)
|
||||
{
|
||||
LOG_ERROR(BinaryTranslator, "Cannot create debug file: %s", output_debug_filename);
|
||||
return;
|
||||
}
|
||||
raw_os_ostream stream(file);
|
||||
|
||||
module->print(stream, nullptr);
|
||||
file.close();
|
||||
LOG_INFO(BinaryTranslator, "Done");
|
||||
}
|
||||
|
||||
bool CodeGen::Verify()
|
||||
{
|
||||
LOG_INFO(BinaryTranslator, "Verifying");
|
||||
raw_os_ostream os(std::cout);
|
||||
if (verifyModule(*module, &os))
|
||||
{
|
||||
LOG_CRITICAL(BinaryTranslator, "Verify failed");
|
||||
return false;
|
||||
}
|
||||
LOG_INFO(BinaryTranslator, "Done");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CodeGen::OptimizeAndGenerate()
|
||||
{
|
||||
/*
|
||||
* Taken from opt for O3
|
||||
*/
|
||||
PassManagerBuilder pass_manager_builder;
|
||||
|
||||
FunctionPassManager function_pass_manager(module.get());
|
||||
|
||||
module->setDataLayout(target_machine->getSubtargetImpl()->getDataLayout());
|
||||
|
||||
function_pass_manager.add(new DataLayoutPass());
|
||||
target_machine->addAnalysisPasses(function_pass_manager);
|
||||
|
||||
pass_manager_builder.OptLevel = 3;
|
||||
pass_manager_builder.SizeLevel = 0;
|
||||
pass_manager_builder.Inliner = createFunctionInliningPass(3, 0);
|
||||
pass_manager_builder.LoopVectorize = true;
|
||||
pass_manager_builder.SLPVectorize = true;
|
||||
|
||||
pass_manager_builder.populateFunctionPassManager(function_pass_manager);
|
||||
|
||||
LOG_INFO(BinaryTranslator, "Optimizing functions");
|
||||
function_pass_manager.doInitialization();
|
||||
for (auto &function : *module)
|
||||
function_pass_manager.run(function);
|
||||
function_pass_manager.doFinalization();
|
||||
LOG_INFO(BinaryTranslator, "Done");
|
||||
|
||||
PassManager pass_manager;
|
||||
|
||||
pass_manager.add(createVerifierPass());
|
||||
pass_manager.add(new TargetLibraryInfo(*triple));
|
||||
pass_manager.add(new DataLayoutPass());
|
||||
target_machine->addAnalysisPasses(pass_manager);
|
||||
pass_manager_builder.populateModulePassManager(pass_manager);
|
||||
pass_manager.add(createVerifierPass());
|
||||
|
||||
LOG_INFO(BinaryTranslator, "Optimizing module");
|
||||
pass_manager.run(*module);
|
||||
LOG_INFO(BinaryTranslator, "Done");
|
||||
|
||||
MCContext *context;
|
||||
std::ofstream file(output_object_filename, std::ios::binary);
|
||||
if (!file)
|
||||
{
|
||||
LOG_CRITICAL(BinaryTranslator, "Cannot create object file: %s", output_object_filename);
|
||||
return;
|
||||
}
|
||||
raw_os_ostream stream(file);
|
||||
if (target_machine->addPassesToEmitMC(pass_manager, context, stream, false))
|
||||
{
|
||||
LOG_CRITICAL(BinaryTranslator, "Target does not support MC emission!");
|
||||
return;
|
||||
}
|
||||
LOG_INFO(BinaryTranslator, "Generating code");
|
||||
pass_manager.run(*module);
|
||||
file.close();
|
||||
LOG_INFO(BinaryTranslator, "Done");
|
||||
}
|
33
src/binary_translation/CodeGen.h
Normal file
33
src/binary_translation/CodeGen.h
Normal file
@ -0,0 +1,33 @@
|
||||
#include <memory>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
|
||||
namespace llvm
|
||||
{
|
||||
class TargetMachine;
|
||||
class Module;
|
||||
}
|
||||
|
||||
/*
|
||||
* Holds alls the basic llvm structures
|
||||
*/
|
||||
class CodeGen
|
||||
{
|
||||
public:
|
||||
CodeGen(const char *output_object_filename, const char *output_debug_filename);
|
||||
~CodeGen();
|
||||
|
||||
void Run();
|
||||
void IntializeLLVM();
|
||||
void GenerateModule();
|
||||
void GenerateDebugFiles();
|
||||
bool Verify();
|
||||
void OptimizeAndGenerate();
|
||||
private:
|
||||
const char *output_object_filename;
|
||||
const char *output_debug_filename;
|
||||
|
||||
std::unique_ptr<llvm::Triple> triple;
|
||||
std::unique_ptr<llvm::TargetMachine> target_machine;
|
||||
std::unique_ptr<llvm::Module> module;
|
||||
std::unique_ptr<llvm::IRBuilder<>> ir_builder;
|
||||
};
|
42
src/binary_translation/main.cpp
Normal file
42
src/binary_translation/main.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/text_formatter.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/mem_map.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "codegen.h"
|
||||
|
||||
int main(int argc, const char *const *argv)
|
||||
{
|
||||
std::shared_ptr<Log::Logger> logger = Log::InitGlobalLogger();
|
||||
Log::Filter log_filter(Log::Level::Debug);
|
||||
Log::SetFilter(&log_filter);
|
||||
std::thread logging_thread(Log::TextLoggingLoop, logger);
|
||||
SCOPE_EXIT({
|
||||
logger->Close();
|
||||
logging_thread.join();
|
||||
});
|
||||
|
||||
if (argc < 3)
|
||||
{
|
||||
LOG_CRITICAL(BinaryTranslator, "Usage: binary_translate <input_rom> <output_object> [<output_debug>]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto input_rom = argv[1];
|
||||
auto output_object = argv[2];
|
||||
auto output_debug = argc > 3 ? argv[3] : nullptr;
|
||||
|
||||
Core::Init();
|
||||
Memory::Init();
|
||||
|
||||
auto load_result = Loader::LoadFile(input_rom);
|
||||
if (Loader::ResultStatus::Success != load_result)
|
||||
{
|
||||
LOG_CRITICAL(BinaryTranslator, "Failed to load ROM (Error %i)!", load_result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CodeGen code_generator(output_object, output_debug);
|
||||
code_generator.Run();
|
||||
}
|
@ -72,5 +72,8 @@ endif()
|
||||
target_link_libraries(citra-qt core common video_core qhexedit)
|
||||
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
|
||||
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
|
||||
if(ENABLE_BINARY_TRANSLATION)
|
||||
target_link_libraries(citra-qt ${llvm_libs})
|
||||
endif()
|
||||
|
||||
#install(TARGETS citra-qt RUNTIME DESTINATION ${bindir})
|
||||
|
@ -53,7 +53,8 @@ static std::shared_ptr<Logger> global_logger;
|
||||
CLS(Render) \
|
||||
SUB(Render, Software) \
|
||||
SUB(Render, OpenGL) \
|
||||
CLS(Loader)
|
||||
CLS(Loader) \
|
||||
CLS(BinaryTranslator)
|
||||
|
||||
Logger::Logger() {
|
||||
// Register logging classes so that they can be queried at runtime
|
||||
|
@ -74,6 +74,7 @@ enum class Class : ClassType {
|
||||
Render_Software, ///< Software renderer backend
|
||||
Render_OpenGL, ///< OpenGL backend
|
||||
Loader, ///< ROM loader
|
||||
BinaryTranslator, ///< Binary translator
|
||||
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "core/loader/elf.h"
|
||||
#include "core/loader/ncch.h"
|
||||
#include "core/mem_map.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
#include "3dsx.h"
|
||||
|
||||
@ -203,6 +204,11 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr)
|
||||
// Write the data
|
||||
memcpy(Memory::GetPointer(base_addr), &all_mem[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + loadinfo.seg_sizes[2]);
|
||||
|
||||
Loader::ROMCodeStart = base_addr;
|
||||
Loader::ROMCodeSize = loadinfo.seg_sizes[0];
|
||||
Loader::ROMReadOnlyDataStart = base_addr + loadinfo.seg_sizes[0];
|
||||
Loader::ROMReadOnlyDataSize = loadinfo.seg_sizes[1];
|
||||
|
||||
LOG_DEBUG(Loader, "CODE: %u pages\n", loadinfo.seg_sizes[0] / 0x1000);
|
||||
LOG_DEBUG(Loader, "RODATA: %u pages\n", loadinfo.seg_sizes[1] / 0x1000);
|
||||
LOG_DEBUG(Loader, "DATA: %u pages\n", data_load_size / 0x1000);
|
||||
|
@ -24,6 +24,10 @@ const std::initializer_list<Kernel::AddressMapping> default_address_mappings = {
|
||||
{ 0x1FF70000, 0x8000, true }, // part of DSP RAM
|
||||
{ 0x1F000000, 0x600000, false }, // entire VRAM
|
||||
};
|
||||
u32 ROMCodeStart = 0;
|
||||
u32 ROMCodeSize = 0;
|
||||
u32 ROMReadOnlyDataStart = 0;
|
||||
u32 ROMReadOnlyDataSize = 0;
|
||||
|
||||
/**
|
||||
* Identifies the type of a bootable file
|
||||
|
@ -118,4 +118,12 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi
|
||||
*/
|
||||
ResultStatus LoadFile(const std::string& filename);
|
||||
|
||||
/*
|
||||
* Infomation about ROM
|
||||
*/
|
||||
extern u32 ROMCodeStart;
|
||||
extern u32 ROMCodeSize;
|
||||
extern u32 ROMReadOnlyDataStart;
|
||||
extern u32 ROMReadOnlyDataSize;
|
||||
|
||||
} // namespace
|
||||
|
@ -136,6 +136,13 @@ ResultStatus AppLoader_NCCH::LoadExec() const {
|
||||
s32 priority = exheader_header.arm11_system_local_caps.priority;
|
||||
u32 stack_size = exheader_header.codeset_info.stack_size;
|
||||
Kernel::g_current_process->Run(entry_point, priority, stack_size);
|
||||
|
||||
Loader::ROMCodeStart = entry_point;
|
||||
Loader::ROMCodeSize = entry_point + code.size();
|
||||
// TODO: Don't know where these are
|
||||
Loader::ROMReadOnlyDataStart = 0;
|
||||
Loader::ROMReadOnlyDataSize = 0;
|
||||
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
return ResultStatus::Error;
|
||||
|
Loading…
Reference in New Issue
Block a user