mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 21:11:05 +00:00
Updated AOT Recompiler to latest Citra, added 64bit support.
Signed-off-by: Patrick Martin <patrick.martin.r@gmail.com>
This commit is contained in:
parent
bbb96a392d
commit
4e7234a1c4
@ -41,6 +41,14 @@ option(CITRA_USE_BUNDLED_GLFW "Download bundled GLFW binaries" OFF)
|
|||||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||||
option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF)
|
option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF)
|
||||||
option(CITRA_FORCE_QT4 "Use Qt4 even if Qt5 is available." OFF)
|
option(CITRA_FORCE_QT4 "Use Qt4 even if Qt5 is available." OFF)
|
||||||
|
option(ENABLE_BINARY_TRANSLATION "Enable binary translation. Requires LLVM" OFF)
|
||||||
|
if(ENABLE_BINARY_TRANSLATION)
|
||||||
|
add_definitions(-DENABLE_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 native ExecutionEngine MCJIT BitWriter ipo)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit)
|
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||||
message(STATUS "Copying pre-commit hook")
|
message(STATUS "Copying pre-commit hook")
|
||||||
|
@ -10,3 +10,6 @@ endif()
|
|||||||
if (ENABLE_QT)
|
if (ENABLE_QT)
|
||||||
add_subdirectory(citra_qt)
|
add_subdirectory(citra_qt)
|
||||||
endif()
|
endif()
|
||||||
|
if(ENABLE_BINARY_TRANSLATION)
|
||||||
|
add_subdirectory(binary_translation)
|
||||||
|
endif()
|
||||||
|
160
src/binary_translation/ARMFuncs.cpp
Normal file
160
src/binary_translation/ARMFuncs.cpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#include "ARMFuncs.h"
|
||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
ARMFuncs::ShiftTN ARMFuncs::DecodeImmShift(InstructionBlock* instruction, u32 type, u32 imm5)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case 0: return{ SRType::LSL, ir_builder->getInt32(imm5) };
|
||||||
|
case 1: return{ SRType::LSR, ir_builder->getInt32(imm5 ? imm5 : 32) };
|
||||||
|
case 2: return{ SRType::ASR, ir_builder->getInt32(imm5 ? imm5 : 32) };
|
||||||
|
case 3:
|
||||||
|
if (imm5)
|
||||||
|
return{ SRType::ROR, ir_builder->getInt32(imm5) };
|
||||||
|
else
|
||||||
|
return{ SRType::RRX, ir_builder->getInt32(1) };
|
||||||
|
default: assert(false, "Invalid shift type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* ARMFuncs::Shift(InstructionBlock* instruction, llvm::Value* value, SRType type, llvm::Value* amount, llvm::Value* carry_in)
|
||||||
|
{
|
||||||
|
return Shift_C(instruction, value, type, amount, carry_in).result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARMFuncs::ResultCarry ARMFuncs::Shift_C(InstructionBlock* instruction, llvm::Value* value, SRType type, llvm::Value* amount, llvm::Value* carry_in)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
|
||||||
|
auto amount_zero = ir_builder->CreateICmpEQ(amount, ir_builder->getInt32(0));
|
||||||
|
ResultCarry result_amount_not_zero = {};
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SRType::LSL: result_amount_not_zero = LSL_C(instruction, value, amount); break;
|
||||||
|
case SRType::LSR: result_amount_not_zero = LSR_C(instruction, value, amount); break;
|
||||||
|
case SRType::ASR: result_amount_not_zero = ASR_C(instruction, value, amount); break;
|
||||||
|
case SRType::ROR: result_amount_not_zero = ROR_C(instruction, value, amount); break;
|
||||||
|
case SRType::RRX: result_amount_not_zero = RRX_C(instruction, value, carry_in); break;
|
||||||
|
default: assert(false, "Invalid shift type");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = ir_builder->CreateSelect(amount_zero, value, result_amount_not_zero.result);
|
||||||
|
auto carry = ir_builder->CreateSelect(amount_zero, carry_in, result_amount_not_zero.carry);
|
||||||
|
|
||||||
|
return{ result, carry };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates code for LSL, LSR that checks for 0 shift
|
||||||
|
llvm::Value* ShiftZeroCheck(
|
||||||
|
InstructionBlock *instruction, llvm::Value* x, llvm::Value* shift,
|
||||||
|
std::function<ARMFuncs::ResultCarry(InstructionBlock *, llvm::Value*, llvm::Value*)> non_zero_function)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
|
||||||
|
auto amount_zero = ir_builder->CreateICmpEQ(shift, ir_builder->getInt32(0));
|
||||||
|
auto result_amount_not_zero = non_zero_function(instruction, x, shift);
|
||||||
|
|
||||||
|
return ir_builder->CreateSelect(amount_zero, x, result_amount_not_zero.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ARMFuncs::ResultCarry ARMFuncs::LSL_C(InstructionBlock* instruction, llvm::Value* x, llvm::Value* shift)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
auto N = ir_builder->getInt32(32);
|
||||||
|
|
||||||
|
auto result = ir_builder->CreateShl(x, shift);
|
||||||
|
auto carry = ir_builder->CreateTrunc(ir_builder->CreateLShr(x, ir_builder->CreateSub(N, shift, "", true, true)), ir_builder->getInt1Ty());
|
||||||
|
return{ result, carry };
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* ARMFuncs::LSL(InstructionBlock* instruction, llvm::Value* x, llvm::Value* shift)
|
||||||
|
{
|
||||||
|
return ShiftZeroCheck(instruction, x, shift, &ARMFuncs::LSL_C);
|
||||||
|
}
|
||||||
|
|
||||||
|
ARMFuncs::ResultCarry ARMFuncs::LSR_C(InstructionBlock* instruction, llvm::Value* x, llvm::Value* shift)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
auto one = ir_builder->getInt32(1);
|
||||||
|
|
||||||
|
auto result = ir_builder->CreateLShr(x, shift);
|
||||||
|
auto carry = ir_builder->CreateTrunc(ir_builder->CreateLShr(x, ir_builder->CreateSub(shift, one, "", true, true)), ir_builder->getInt1Ty());
|
||||||
|
return{ result, carry };
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* ARMFuncs::LSR(InstructionBlock* instruction, llvm::Value* x, llvm::Value* shift)
|
||||||
|
{
|
||||||
|
return ShiftZeroCheck(instruction, x, shift, &ARMFuncs::LSR_C);
|
||||||
|
}
|
||||||
|
|
||||||
|
ARMFuncs::ResultCarry ARMFuncs::ASR_C(InstructionBlock* instruction, llvm::Value* x, llvm::Value* shift)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
auto one = ir_builder->getInt32(1);
|
||||||
|
|
||||||
|
auto result = ir_builder->CreateAShr(x, shift);
|
||||||
|
auto carry = ir_builder->CreateTrunc(ir_builder->CreateLShr(x, ir_builder->CreateSub(shift, one, "", true, true)), ir_builder->getInt1Ty());
|
||||||
|
return{ result, carry };
|
||||||
|
}
|
||||||
|
|
||||||
|
ARMFuncs::ResultCarry ARMFuncs::ROR_C(InstructionBlock* instruction, llvm::Value* x, llvm::Value* shift)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
auto N = ir_builder->getInt32(32);
|
||||||
|
auto m = ir_builder->CreateURem(shift, N);
|
||||||
|
|
||||||
|
auto result = ir_builder->CreateOr(LSR(instruction, x, m), LSL(instruction, x, ir_builder->CreateSub(N, m)));
|
||||||
|
auto carry = ir_builder->CreateTrunc(ir_builder->CreateLShr(result, ir_builder->getInt32(31)), ir_builder->getInt1Ty());
|
||||||
|
return{ result, carry };
|
||||||
|
}
|
||||||
|
|
||||||
|
ARMFuncs::ResultCarry ARMFuncs::RRX_C(InstructionBlock* instruction, llvm::Value* x, llvm::Value* carry_in)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
|
||||||
|
auto result = ir_builder->CreateLShr(x, 1);
|
||||||
|
result = ir_builder->CreateOr(result, ir_builder->CreateShl(ir_builder->CreateZExt(carry_in, ir_builder->getInt32Ty()), 31));
|
||||||
|
auto carry = ir_builder->CreateTrunc(x, ir_builder->getInt1Ty());
|
||||||
|
return{ result, carry };
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* ARMFuncs::ARMExpandImm(InstructionBlock* instruction, u32 imm12)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
// Manual says carry in does not affect the result, so use undef
|
||||||
|
return ARMExpandImm_C(instruction, imm12, llvm::UndefValue::get(ir_builder->getInt1Ty())).result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ARMFuncs::ResultCarry ARMFuncs::ARMExpandImm_C(InstructionBlock *instruction, u32 imm12, llvm::Value* carry)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
|
||||||
|
auto value = ir_builder->getInt32(imm12 & 0xFF);
|
||||||
|
auto shift = ir_builder->getInt32(2 * (imm12 >> 8));
|
||||||
|
return Shift_C(instruction, value, SRType::ROR, shift, carry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddWithCarry from armsupp.cpp
|
||||||
|
ARMFuncs::ResultCarryOverflow ARMFuncs::AddWithCarry(InstructionBlock* instruction, llvm::Value* x, llvm::Value* y, llvm::Value* carry_in)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction->IrBuilder();
|
||||||
|
|
||||||
|
auto xu64 = ir_builder->CreateZExt(x, ir_builder->getInt64Ty());
|
||||||
|
auto xs64 = ir_builder->CreateSExt(x, ir_builder->getInt64Ty());
|
||||||
|
auto yu64 = ir_builder->CreateZExt(y, ir_builder->getInt64Ty());
|
||||||
|
auto ys64 = ir_builder->CreateSExt(y, ir_builder->getInt64Ty());
|
||||||
|
auto c64 = ir_builder->CreateZExt(carry_in, ir_builder->getInt64Ty());
|
||||||
|
|
||||||
|
auto unsignedSum = ir_builder->CreateAdd(ir_builder->CreateAdd(xu64, yu64), c64);
|
||||||
|
auto singedSum = ir_builder->CreateAdd(ir_builder->CreateAdd(xs64, ys64), c64);
|
||||||
|
auto result32 = ir_builder->CreateTrunc(unsignedSum, ir_builder->getInt32Ty());
|
||||||
|
auto resultU64 = ir_builder->CreateZExt(result32, ir_builder->getInt64Ty());
|
||||||
|
auto resultS64 = ir_builder->CreateSExt(result32, ir_builder->getInt64Ty());
|
||||||
|
|
||||||
|
auto carry = ir_builder->CreateICmpNE(resultU64, unsignedSum);
|
||||||
|
auto overflow = ir_builder->CreateICmpNE(resultS64, singedSum);
|
||||||
|
|
||||||
|
return{ result32, carry, overflow };
|
||||||
|
}
|
51
src/binary_translation/ARMFuncs.h
Normal file
51
src/binary_translation/ARMFuncs.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#include <common/common_types.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Functions from the manual,
|
||||||
|
* A8.4.3 Pseudocode details of instruction-specified shifts and rotates
|
||||||
|
* A2.2.1 Integer arithmetic
|
||||||
|
* A5.2.4 Modified immediate constants in ARM instructions
|
||||||
|
*/
|
||||||
|
|
||||||
|
class InstructionBlock;
|
||||||
|
|
||||||
|
namespace llvm
|
||||||
|
{
|
||||||
|
class Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ARMFuncs
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class SRType { LSL, LSR, ASR, RRX, ROR };
|
||||||
|
struct ShiftTN
|
||||||
|
{
|
||||||
|
SRType type;
|
||||||
|
llvm::Value *amount;
|
||||||
|
};
|
||||||
|
struct ResultCarry
|
||||||
|
{
|
||||||
|
llvm::Value *result, *carry;
|
||||||
|
};
|
||||||
|
struct ResultCarryOverflow
|
||||||
|
{
|
||||||
|
llvm::Value *result, *carry, *overflow;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ShiftTN DecodeImmShift(InstructionBlock *instruction, u32 type, u32 imm5);
|
||||||
|
|
||||||
|
static llvm::Value *Shift(InstructionBlock *instruction, llvm::Value *value, SRType type, llvm::Value *amount, llvm::Value *carry_in);
|
||||||
|
static ResultCarry Shift_C(InstructionBlock *instruction, llvm::Value *value, SRType type, llvm::Value *amount, llvm::Value *carry_in);
|
||||||
|
static ResultCarry LSL_C(InstructionBlock *instruction, llvm::Value *x, llvm::Value *shift);
|
||||||
|
static llvm::Value *LSL(InstructionBlock *instruction, llvm::Value *x, llvm::Value *shift);
|
||||||
|
static ResultCarry LSR_C(InstructionBlock *instruction, llvm::Value *x, llvm::Value *shift);
|
||||||
|
static llvm::Value *LSR(InstructionBlock *instruction, llvm::Value *x, llvm::Value *shift);
|
||||||
|
static ResultCarry ASR_C(InstructionBlock *instruction, llvm::Value *x, llvm::Value *shift);
|
||||||
|
static ResultCarry ROR_C(InstructionBlock *instruction, llvm::Value *x, llvm::Value *shift);
|
||||||
|
static ResultCarry RRX_C(InstructionBlock *instruction, llvm::Value *x, llvm::Value *carry_in);
|
||||||
|
|
||||||
|
static llvm::Value *ARMExpandImm(InstructionBlock *instruction, u32 imm12);
|
||||||
|
static ResultCarry ARMExpandImm_C(InstructionBlock *instruction, u32 imm12, llvm::Value *carry);
|
||||||
|
|
||||||
|
static ResultCarryOverflow AddWithCarry(InstructionBlock *instruction, llvm::Value *x, llvm::Value *y, llvm::Value *carry_in);
|
||||||
|
};
|
22
src/binary_translation/BinarySearch.h
Normal file
22
src/binary_translation/BinarySearch.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
// Used for debugging
|
||||||
|
|
||||||
|
struct BinarySearch
|
||||||
|
{
|
||||||
|
size_t min;
|
||||||
|
size_t mid;
|
||||||
|
size_t max;
|
||||||
|
BinarySearch(size_t max) : min(0), mid(max / 2), max(max) { }
|
||||||
|
BinarySearch(size_t min, size_t max) : min(min), mid((min + max) / 2), max(max) { }
|
||||||
|
BinarySearch l() const { return BinarySearch(min, mid); }
|
||||||
|
BinarySearch r() const { return BinarySearch(mid, max); }
|
||||||
|
operator size_t()
|
||||||
|
{
|
||||||
|
LOG_DEBUG(BinaryTranslator, "BinarySearch: %x: %x - %x (%x, %d)", mid, max, min, max - min, (size_t)std::log2(max - min));
|
||||||
|
return mid;
|
||||||
|
}
|
||||||
|
operator int() { return static_cast<size_t>(*this); }
|
||||||
|
};
|
105
src/binary_translation/BlockColors.cpp
Normal file
105
src/binary_translation/BlockColors.cpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include "BlockColors.h"
|
||||||
|
#include <stack>
|
||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include <llvm/IR/Function.h>
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
BlockColors::BlockColors(ModuleGen* module) : module(module)
|
||||||
|
{
|
||||||
|
auto ir_builder = module->IrBuilder();
|
||||||
|
function_type = FunctionType::get(ir_builder->getVoidTy(), ir_builder->getInt32Ty(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockColors::~BlockColors()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockColors::AddBlock(InstructionBlock* block)
|
||||||
|
{
|
||||||
|
if (block->HasColor()) return;
|
||||||
|
|
||||||
|
std::stack<InstructionBlock *> current_color_stack;
|
||||||
|
current_color_stack.push(block);
|
||||||
|
auto color = colors.size();
|
||||||
|
colors.push_back({ color });
|
||||||
|
|
||||||
|
while (current_color_stack.size())
|
||||||
|
{
|
||||||
|
auto item = current_color_stack.top();
|
||||||
|
current_color_stack.pop();
|
||||||
|
|
||||||
|
item->SetColor(color);
|
||||||
|
colors[color].instructions.push_back(item);
|
||||||
|
for (auto next : item->GetNexts())
|
||||||
|
{
|
||||||
|
if (next->HasColor()) assert(next->GetColor() == color);
|
||||||
|
else current_color_stack.push(next);
|
||||||
|
}
|
||||||
|
for (auto prev : item->GetPrevs())
|
||||||
|
{
|
||||||
|
if (prev->HasColor()) assert(prev->GetColor() == color);
|
||||||
|
else current_color_stack.push(prev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockColors::GenerateFunctions()
|
||||||
|
{
|
||||||
|
auto ir_builder = module->IrBuilder();
|
||||||
|
|
||||||
|
LOG_INFO(BinaryTranslator, "%x block colors", colors.size());
|
||||||
|
|
||||||
|
for (auto &color : colors)
|
||||||
|
{
|
||||||
|
auto function = Function::Create(function_type, GlobalValue::PrivateLinkage,
|
||||||
|
"ColorFunction", module->Module());
|
||||||
|
color.function = function;
|
||||||
|
auto index = &function->getArgumentList().front();
|
||||||
|
|
||||||
|
auto entry_basic_block = BasicBlock::Create(getGlobalContext(), "Entry", function);
|
||||||
|
auto default_case_basic_block = BasicBlock::Create(getGlobalContext(), "Default", function);
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(default_case_basic_block);
|
||||||
|
ir_builder->CreateUnreachable();
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(entry_basic_block);
|
||||||
|
auto switch_instruction = ir_builder->CreateSwitch(index, default_case_basic_block, color.instructions.size());
|
||||||
|
for (size_t i = 0; i < color.instructions.size(); ++i)
|
||||||
|
{
|
||||||
|
switch_instruction->addCase(ir_builder->getInt32(i), color.instructions[i]->GetEntryBasicBlock());
|
||||||
|
AddBasicBlocksToFunction(function, color.instructions[i]->GetEntryBasicBlock());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlockColors::AddBasicBlocksToFunction(Function* function, BasicBlock* basic_block)
|
||||||
|
{
|
||||||
|
if (basic_block->getParent())
|
||||||
|
{
|
||||||
|
assert(basic_block->getParent() == function);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stack<BasicBlock *> basic_blocks;
|
||||||
|
basic_blocks.push(basic_block);
|
||||||
|
while (basic_blocks.size())
|
||||||
|
{
|
||||||
|
auto top = basic_blocks.top();
|
||||||
|
basic_blocks.pop();
|
||||||
|
|
||||||
|
top->insertInto(function);
|
||||||
|
auto terminator = top->getTerminator();
|
||||||
|
for (auto i = 0; i < terminator->getNumSuccessors(); ++i)
|
||||||
|
{
|
||||||
|
auto next = terminator->getSuccessor(i);
|
||||||
|
if (next->getParent())
|
||||||
|
{
|
||||||
|
assert(next->getParent() == function);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
basic_blocks.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
src/binary_translation/BlockColors.h
Normal file
50
src/binary_translation/BlockColors.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace llvm
|
||||||
|
{
|
||||||
|
class BasicBlock;
|
||||||
|
class Function;
|
||||||
|
class FunctionType;
|
||||||
|
}
|
||||||
|
class InstructionBlock;
|
||||||
|
class ModuleGen;
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Responsible to partition the blocks by connectivity, each disjoined graph gets a color
|
||||||
|
And to generate a function for each color
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
class BlockColors
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlockColors(ModuleGen *module);
|
||||||
|
~BlockColors();
|
||||||
|
|
||||||
|
void AddBlock(InstructionBlock *block);
|
||||||
|
// Generates a function for each color
|
||||||
|
void GenerateFunctions();
|
||||||
|
|
||||||
|
llvm::FunctionType *GetFunctionType() { return function_type; }
|
||||||
|
size_t GetColorCount() const { return colors.size(); }
|
||||||
|
size_t GetColorInstructionCount(size_t color) const { return colors[color].instructions.size(); }
|
||||||
|
InstructionBlock *GetColorInstruction(size_t color, size_t index) { return colors[color].instructions[index]; }
|
||||||
|
llvm::Function *GetColorFunction(size_t color) { return colors[color].function; }
|
||||||
|
private:
|
||||||
|
ModuleGen *module;
|
||||||
|
|
||||||
|
// void ColorFunction(int i)
|
||||||
|
// Runs the code for color->instructions[i]
|
||||||
|
llvm::FunctionType *function_type;
|
||||||
|
|
||||||
|
void AddBasicBlocksToFunction(llvm::Function *function, llvm::BasicBlock *basic_block);
|
||||||
|
|
||||||
|
struct Color
|
||||||
|
{
|
||||||
|
size_t color;
|
||||||
|
std::vector<InstructionBlock *> instructions;
|
||||||
|
llvm::Function *function;
|
||||||
|
};
|
||||||
|
std::vector<Color> colors;
|
||||||
|
};
|
46
src/binary_translation/CMakeLists.txt
Normal file
46
src/binary_translation/CMakeLists.txt
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
set(SRCS
|
||||||
|
main.cpp
|
||||||
|
CodeGen.cpp
|
||||||
|
ModuleGen.cpp
|
||||||
|
Disassembler.cpp
|
||||||
|
InstructionBlock.cpp
|
||||||
|
MachineState.cpp
|
||||||
|
TBAA.cpp
|
||||||
|
ARMFuncs.cpp
|
||||||
|
BlockColors.cpp
|
||||||
|
|
||||||
|
Instructions/Instruction.cpp
|
||||||
|
Instructions/MovShift.cpp
|
||||||
|
Instructions/Branch.cpp
|
||||||
|
Instructions/Arithmetic.cpp
|
||||||
|
Instructions/Ldr.cpp
|
||||||
|
Instructions/Str.cpp
|
||||||
|
)
|
||||||
|
set(HEADERS
|
||||||
|
CodeGen.h
|
||||||
|
ModuleGen.h
|
||||||
|
Disassembler.h
|
||||||
|
InstructionBlock.h
|
||||||
|
MachineState.h
|
||||||
|
TBAA.h
|
||||||
|
BinarySearch.h
|
||||||
|
ARMFuncs.h
|
||||||
|
BlockColors.h
|
||||||
|
|
||||||
|
Instructions/Types.h
|
||||||
|
Instructions/Types.h
|
||||||
|
Instructions/Instruction.h
|
||||||
|
Instructions/MovShift.h
|
||||||
|
Instructions/Branch.h
|
||||||
|
Instructions/Arithmetic.h
|
||||||
|
Instructions/Ldr.h
|
||||||
|
Instructions/Str.h
|
||||||
|
)
|
||||||
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
|
|
||||||
|
include_directories(.)
|
||||||
|
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 inih)
|
||||||
|
target_link_libraries(binary_translate ${PLATFORM_LIBRARIES})
|
170
src/binary_translation/CodeGen.cpp
Normal file
170
src/binary_translation/CodeGen.cpp
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include "CodeGen.h"
|
||||||
|
#include "ModuleGen.h"
|
||||||
|
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "common/logging/log.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/Support/raw_ostream.h>
|
||||||
|
#include <llvm/IR/LegacyPassManager.h>
|
||||||
|
#include <llvm/IR/Verifier.h>
|
||||||
|
#include <llvm/Analysis/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, bool verify)
|
||||||
|
: output_object_filename(output_object_filename),
|
||||||
|
output_debug_filename(output_debug_filename),
|
||||||
|
verify(verify)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeGen::~CodeGen()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeGen::Run()
|
||||||
|
{
|
||||||
|
if (!Loader::ROMCodeStart)
|
||||||
|
{
|
||||||
|
LOG_CRITICAL(BinaryTranslator, "No information from the loader about ROM file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeLLVM();
|
||||||
|
GenerateModule();
|
||||||
|
GenerateDebugFiles();
|
||||||
|
if (!Verify()) return;
|
||||||
|
OptimizeAndGenerate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeGen::InitializeLLVM()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CodeGen::GenerateModule()
|
||||||
|
{
|
||||||
|
moduleGenerator = std::make_unique<ModuleGen>(module.get(), verify);
|
||||||
|
moduleGenerator->Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
stream.flush();
|
||||||
|
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;
|
||||||
|
|
||||||
|
legacy::FunctionPassManager function_pass_manager(module.get());
|
||||||
|
legacy::PassManager pass_manager;
|
||||||
|
|
||||||
|
module->setDataLayout(*target_machine->getDataLayout());
|
||||||
|
|
||||||
|
pass_manager.add(createVerifierPass());
|
||||||
|
pass_manager.add(new TargetLibraryInfoWrapperPass(*triple.get()));
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
pass_manager_builder.OptLevel = 1;
|
||||||
|
pass_manager_builder.SizeLevel = 0;
|
||||||
|
pass_manager_builder.LoopVectorize = false;
|
||||||
|
pass_manager_builder.SLPVectorize = false;
|
||||||
|
pass_manager_builder.populateModulePassManager(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");
|
||||||
|
|
||||||
|
pass_manager.add(createVerifierPass());
|
||||||
|
|
||||||
|
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 fstream(file);
|
||||||
|
buffer_ostream* stream = new buffer_ostream(fstream);
|
||||||
|
|
||||||
|
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);
|
||||||
|
stream->flush();
|
||||||
|
delete stream;
|
||||||
|
fstream.flush();
|
||||||
|
file.close();
|
||||||
|
LOG_INFO(BinaryTranslator, "Done");
|
||||||
|
}
|
37
src/binary_translation/CodeGen.h
Normal file
37
src/binary_translation/CodeGen.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <llvm/IR/IRBuilder.h>
|
||||||
|
|
||||||
|
namespace llvm
|
||||||
|
{
|
||||||
|
class TargetMachine;
|
||||||
|
class Module;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleGen;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Holds alls the basic llvm structures
|
||||||
|
*/
|
||||||
|
class CodeGen
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CodeGen(const char *output_object_filename, const char *output_debug_filename, bool verify);
|
||||||
|
~CodeGen();
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
void InitializeLLVM();
|
||||||
|
void GenerateModule();
|
||||||
|
void GenerateDebugFiles();
|
||||||
|
bool Verify();
|
||||||
|
void OptimizeAndGenerate();
|
||||||
|
private:
|
||||||
|
const char *output_object_filename;
|
||||||
|
const char *output_debug_filename;
|
||||||
|
bool verify;
|
||||||
|
|
||||||
|
std::unique_ptr<ModuleGen> moduleGenerator;
|
||||||
|
|
||||||
|
std::unique_ptr<llvm::Triple> triple;
|
||||||
|
std::unique_ptr<llvm::TargetMachine> target_machine;
|
||||||
|
std::unique_ptr<llvm::Module> module;
|
||||||
|
};
|
23
src/binary_translation/Disassembler.cpp
Normal file
23
src/binary_translation/Disassembler.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "Disassembler.h"
|
||||||
|
#include "Instructions/Instruction.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
std::vector<RegisterInstructionBase::CreateFunctionType> g_read_functions;
|
||||||
|
|
||||||
|
RegisterInstructionBase::RegisterInstructionBase(CreateFunctionType create_function)
|
||||||
|
{
|
||||||
|
g_read_functions.push_back(create_function);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Instruction> Disassembler::Disassemble(u32 instruction, u32 address)
|
||||||
|
{
|
||||||
|
for (auto read_function : g_read_functions)
|
||||||
|
{
|
||||||
|
auto result = read_function(instruction, address);
|
||||||
|
if (result != nullptr)
|
||||||
|
{
|
||||||
|
return std::unique_ptr<Instruction>(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
43
src/binary_translation/Disassembler.h
Normal file
43
src/binary_translation/Disassembler.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include "common/common_types.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class Instruction;
|
||||||
|
|
||||||
|
class Disassembler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Returns the instruction at address or null if unknown or not translatable
|
||||||
|
* address is used for PC relative operations
|
||||||
|
*/
|
||||||
|
static std::unique_ptr<Instruction> Disassemble(u32 instruction, u32 address);
|
||||||
|
};
|
||||||
|
|
||||||
|
class RegisterInstructionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Instruction *(*CreateFunctionType)(u32 instruction, u32 address);
|
||||||
|
|
||||||
|
RegisterInstructionBase(CreateFunctionType create_function);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Instantiate this class in a source file to register instruction in the disassembler
|
||||||
|
*/
|
||||||
|
template<typename DerivedInstruction>
|
||||||
|
class RegisterInstruction : RegisterInstructionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RegisterInstruction() : RegisterInstructionBase(&RegisterInstruction::Create) {}
|
||||||
|
private:
|
||||||
|
static Instruction *Create(u32 instruction, u32 address)
|
||||||
|
{
|
||||||
|
auto result = new DerivedInstruction();
|
||||||
|
if (!result->Read(instruction, address))
|
||||||
|
{
|
||||||
|
delete result;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
60
src/binary_translation/InstructionBlock.cpp
Normal file
60
src/binary_translation/InstructionBlock.cpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include "ModuleGen.h"
|
||||||
|
#include "Instructions/Instruction.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include "MachineState.h"
|
||||||
|
|
||||||
|
InstructionBlock::InstructionBlock(ModuleGen* module, Instruction* instruction)
|
||||||
|
: module(module),
|
||||||
|
instruction(std::unique_ptr<Instruction>(instruction))
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::hex << std::setfill('0') << std::setw(8) << instruction->Address() << "_";
|
||||||
|
address_string = ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
InstructionBlock::~InstructionBlock()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionBlock::GenerateEntryBlock()
|
||||||
|
{
|
||||||
|
entry_basic_block = CreateBasicBlock("Entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionBlock::GenerateCode()
|
||||||
|
{
|
||||||
|
auto ir_builder = Module()->IrBuilder();
|
||||||
|
ir_builder->SetInsertPoint(entry_basic_block);
|
||||||
|
|
||||||
|
module->GenerateIncInstructionCount();
|
||||||
|
|
||||||
|
instruction->GenerateCode(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *InstructionBlock::Read(Register reg)
|
||||||
|
{
|
||||||
|
return module->Machine()->ReadRegiser(reg,true);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value *InstructionBlock::Write(Register reg, llvm::Value *value)
|
||||||
|
{
|
||||||
|
return module->Machine()->WriteRegiser(reg, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::BasicBlock *InstructionBlock::CreateBasicBlock(const char *name)
|
||||||
|
{
|
||||||
|
return llvm::BasicBlock::Create(llvm::getGlobalContext(), address_string + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstructionBlock::Link(InstructionBlock* prev, InstructionBlock* next)
|
||||||
|
{
|
||||||
|
prev->nexts.push_back(next);
|
||||||
|
next->prevs.push_back(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 InstructionBlock::Address() const
|
||||||
|
{
|
||||||
|
return instruction->Address();
|
||||||
|
}
|
85
src/binary_translation/InstructionBlock.h
Normal file
85
src/binary_translation/InstructionBlock.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <common/common_types.h>
|
||||||
|
#include <llvm/IR/IRBuilder.h>
|
||||||
|
#include "ModuleGen.h"
|
||||||
|
|
||||||
|
namespace llvm
|
||||||
|
{
|
||||||
|
class Value;
|
||||||
|
class BasicBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Instruction;
|
||||||
|
|
||||||
|
enum class Register;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An instruction blocks
|
||||||
|
* Holds the entry and exit points for an instruction
|
||||||
|
* Responsible to generate the code
|
||||||
|
*/
|
||||||
|
class InstructionBlock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InstructionBlock(ModuleGen *module, Instruction *instruction);
|
||||||
|
~InstructionBlock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates the basic block of the instruction
|
||||||
|
*/
|
||||||
|
void GenerateEntryBlock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates the code for the instruction
|
||||||
|
*/
|
||||||
|
void GenerateCode();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates code to read the register
|
||||||
|
*/
|
||||||
|
llvm::Value *Read(Register reg);
|
||||||
|
/*
|
||||||
|
* Generates code to write the value
|
||||||
|
* Returns the write instruction = written value
|
||||||
|
*/
|
||||||
|
llvm::Value *Write(Register reg, llvm::Value *value);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a basic block for use by instructions
|
||||||
|
*/
|
||||||
|
llvm::BasicBlock *CreateBasicBlock(const char *name);
|
||||||
|
/*
|
||||||
|
* Links two instructions, adding to prev and next lists
|
||||||
|
*/
|
||||||
|
static void Link(InstructionBlock *prev, InstructionBlock *next);
|
||||||
|
|
||||||
|
u32 Address() const;
|
||||||
|
ModuleGen *Module() { return module; }
|
||||||
|
llvm::IRBuilder<> *IrBuilder() { return module->IrBuilder(); }
|
||||||
|
|
||||||
|
llvm::BasicBlock *GetEntryBasicBlock() { return entry_basic_block; }
|
||||||
|
|
||||||
|
bool HasColor() const { return has_color; }
|
||||||
|
void SetColor(size_t color) { this->color = color; has_color = true; }
|
||||||
|
size_t GetColor() const { return color; }
|
||||||
|
|
||||||
|
std::list<InstructionBlock *> GetNexts() const { return nexts; }
|
||||||
|
std::list<InstructionBlock *> GetPrevs() const { return prevs; }
|
||||||
|
private:
|
||||||
|
// Textual representation of the address
|
||||||
|
// Used to generate names
|
||||||
|
std::string address_string;
|
||||||
|
|
||||||
|
ModuleGen *module;
|
||||||
|
std::unique_ptr<Instruction> instruction;
|
||||||
|
|
||||||
|
// The block at the entry to instruction
|
||||||
|
llvm::BasicBlock *entry_basic_block;
|
||||||
|
|
||||||
|
bool has_color = false;
|
||||||
|
size_t color;
|
||||||
|
|
||||||
|
std::list<InstructionBlock *> nexts;
|
||||||
|
std::list<InstructionBlock *> prevs;
|
||||||
|
};
|
124
src/binary_translation/Instructions/Arithmetic.cpp
Normal file
124
src/binary_translation/Instructions/Arithmetic.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#include "Arithmetic.h"
|
||||||
|
#include "Disassembler.h"
|
||||||
|
#include <binary_translation/ARMFuncs.h>
|
||||||
|
#include <binary_translation/InstructionBlock.h>
|
||||||
|
|
||||||
|
static RegisterInstruction<Arithmetic> register_instruction;
|
||||||
|
|
||||||
|
bool IsSupported(Arithmetic::Op op)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case Arithmetic::Op::BitwiseAnd: return true;
|
||||||
|
case Arithmetic::Op::BitwiseXor: return true;
|
||||||
|
case Arithmetic::Op::Subtract: return true;
|
||||||
|
case Arithmetic::Op::RevSubtract: return true;
|
||||||
|
case Arithmetic::Op::Add: return true;
|
||||||
|
case Arithmetic::Op::AddWithCarry: return true;
|
||||||
|
case Arithmetic::Op::SubtractWithCarry: return true;
|
||||||
|
case Arithmetic::Op::ReverseSubtractWithCarry: return true;
|
||||||
|
case Arithmetic::Op::BitwiseOr: return true;
|
||||||
|
case Arithmetic::Op::MoveAndShifts: return false; // Handled in MovShift.cpp
|
||||||
|
case Arithmetic::Op::BitwiseBitClear: return true;
|
||||||
|
case Arithmetic::Op::BitwiseNot: return false; // MVN
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBitwise(Arithmetic::Op op)
|
||||||
|
{
|
||||||
|
return op == Arithmetic::Op::BitwiseAnd ||
|
||||||
|
op == Arithmetic::Op::BitwiseXor ||
|
||||||
|
op == Arithmetic::Op::BitwiseOr ||
|
||||||
|
op == Arithmetic::Op::BitwiseBitClear;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Arithmetic::Decode()
|
||||||
|
{
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<3>(0), FieldDef<4>(&op), FieldDef<1>(&set_flags), FieldDef<4>(&rn),
|
||||||
|
FieldDef<4>(&rd), FieldDef<5>(&imm5), FieldDef<2>(&type), FieldDef<1>(0), FieldDef<4>(&rm)}))
|
||||||
|
{
|
||||||
|
form = Form::Register;
|
||||||
|
if (rd == Register::PC && set_flags) return false; // SEE SUBS PC, LR and related instructions;
|
||||||
|
if (rn == Register::PC) return false;
|
||||||
|
if (rm == Register::PC) return false;
|
||||||
|
return IsSupported(op);
|
||||||
|
}
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<3>(1), FieldDef<4>(&op), FieldDef<1>(&set_flags), FieldDef<4>(&rn),
|
||||||
|
FieldDef<4>(&rd), FieldDef<12>(&imm12) }))
|
||||||
|
{
|
||||||
|
form = Form::Immediate;
|
||||||
|
if (rd == Register::PC && set_flags) return false; // SEE SUBS PC, LR and related instructions;
|
||||||
|
if (rn == Register::PC) return false;
|
||||||
|
return IsSupported(op);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arithmetic::GenerateInstructionCode(InstructionBlock* instruction_block)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction_block->Module()->IrBuilder();
|
||||||
|
|
||||||
|
llvm::Value *left, *right, *carry_in;
|
||||||
|
ARMFuncs::ResultCarryOverflow result = { nullptr, nullptr, nullptr };
|
||||||
|
auto bitwise = IsBitwise(op);
|
||||||
|
|
||||||
|
carry_in = instruction_block->Read(Register::C);
|
||||||
|
|
||||||
|
left = instruction_block->Read(rn);
|
||||||
|
|
||||||
|
if (form == Form::Register)
|
||||||
|
{
|
||||||
|
if (bitwise)
|
||||||
|
{
|
||||||
|
auto shift_tn = ARMFuncs::DecodeImmShift(instruction_block, type, imm5);
|
||||||
|
auto shifted_carry = ARMFuncs::Shift_C(instruction_block, instruction_block->Read(rm), shift_tn.type, shift_tn.amount, carry_in);
|
||||||
|
right = shifted_carry.result;
|
||||||
|
result.carry = shifted_carry.carry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto shift_tn = ARMFuncs::DecodeImmShift(instruction_block, type, imm5);
|
||||||
|
right = ARMFuncs::Shift(instruction_block, instruction_block->Read(rm), shift_tn.type, shift_tn.amount, carry_in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (bitwise)
|
||||||
|
{
|
||||||
|
auto imm32_carry = ARMFuncs::ARMExpandImm_C(instruction_block, imm12, carry_in);
|
||||||
|
right = imm32_carry.result;
|
||||||
|
result.carry = imm32_carry.carry;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
right = ARMFuncs::ARMExpandImm(instruction_block, imm12);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case Op::BitwiseAnd: result.result = ir_builder->CreateAnd(left, right); break;
|
||||||
|
case Op::BitwiseXor: result.result = ir_builder->CreateXor(left, right); break;
|
||||||
|
case Op::Subtract: result = ARMFuncs::AddWithCarry(instruction_block, left, ir_builder->CreateNot(right), ir_builder->getInt32(1)); break;
|
||||||
|
case Op::RevSubtract: result = ARMFuncs::AddWithCarry(instruction_block, ir_builder->CreateNot(left), right, ir_builder->getInt32(1)); break;
|
||||||
|
case Op::Add: result = ARMFuncs::AddWithCarry(instruction_block, left, right, ir_builder->getInt32(0)); break;
|
||||||
|
case Op::AddWithCarry: result = ARMFuncs::AddWithCarry(instruction_block, left, right, carry_in); break;
|
||||||
|
case Op::SubtractWithCarry: result = ARMFuncs::AddWithCarry(instruction_block, left, ir_builder->CreateNot(right), carry_in); break;
|
||||||
|
case Op::ReverseSubtractWithCarry: result = ARMFuncs::AddWithCarry(instruction_block, ir_builder->CreateNot(left), right, carry_in); break;
|
||||||
|
case Op::BitwiseOr: result.result = ir_builder->CreateOr(left, right); break;
|
||||||
|
case Op::BitwiseBitClear: result.result = ir_builder->CreateAnd(left, ir_builder->CreateNot(right)); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction_block->Write(rd, result.result);
|
||||||
|
|
||||||
|
if (set_flags)
|
||||||
|
{
|
||||||
|
instruction_block->Write(Register::N, ir_builder->CreateICmpSLT(result.result, ir_builder->getInt32(0)));
|
||||||
|
instruction_block->Write(Register::Z, ir_builder->CreateICmpEQ(result.result, ir_builder->getInt32(0)));
|
||||||
|
instruction_block->Write(Register::C, result.carry);
|
||||||
|
if (result.overflow)
|
||||||
|
instruction_block->Write(Register::V, result.overflow);
|
||||||
|
}
|
||||||
|
}
|
35
src/binary_translation/Instructions/Arithmetic.h
Normal file
35
src/binary_translation/Instructions/Arithmetic.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include "Instruction.h"
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data processing instructions
|
||||||
|
* ARMv7-A 5.2.1 (register), 5.2.2 (register-shifted register), 5.2.3 (immediate)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Arithmetic : public Instruction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Form
|
||||||
|
{
|
||||||
|
Register, RegisterShiftedRegister, Immediate
|
||||||
|
};
|
||||||
|
enum class Op
|
||||||
|
{
|
||||||
|
BitwiseAnd = 0, BitwiseXor, Subtract, RevSubtract, Add, AddWithCarry, SubtractWithCarry, ReverseSubtractWithCarry,
|
||||||
|
// Compare, Test, Misc
|
||||||
|
BitwiseOr = 12, MoveAndShifts, BitwiseBitClear, BitwiseNot
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Decode() override;
|
||||||
|
void GenerateInstructionCode(InstructionBlock* instruction_block) override;
|
||||||
|
private:
|
||||||
|
Form form;
|
||||||
|
Op op;
|
||||||
|
bool set_flags;
|
||||||
|
Register rn;
|
||||||
|
Register rd;
|
||||||
|
u32 imm5;
|
||||||
|
u32 imm12;
|
||||||
|
u32 type;
|
||||||
|
Register rm;
|
||||||
|
};
|
48
src/binary_translation/Instructions/Branch.cpp
Normal file
48
src/binary_translation/Instructions/Branch.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "Branch.h"
|
||||||
|
#include "Disassembler.h"
|
||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include "ModuleGen.h"
|
||||||
|
|
||||||
|
static RegisterInstruction<Branch> register_instruction;
|
||||||
|
|
||||||
|
bool Branch::Decode()
|
||||||
|
{
|
||||||
|
// B imm, BL imm
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<3>(5), FieldDef<1>(&link), FieldDef<24>(&imm24) }))
|
||||||
|
{
|
||||||
|
form = Form::Immediate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// BLX reg
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<24>(0x12fff3), FieldDef<4>(&rm) }))
|
||||||
|
{
|
||||||
|
if (rm == Register::PC) return false;
|
||||||
|
|
||||||
|
link = true;
|
||||||
|
form = Form::Register;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Branch::GenerateInstructionCode(InstructionBlock* instruction_block)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction_block->Module()->IrBuilder();
|
||||||
|
if (link)
|
||||||
|
{
|
||||||
|
instruction_block->Write(Register::LR, ir_builder->getInt32(instruction_block->Address() + 4));
|
||||||
|
}
|
||||||
|
if (form == Form::Immediate)
|
||||||
|
{
|
||||||
|
auto pc = static_cast<s32>(imm24 << 2);
|
||||||
|
pc = pc << 6 >> 6; // Sign extend
|
||||||
|
pc += instruction_block->Address() + 8;
|
||||||
|
instruction_block->Module()->BranchWritePCConst(instruction_block, pc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto pc = instruction_block->Read(rm);
|
||||||
|
instruction_block->Write(Register::PC, pc);
|
||||||
|
instruction_block->Module()->BranchReadPC();
|
||||||
|
}
|
||||||
|
}
|
19
src/binary_translation/Instructions/Branch.h
Normal file
19
src/binary_translation/Instructions/Branch.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "Instruction.h"
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
class Branch : public Instruction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Form
|
||||||
|
{
|
||||||
|
Immediate, Register
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Decode() override;
|
||||||
|
void GenerateInstructionCode(InstructionBlock* instruction_block) override;
|
||||||
|
private:
|
||||||
|
Form form;
|
||||||
|
bool link;
|
||||||
|
u32 imm24;
|
||||||
|
Register rm;
|
||||||
|
};
|
108
src/binary_translation/Instructions/Instruction.cpp
Normal file
108
src/binary_translation/Instructions/Instruction.cpp
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
#include "Instruction.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include "ModuleGen.h"
|
||||||
|
#include "MachineState.h"
|
||||||
|
#include "BinarySearch.h"
|
||||||
|
|
||||||
|
Instruction::Instruction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction::~Instruction()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Instruction::Read(u32 instruction, u32 address)
|
||||||
|
{
|
||||||
|
this->instruction = instruction;
|
||||||
|
this->address = address;
|
||||||
|
// Call the read of derived class
|
||||||
|
if (!Decode()) return false;
|
||||||
|
|
||||||
|
if (cond == Condition::Invalid) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Instruction::GenerateCode(InstructionBlock *instruction_block)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction_block->Module()->IrBuilder();
|
||||||
|
|
||||||
|
if (cond == Condition::AL)
|
||||||
|
{
|
||||||
|
GenerateInstructionCode(instruction_block);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto pred = instruction_block->Module()->Machine()->ConditionPassed(cond);
|
||||||
|
auto passed_block = instruction_block->CreateBasicBlock("Passed");
|
||||||
|
auto not_passed_block = instruction_block->CreateBasicBlock("NotPassed");
|
||||||
|
|
||||||
|
ir_builder->CreateCondBr(pred, passed_block, not_passed_block);
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(passed_block);
|
||||||
|
GenerateInstructionCode(instruction_block);
|
||||||
|
// If the basic block is terminated there has been a jump
|
||||||
|
// If not, jump to the next not passed block (which will jump to the next instruction)
|
||||||
|
if (!ir_builder->GetInsertBlock()->getTerminator())
|
||||||
|
{
|
||||||
|
ir_builder->CreateBr(not_passed_block);
|
||||||
|
}
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(not_passed_block);
|
||||||
|
}
|
||||||
|
// If the basic block is terminated there has been a jump
|
||||||
|
// If not, jump to the next instruction
|
||||||
|
if (!ir_builder->GetInsertBlock()->getTerminator())
|
||||||
|
{
|
||||||
|
instruction_block->Module()->BranchWritePCConst(instruction_block, Address() + 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Instruction::ReadFields(const std::initializer_list<FieldDefObject> &fields)
|
||||||
|
{
|
||||||
|
size_t total_bit_count = 0;
|
||||||
|
auto current_instruction = instruction;
|
||||||
|
|
||||||
|
for (auto &field : fields)
|
||||||
|
{
|
||||||
|
total_bit_count += field.BitCount();
|
||||||
|
// Read the upper bits
|
||||||
|
auto value = current_instruction >> (32 - field.BitCount());
|
||||||
|
if (!field.Read(value)) return false;
|
||||||
|
// Remove the upper bits
|
||||||
|
current_instruction <<= field.BitCount();
|
||||||
|
}
|
||||||
|
assert(total_bit_count == 32);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction::FieldDefObject Instruction::CondDef()
|
||||||
|
{
|
||||||
|
return FieldDef<4>(&cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction::FieldDefObject::FieldDefObject(u32 bit_count, u32 const_value)
|
||||||
|
: bit_count(bit_count), const_value(const_value), constant(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction::FieldDefObject::FieldDefObject(u32 bit_count, void* field_address, WriteFunctionType write_function)
|
||||||
|
: bit_count(bit_count), field_address(field_address), write_function(write_function), constant(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Instruction::FieldDefObject::Read(u32 value) const
|
||||||
|
{
|
||||||
|
if (constant)
|
||||||
|
{
|
||||||
|
return value == const_value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write_function(value, field_address);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
111
src/binary_translation/Instructions/Instruction.h
Normal file
111
src/binary_translation/Instructions/Instruction.h
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include <initializer_list>
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
class InstructionBlock;
|
||||||
|
|
||||||
|
class Instruction
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
class FieldDefObject;
|
||||||
|
public:
|
||||||
|
Instruction();
|
||||||
|
virtual ~Instruction();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads the instruction.
|
||||||
|
* Returns true on success, or false otherwise
|
||||||
|
*/
|
||||||
|
bool Read(u32 instruction, u32 address);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generates non instruction specific code, and then calls GenerateInstructionCode
|
||||||
|
*/
|
||||||
|
void GenerateCode(InstructionBlock *instruction_block);
|
||||||
|
|
||||||
|
u32 Address() const { return address; }
|
||||||
|
protected:
|
||||||
|
/*
|
||||||
|
* Derived classes must override this, and implement it by calling ReadFields
|
||||||
|
*/
|
||||||
|
virtual bool Decode() = 0;
|
||||||
|
/*
|
||||||
|
* Generates code for the instruction into the instruction block
|
||||||
|
* Derived classes must override this
|
||||||
|
*/
|
||||||
|
virtual void GenerateInstructionCode(InstructionBlock *instruction_block) = 0;
|
||||||
|
/*
|
||||||
|
* Reads fields from the instruction
|
||||||
|
* The fields come most significant first
|
||||||
|
*/
|
||||||
|
bool ReadFields(const std::initializer_list<FieldDefObject> &fields);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Creates a field definition for a constant
|
||||||
|
*/
|
||||||
|
template<size_t BitCount>
|
||||||
|
static FieldDefObject FieldDef(u32 value);
|
||||||
|
/*
|
||||||
|
* Creates a field definition for a field
|
||||||
|
*/
|
||||||
|
template<size_t BitCount, typename Type>
|
||||||
|
static FieldDefObject FieldDef(Type *field);
|
||||||
|
/*
|
||||||
|
* Creates a field definition for the condition field
|
||||||
|
*/
|
||||||
|
FieldDefObject CondDef();
|
||||||
|
private:
|
||||||
|
/*
|
||||||
|
* Function used by FieldDefObject to write to a field
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
static void WriteFunction(u32 value, void *field_address);
|
||||||
|
|
||||||
|
// Instruction value
|
||||||
|
u32 instruction;
|
||||||
|
// Instruction address
|
||||||
|
u32 address;
|
||||||
|
|
||||||
|
Condition cond;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Object produced by FieldDef
|
||||||
|
*/
|
||||||
|
class Instruction::FieldDefObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef void(*WriteFunctionType)(u32 value, void *field_address);
|
||||||
|
public:
|
||||||
|
// Constant
|
||||||
|
FieldDefObject(u32 bit_count, u32 const_value);
|
||||||
|
// Field
|
||||||
|
FieldDefObject(u32 bit_count, void *field_address, WriteFunctionType write_function);
|
||||||
|
bool Read(u32 value) const;
|
||||||
|
u32 BitCount() const { return bit_count; }
|
||||||
|
private:
|
||||||
|
u32 bit_count;
|
||||||
|
u32 const_value;
|
||||||
|
void *field_address;
|
||||||
|
WriteFunctionType write_function;
|
||||||
|
bool constant;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t BitCount>
|
||||||
|
Instruction::FieldDefObject Instruction::FieldDef(u32 value)
|
||||||
|
{
|
||||||
|
return FieldDefObject(BitCount, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t BitCount, typename Type>
|
||||||
|
Instruction::FieldDefObject Instruction::FieldDef(Type* field)
|
||||||
|
{
|
||||||
|
return FieldDefObject(BitCount, field, &Instruction::WriteFunction<Type>);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
void Instruction::WriteFunction(u32 value, void *field_address)
|
||||||
|
{
|
||||||
|
*static_cast<Type *>(field_address) = Type(value);
|
||||||
|
}
|
107
src/binary_translation/Instructions/Ldr.cpp
Normal file
107
src/binary_translation/Instructions/Ldr.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include "Ldr.h"
|
||||||
|
#include "Disassembler.h"
|
||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include <llvm/IR/Value.h>
|
||||||
|
#include <core/loader/loader.h>
|
||||||
|
#include <core/mem_map.h>
|
||||||
|
#include "MachineState.h"
|
||||||
|
|
||||||
|
static RegisterInstruction<Ldr> register_instruction;
|
||||||
|
|
||||||
|
bool Ldr::Decode()
|
||||||
|
{
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<4>(5), FieldDef<1>(&U), FieldDef<7>(0x1f),
|
||||||
|
FieldDef<4>(&rt), FieldDef<12>(&imm12)}))
|
||||||
|
{
|
||||||
|
form = Form::PC;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<3>(2), FieldDef<1>(&P), FieldDef<1>(&U), FieldDef<1>(0), FieldDef<1>(&W), FieldDef<1>(1), FieldDef<4>(&rn),
|
||||||
|
FieldDef<4>(&rt), FieldDef<12>(&imm12) }))
|
||||||
|
{
|
||||||
|
form = Form::Reg;
|
||||||
|
|
||||||
|
if (!P && W) return false; // SEE LDRT;
|
||||||
|
//if (rn == Register::SP && !P && U && !W && imm12 == 4) return false; // SEE POP;
|
||||||
|
if ((!P || W) && rn == rt) return false; // UNPREDICTABLE;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<6>(0x22), FieldDef<1>(&W), FieldDef<1>(1), FieldDef<4>(&rn),
|
||||||
|
FieldDef<16>(®ister_list) }))
|
||||||
|
{
|
||||||
|
form = Form::MultiReg;
|
||||||
|
|
||||||
|
//if (W && rn == Register::SP && register_list.size() > 1) return false; // SEE POP (ARM);
|
||||||
|
if (rn == Register::PC || register_list.size() < 1) return false; // UNPREDICTABLE;
|
||||||
|
if (W && register_list[(u32)rn]) return false; // UNPREDICTABLE;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ldr::GenerateInstructionCode(InstructionBlock* instruction_block)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction_block->IrBuilder();
|
||||||
|
|
||||||
|
if (form != Form::MultiReg)
|
||||||
|
{
|
||||||
|
llvm::Value *address = nullptr;
|
||||||
|
llvm::Value *value = nullptr;
|
||||||
|
|
||||||
|
auto add = (bool)U;
|
||||||
|
|
||||||
|
if (form == Form::PC)
|
||||||
|
{
|
||||||
|
auto base = instruction_block->Address() + 8;
|
||||||
|
auto constAddress = add ? base + imm12 : base - imm12;
|
||||||
|
auto constAddressEnd = constAddress + 4;
|
||||||
|
// If the value is read only, inline it
|
||||||
|
if (constAddress >= Loader::ROMCodeStart && constAddressEnd <= (Loader::ROMCodeStart + Loader::ROMCodeSize) ||
|
||||||
|
constAddress >= Loader::ROMReadOnlyDataStart && constAddressEnd <= (Loader::ROMReadOnlyDataStart + Loader::ROMReadOnlyDataSize))
|
||||||
|
{
|
||||||
|
value = ir_builder->getInt32(Memory::Read32(constAddress));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
address = ir_builder->getInt32(constAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto index = P == 1;
|
||||||
|
auto wback = P == 0 || W == 1;
|
||||||
|
auto source_register = instruction_block->Read(rn);
|
||||||
|
auto imm32 = ir_builder->getInt32(add ? imm12 : -imm12);
|
||||||
|
|
||||||
|
auto offset_address = ir_builder->CreateAdd(source_register, imm32);
|
||||||
|
address = index ? offset_address : source_register;
|
||||||
|
if (wback)
|
||||||
|
instruction_block->Write(rn, offset_address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value) value = instruction_block->Module()->Machine()->ReadMemory32(address);
|
||||||
|
instruction_block->Write(rt, value);
|
||||||
|
|
||||||
|
if (rt == Register::PC)
|
||||||
|
instruction_block->Module()->BranchReadPC();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto wback = (bool)W;
|
||||||
|
auto address = instruction_block->Read(rn);
|
||||||
|
for (auto i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
if (!register_list[i]) continue;
|
||||||
|
instruction_block->Write((Register)i, instruction_block->Module()->Machine()->ReadMemory32(address));
|
||||||
|
address = ir_builder->CreateAdd(address, ir_builder->getInt32(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wback)
|
||||||
|
instruction_block->Write(rn, address);
|
||||||
|
|
||||||
|
if (register_list[15])
|
||||||
|
instruction_block->Module()->BranchReadPC();
|
||||||
|
}
|
||||||
|
}
|
25
src/binary_translation/Instructions/Ldr.h
Normal file
25
src/binary_translation/Instructions/Ldr.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "Instruction.h"
|
||||||
|
#include "Types.h"
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
class Ldr : public Instruction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Form
|
||||||
|
{
|
||||||
|
PC, Reg, MultiReg
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Decode() override;
|
||||||
|
void GenerateInstructionCode(InstructionBlock* instruction_block) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Form form;
|
||||||
|
bool U;
|
||||||
|
Register rt;
|
||||||
|
u32 imm12;
|
||||||
|
bool P;
|
||||||
|
bool W;
|
||||||
|
Register rn;
|
||||||
|
std::bitset<16> register_list;
|
||||||
|
};
|
101
src/binary_translation/Instructions/MovShift.cpp
Normal file
101
src/binary_translation/Instructions/MovShift.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include "MovShift.h"
|
||||||
|
#include "Disassembler.h"
|
||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include "ModuleGen.h"
|
||||||
|
#include "ARMFuncs.h"
|
||||||
|
#include <binary_translation/BinarySearch.h>
|
||||||
|
|
||||||
|
static RegisterInstruction<MovShift> register_instruction;
|
||||||
|
|
||||||
|
bool MovShift::Decode()
|
||||||
|
{
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<3>(0), FieldDef<4>(13), FieldDef<1>(&s), FieldDef<4>(0),
|
||||||
|
FieldDef<4>(&rd), FieldDef<5>(&imm5), FieldDef<2>(&op2), FieldDef<1>(0), FieldDef<4>(&rm) }))
|
||||||
|
{
|
||||||
|
form = Form::Register;
|
||||||
|
if (rm == Register::PC) return false;
|
||||||
|
if (rd == Register::PC && s) return false; // SEE SUBS PC, LR and related instructions;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<7>(0x1d), FieldDef<1>(&s), FieldDef<4>(0),
|
||||||
|
FieldDef<4>(&rd), FieldDef<12>(&imm12) }))
|
||||||
|
{
|
||||||
|
form = Form::ImmediateA1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<8>(0x30), FieldDef<4>(&imm4),
|
||||||
|
FieldDef<4>(&rd), FieldDef<12>(&imm12) }))
|
||||||
|
{
|
||||||
|
s = false;
|
||||||
|
form = Form::ImmediateA2;
|
||||||
|
if (rd == Register::PC) return false; // UNPREDICTIBLE
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MovShift::GenerateInstructionCode(InstructionBlock* instruction_block)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction_block->IrBuilder();
|
||||||
|
|
||||||
|
auto carry_in = instruction_block->Read(Register::C);
|
||||||
|
ARMFuncs::ResultCarry result = {};
|
||||||
|
|
||||||
|
switch (form)
|
||||||
|
{
|
||||||
|
case Form::Register:
|
||||||
|
result = { instruction_block->Read(rm), carry_in };
|
||||||
|
switch (op2)
|
||||||
|
{
|
||||||
|
case Op2Type::MoveAndLSL:
|
||||||
|
if (imm5 != 0)
|
||||||
|
{
|
||||||
|
result = ARMFuncs::Shift_C(instruction_block, result.result, ARMFuncs::SRType::LSL,
|
||||||
|
ARMFuncs::DecodeImmShift(instruction_block, 0, imm5).amount, result.carry);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Op2Type::LSR:
|
||||||
|
result = ARMFuncs::Shift_C(instruction_block, result.result, ARMFuncs::SRType::LSR,
|
||||||
|
ARMFuncs::DecodeImmShift(instruction_block, 1, imm5).amount, result.carry);
|
||||||
|
break;
|
||||||
|
case Op2Type::ASR:
|
||||||
|
result = ARMFuncs::Shift_C(instruction_block, result.result, ARMFuncs::SRType::ASR,
|
||||||
|
ARMFuncs::DecodeImmShift(instruction_block, 2, imm5).amount, result.carry);
|
||||||
|
break;
|
||||||
|
case Op2Type::RRXAndROR:
|
||||||
|
if (imm5 == 0)
|
||||||
|
{
|
||||||
|
result = ARMFuncs::Shift_C(instruction_block, result.result, ARMFuncs::SRType::RRX,
|
||||||
|
ir_builder->getInt32(1), result.carry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = ARMFuncs::Shift_C(instruction_block, result.result, ARMFuncs::SRType::ROR,
|
||||||
|
ARMFuncs::DecodeImmShift(instruction_block, 3, imm5).amount, result.carry);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Form::ImmediateA1:
|
||||||
|
result = ARMFuncs::ARMExpandImm_C(instruction_block, imm12, carry_in);
|
||||||
|
break;
|
||||||
|
case Form::ImmediateA2:
|
||||||
|
result.result = ir_builder->getInt32((imm4 << 12) | imm12);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction_block->Write(rd, result.result);
|
||||||
|
|
||||||
|
if (s)
|
||||||
|
{
|
||||||
|
instruction_block->Write(Register::N, ir_builder->CreateTrunc(ir_builder->CreateLShr(result.result, 31), ir_builder->getInt1Ty()));
|
||||||
|
instruction_block->Write(Register::Z, ir_builder->CreateICmpEQ(result.result, ir_builder->getInt32(0)));
|
||||||
|
if (result.carry != carry_in)
|
||||||
|
instruction_block->Write(Register::C, result.carry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rd == Register::PC)
|
||||||
|
{
|
||||||
|
instruction_block->Module()->BranchReadPC();
|
||||||
|
}
|
||||||
|
}
|
33
src/binary_translation/Instructions/MovShift.h
Normal file
33
src/binary_translation/Instructions/MovShift.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "Instruction.h"
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data processing instructions
|
||||||
|
* ARMv7-A 5.2.1 (register), 5.2.2 (register-shifted register, 5.2.3 (immediate)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class MovShift : public Instruction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Op2Type
|
||||||
|
{
|
||||||
|
MoveAndLSL, LSR, ASR, RRXAndROR
|
||||||
|
};
|
||||||
|
enum class Form
|
||||||
|
{
|
||||||
|
Register, ImmediateA1, ImmediateA2
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Decode() override;
|
||||||
|
void GenerateInstructionCode(InstructionBlock* instruction_block) override;
|
||||||
|
private:
|
||||||
|
Form form;
|
||||||
|
bool s;
|
||||||
|
Register rn;
|
||||||
|
Register rd;
|
||||||
|
Register rm;
|
||||||
|
u32 imm12;
|
||||||
|
u32 imm5;
|
||||||
|
u32 imm4;
|
||||||
|
Op2Type op2;
|
||||||
|
};
|
70
src/binary_translation/Instructions/Str.cpp
Normal file
70
src/binary_translation/Instructions/Str.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include "Str.h"
|
||||||
|
#include "Disassembler.h"
|
||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include <llvm/IR/Value.h>
|
||||||
|
#include <core/loader/loader.h>
|
||||||
|
#include <core/mem_map.h>
|
||||||
|
#include "MachineState.h"
|
||||||
|
|
||||||
|
static RegisterInstruction<Str> register_instruction;
|
||||||
|
|
||||||
|
bool Str::Decode()
|
||||||
|
{
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<3>(2), FieldDef<1>(&P), FieldDef<1>(&U), FieldDef<1>(0), FieldDef<1>(&W), FieldDef<1>(0), FieldDef<4>(&rn),
|
||||||
|
FieldDef<4>(&rt), FieldDef<12>(&imm12) }))
|
||||||
|
{
|
||||||
|
form = Form::Immediate;
|
||||||
|
|
||||||
|
if (!P && W) return false; // SEE LDRT;
|
||||||
|
if ((!P || W) && (rn == rt || rn == Register::PC)) return false; // UNPREDICTABLE;
|
||||||
|
if (rn == Register::PC) return false; // Currently unimplemented
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ReadFields({ CondDef(), FieldDef<6>(0x24), FieldDef<1>(&W), FieldDef<1>(0), FieldDef<4>(&rn),
|
||||||
|
FieldDef<16>(®ister_list) }))
|
||||||
|
{
|
||||||
|
form = Form::MultiReg;
|
||||||
|
|
||||||
|
if (rn == Register::PC || register_list.size() < 1) return false; // UNPREDICTABLE;
|
||||||
|
if (register_list[(int)Register::PC]) return false; // Currently unimplemented
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Str::GenerateInstructionCode(InstructionBlock* instruction_block)
|
||||||
|
{
|
||||||
|
auto ir_builder = instruction_block->IrBuilder();
|
||||||
|
|
||||||
|
if (form == Form::Immediate)
|
||||||
|
{
|
||||||
|
auto add = U == 1;
|
||||||
|
auto index = P == 1;
|
||||||
|
auto wback = P == 0 || W == 1;
|
||||||
|
auto source_register = instruction_block->Read(rn);
|
||||||
|
auto imm32 = ir_builder->getInt32(add ? imm12 : -imm12);
|
||||||
|
|
||||||
|
auto offset_address = ir_builder->CreateAdd(source_register, imm32);
|
||||||
|
auto address = index ? offset_address : source_register;
|
||||||
|
if (wback)
|
||||||
|
instruction_block->Write(rn, offset_address);
|
||||||
|
instruction_block->Module()->Machine()->WriteMemory32(address, instruction_block->Read(rt));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto wback = W == 1;
|
||||||
|
auto write_back_address = ir_builder->CreateSub(instruction_block->Read(rn), ir_builder->getInt32(4 * register_list.count()));
|
||||||
|
auto address = write_back_address;
|
||||||
|
for (auto i = 0; i < 16; ++i)
|
||||||
|
{
|
||||||
|
if (!register_list[i]) continue;
|
||||||
|
instruction_block->Module()->Machine()->WriteMemory32(address, instruction_block->Read((Register)i));
|
||||||
|
address = ir_builder->CreateAdd(address, ir_builder->getInt32(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wback)
|
||||||
|
instruction_block->Write(rn, write_back_address);
|
||||||
|
}
|
||||||
|
}
|
25
src/binary_translation/Instructions/Str.h
Normal file
25
src/binary_translation/Instructions/Str.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "Instruction.h"
|
||||||
|
#include "Types.h"
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
class Str : public Instruction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class Form
|
||||||
|
{
|
||||||
|
Immediate, Reg, MultiReg
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Decode() override;
|
||||||
|
void GenerateInstructionCode(InstructionBlock* instruction_block) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Form form;
|
||||||
|
bool U;
|
||||||
|
Register rt;
|
||||||
|
u32 imm12;
|
||||||
|
bool P;
|
||||||
|
bool W;
|
||||||
|
Register rn;
|
||||||
|
std::bitset<16> register_list;
|
||||||
|
};
|
19
src/binary_translation/Instructions/Types.h
Normal file
19
src/binary_translation/Instructions/Types.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A register in a broad sense: R0-R15, and flags
|
||||||
|
*/
|
||||||
|
enum class Register
|
||||||
|
{
|
||||||
|
R0, R1, R2, R3, R4, R5, R6, R7,
|
||||||
|
R8, R9, R10, R11, R12, SP, LR, PC,
|
||||||
|
N, Z, C, V,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
static const size_t RegisterCount = static_cast<size_t>(Register::Count);
|
||||||
|
|
||||||
|
enum class Condition
|
||||||
|
{
|
||||||
|
EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, Invalid
|
||||||
|
};
|
169
src/binary_translation/MachineState.cpp
Normal file
169
src/binary_translation/MachineState.cpp
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
#include "MachineState.h"
|
||||||
|
#include "ModuleGen.h"
|
||||||
|
#include "Instructions/Types.h"
|
||||||
|
#include <llvm/IR/GlobalValue.h>
|
||||||
|
#include <llvm/IR/Constants.h>
|
||||||
|
#include <llvm/IR/LLVMContext.h>
|
||||||
|
#include <llvm/IR/GlobalVariable.h>
|
||||||
|
#include "TBAA.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
MachineState::MachineState(ModuleGen *module) : module(module)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void MachineState::GenerateGlobals()
|
||||||
|
{
|
||||||
|
auto ir_builder = module->IrBuilder();
|
||||||
|
|
||||||
|
#if ARCHITECTURE_x86_64
|
||||||
|
auto registers_global_initializer = ConstantPointerNull::get(IntegerType::getInt64PtrTy(getGlobalContext()));
|
||||||
|
#else
|
||||||
|
auto registers_global_initializer = ConstantPointerNull::get(IntegerType::getInt32PtrTy(getGlobalContext()));
|
||||||
|
#endif
|
||||||
|
registers_global = new GlobalVariable(*module->Module(), registers_global_initializer->getType(),
|
||||||
|
false, GlobalValue::ExternalLinkage, registers_global_initializer, "Registers");
|
||||||
|
|
||||||
|
// Flags is stored internally as i1* indexed in multiples of 4
|
||||||
|
auto flags_global_initializer = ConstantPointerNull::get(IntegerType::getInt1PtrTy(getGlobalContext()));
|
||||||
|
flags_global = new GlobalVariable(*module->Module(), flags_global_initializer->getType(),
|
||||||
|
false, GlobalValue::ExternalLinkage, flags_global_initializer, "Flags");
|
||||||
|
|
||||||
|
|
||||||
|
auto memory_read_32_signature = FunctionType::get(ir_builder->getInt32Ty(), ir_builder->getInt32Ty(), false);
|
||||||
|
auto memory_read_32_ptr = PointerType::get(memory_read_32_signature, 0);
|
||||||
|
auto memory_read_32_initializer = ConstantPointerNull::get(memory_read_32_ptr);
|
||||||
|
memory_read_32_global = new GlobalVariable(*module->Module(), memory_read_32_ptr,
|
||||||
|
false, GlobalValue::ExternalLinkage, memory_read_32_initializer, "Memory::Read32");
|
||||||
|
|
||||||
|
|
||||||
|
llvm::Type *memory_write_32_args[] = { ir_builder->getInt32Ty(), ir_builder->getInt32Ty() };
|
||||||
|
auto memory_write_32_signature = FunctionType::get(ir_builder->getVoidTy(), memory_write_32_args, false);
|
||||||
|
auto memory_write_32_ptr = PointerType::get(memory_write_32_signature, 0);
|
||||||
|
auto memory_write_32_initializer = ConstantPointerNull::get(memory_write_32_ptr);
|
||||||
|
memory_write_32_global = new GlobalVariable(*module->Module(), memory_write_32_ptr,
|
||||||
|
false, GlobalValue::ExternalLinkage, memory_write_32_initializer, "Memory::Write32");
|
||||||
|
}
|
||||||
|
|
||||||
|
Value *MachineState::GetRegisterPtr(Register reg)
|
||||||
|
{
|
||||||
|
Value *global;
|
||||||
|
unsigned index;
|
||||||
|
if (reg <= Register::PC)
|
||||||
|
{
|
||||||
|
global = registers_global;
|
||||||
|
index = static_cast<unsigned>(reg)-static_cast<unsigned>(Register::R0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
global = flags_global;
|
||||||
|
index = (static_cast<unsigned>(reg)-static_cast<unsigned>(Register::N)) * 4;
|
||||||
|
}
|
||||||
|
auto base = module->IrBuilder()->CreateAlignedLoad(global, 4);
|
||||||
|
module->GetTBAA()->TagConst(base);
|
||||||
|
#if ARCHITECTURE_x86_64
|
||||||
|
return module->IrBuilder()->CreateConstInBoundsGEP1_64(base, index);
|
||||||
|
#else
|
||||||
|
return module->IrBuilder()->CreateConstInBoundsGEP1_32(base->getType(),base, index);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Value* MachineState::ReadRegiser(Register reg, bool allow_pc,bool full_length)
|
||||||
|
{
|
||||||
|
assert(allow_pc || reg != Register::PC);
|
||||||
|
#if ARCHITECTURE_x86_64
|
||||||
|
Value* load;
|
||||||
|
if (reg == Register::PC&&full_length) {
|
||||||
|
load = module->IrBuilder()->CreateAlignedLoad(GetRegisterPtr(reg), 4);
|
||||||
|
module->GetTBAA()->TagRegister(static_cast<Instruction*>(load), reg);
|
||||||
|
}
|
||||||
|
else if(reg <= Register::PC){
|
||||||
|
auto loadinst = module->IrBuilder()->CreateAlignedLoad(GetRegisterPtr(reg), 4);
|
||||||
|
module->GetTBAA()->TagRegister(loadinst, reg);
|
||||||
|
load = module->IrBuilder()->CreateIntCast(loadinst, module->IrBuilder()->getInt32Ty(),false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto loadinst = module->IrBuilder()->CreateAlignedLoad(GetRegisterPtr(reg), 4);
|
||||||
|
module->GetTBAA()->TagRegister(loadinst, reg);
|
||||||
|
load = module->IrBuilder()->CreateIntCast(loadinst, module->IrBuilder()->getInt1Ty(), false);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
auto load = module->IrBuilder()->CreateAlignedLoad(GetRegisterPtr(reg), 4);
|
||||||
|
module->GetTBAA()->TagRegister(load, reg);
|
||||||
|
#endif
|
||||||
|
return load;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value* MachineState::WriteRegiser(Register reg, Value *value,bool full_length)
|
||||||
|
{
|
||||||
|
#if ARCHITECTURE_x86_64
|
||||||
|
Instruction* store;
|
||||||
|
if (reg == Register::PC && full_length)
|
||||||
|
store = module->IrBuilder()->CreateAlignedStore(value, GetRegisterPtr(reg), 4);
|
||||||
|
else if(reg <= Register::PC)
|
||||||
|
store = module->IrBuilder()->CreateAlignedStore(module->IrBuilder()->CreateIntCast(value, module->IrBuilder()->getInt64Ty(), false), GetRegisterPtr(reg), 4);
|
||||||
|
else
|
||||||
|
store = module->IrBuilder()->CreateAlignedStore(module->IrBuilder()->CreateIntCast(value, module->IrBuilder()->getInt1Ty(), false), GetRegisterPtr(reg), 4);
|
||||||
|
#else
|
||||||
|
auto store = module->IrBuilder()->CreateAlignedStore(value, GetRegisterPtr(reg), 4);
|
||||||
|
#endif
|
||||||
|
module->GetTBAA()->TagRegister(store, reg);
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value* MachineState::ConditionPassed(Condition cond)
|
||||||
|
{
|
||||||
|
auto ir_builder = module->IrBuilder();
|
||||||
|
Value *pred = nullptr;
|
||||||
|
auto not = false;
|
||||||
|
switch (cond)
|
||||||
|
{
|
||||||
|
case Condition::NE: case Condition::CC: case Condition::PL: case Condition::VC:
|
||||||
|
case Condition::LS: case Condition::LT: case Condition::LE:
|
||||||
|
not = true;
|
||||||
|
cond = (Condition)((int)cond - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cond)
|
||||||
|
{
|
||||||
|
case Condition::EQ: pred = ReadRegiser(Register::Z); break;
|
||||||
|
case Condition::CS: pred = ReadRegiser(Register::C); break;
|
||||||
|
case Condition::MI: pred = ReadRegiser(Register::N); break;
|
||||||
|
case Condition::VS: pred = ReadRegiser(Register::V); break;
|
||||||
|
case Condition::HI: pred = ir_builder->CreateAnd(ReadRegiser(Register::C), ir_builder->CreateNot(ReadRegiser(Register::Z))); break;
|
||||||
|
case Condition::GE: pred = ir_builder->CreateICmpEQ(ReadRegiser(Register::N), ReadRegiser(Register::V)); break;
|
||||||
|
case Condition::GT: pred = ir_builder->CreateAnd(ir_builder->CreateNot(ReadRegiser(Register::Z)),
|
||||||
|
ir_builder->CreateICmpEQ(ReadRegiser(Register::N), ReadRegiser(Register::V))); break;
|
||||||
|
case Condition::AL: pred = ir_builder->getInt1(true);
|
||||||
|
default: assert(false, "Invalid condition");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not) pred = ir_builder->CreateNot(pred);
|
||||||
|
return pred;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* MachineState::ReadMemory32(llvm::Value* address)
|
||||||
|
{
|
||||||
|
auto ir_builder = module->IrBuilder();
|
||||||
|
|
||||||
|
auto memory_read_32 = ir_builder->CreateLoad(memory_read_32_global);
|
||||||
|
module->GetTBAA()->TagConst(memory_read_32);
|
||||||
|
auto call = ir_builder->CreateCall(memory_read_32,address);
|
||||||
|
call->setOnlyReadsMemory();
|
||||||
|
module->GetTBAA()->TagMemory(call);
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* MachineState::WriteMemory32(llvm::Value* address, llvm::Value* value)
|
||||||
|
{
|
||||||
|
auto ir_builder = module->IrBuilder();
|
||||||
|
|
||||||
|
auto memory_write_32 = ir_builder->CreateLoad(memory_write_32_global);
|
||||||
|
module->GetTBAA()->TagConst(memory_write_32);
|
||||||
|
|
||||||
|
auto call = ir_builder->CreateCall(memory_write_32, llvm::ArrayRef<llvm::Value*>(std::vector<llvm::Value*>{ address, value }));
|
||||||
|
module->GetTBAA()->TagMemory(call);
|
||||||
|
return value;
|
||||||
|
}
|
59
src/binary_translation/MachineState.h
Normal file
59
src/binary_translation/MachineState.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
enum class Condition;
|
||||||
|
enum class Register;
|
||||||
|
class ModuleGen;
|
||||||
|
|
||||||
|
namespace llvm
|
||||||
|
{
|
||||||
|
class Value;
|
||||||
|
class Instruction;
|
||||||
|
class GlobalVariable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Contains all the machine state:
|
||||||
|
Registers, Flags, Memory
|
||||||
|
*/
|
||||||
|
class MachineState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MachineState(ModuleGen *module);
|
||||||
|
|
||||||
|
void GenerateGlobals();
|
||||||
|
// allow_pc exists because most of the times reading the PC is not what the instruction meant
|
||||||
|
llvm::Value *ReadRegiser(Register reg, bool allow_pc = false,bool full_length = false);
|
||||||
|
llvm::Value *WriteRegiser(Register reg, llvm::Value *value, bool full_length = false);
|
||||||
|
llvm::Value* ConditionPassed(Condition cond);
|
||||||
|
llvm::Value* ReadMemory32(llvm::Value* address);
|
||||||
|
llvm::Value* WriteMemory32(llvm::Value* address, llvm::Value* value);
|
||||||
|
private:
|
||||||
|
// Returns the address of a register or a flag
|
||||||
|
llvm::Value *GetRegisterPtr(Register reg);
|
||||||
|
|
||||||
|
ModuleGen *module;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* u32 *Registers;
|
||||||
|
* The registers of the cpu
|
||||||
|
*/
|
||||||
|
llvm::GlobalVariable *registers_global;
|
||||||
|
/*
|
||||||
|
* u32 *Flags;
|
||||||
|
* The flags of the cpu
|
||||||
|
* Orderered N, Z, C, V
|
||||||
|
*/
|
||||||
|
llvm::GlobalVariable *flags_global;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* u32 (u32) Memory::Read32
|
||||||
|
* Reads the memory at address
|
||||||
|
*/
|
||||||
|
llvm::GlobalVariable *memory_read_32_global;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* void (u32, u32) Memory::Write32
|
||||||
|
* Writes the memory at address
|
||||||
|
*/
|
||||||
|
llvm::GlobalVariable *memory_write_32_global;
|
||||||
|
};
|
308
src/binary_translation/ModuleGen.cpp
Normal file
308
src/binary_translation/ModuleGen.cpp
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
#include "ModuleGen.h"
|
||||||
|
#include "Disassembler.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "core/mem_map.h"
|
||||||
|
#include "Instructions/Instruction.h"
|
||||||
|
#include "Instructions/Types.h"
|
||||||
|
#include "InstructionBlock.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include <llvm/IR/Function.h>
|
||||||
|
#include <llvm/IR/GlobalVariable.h>
|
||||||
|
#include <llvm/ADT/STLExtras.h>
|
||||||
|
#include <stack>
|
||||||
|
#include "MachineState.h"
|
||||||
|
#include "TBAA.h"
|
||||||
|
#include "BlockColors.h"
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
ModuleGen::ModuleGen(llvm::Module* module, bool verify)
|
||||||
|
: module(module),
|
||||||
|
verify(verify)
|
||||||
|
{
|
||||||
|
ir_builder = make_unique<IRBuilder<>>(getGlobalContext());
|
||||||
|
machine = make_unique<MachineState>(this);
|
||||||
|
tbaa = make_unique<TBAA>();
|
||||||
|
block_colors = make_unique<BlockColors>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModuleGen::~ModuleGen()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::Run()
|
||||||
|
{
|
||||||
|
tbaa->GenerateTags();
|
||||||
|
GenerateGlobals();
|
||||||
|
|
||||||
|
DecodeInstructions();
|
||||||
|
GenerateInstructionsEntry();
|
||||||
|
|
||||||
|
GenerateCanRunFunction();
|
||||||
|
GenerateRunFunction();
|
||||||
|
GenerateGetBlockAddressFunction();
|
||||||
|
|
||||||
|
GenerateInstructionsCode();
|
||||||
|
|
||||||
|
ColorBlocks();
|
||||||
|
GenerateBlockAddressArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::GenerateIncInstructionCount()
|
||||||
|
{
|
||||||
|
auto load = ir_builder->CreateLoad(instruction_count);
|
||||||
|
auto inc = ir_builder->CreateAdd(load, ir_builder->getInt32(1));
|
||||||
|
auto store = ir_builder->CreateStore(inc, instruction_count);
|
||||||
|
tbaa->TagInstructionCount(load);
|
||||||
|
tbaa->TagInstructionCount(store);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::BranchReadPC()
|
||||||
|
{
|
||||||
|
if (verify)
|
||||||
|
{
|
||||||
|
ir_builder->CreateRetVoid();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto call = ir_builder->CreateCall(run_function);
|
||||||
|
call->setTailCall();
|
||||||
|
ir_builder->CreateRetVoid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::BranchWritePCConst(InstructionBlock *current, u64 pc)
|
||||||
|
{
|
||||||
|
if (verify)
|
||||||
|
{
|
||||||
|
// Just write PC and exit on verify
|
||||||
|
machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc));
|
||||||
|
ir_builder->CreateRetVoid();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto i = instruction_blocks_by_pc.find(pc);
|
||||||
|
if (i != instruction_blocks_by_pc.end())
|
||||||
|
{
|
||||||
|
// Found instruction, jump to it
|
||||||
|
ir_builder->CreateBr(i->second->GetEntryBasicBlock());
|
||||||
|
InstructionBlock::Link(i->second, current);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Didn't find instruction, write PC and exit
|
||||||
|
machine->WriteRegiser(Register::PC, ir_builder->getInt32(pc));
|
||||||
|
ir_builder->CreateRetVoid();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::GenerateGlobals()
|
||||||
|
{
|
||||||
|
machine->GenerateGlobals();
|
||||||
|
|
||||||
|
auto function_pointer = PointerType::get(block_colors->GetFunctionType(), 0);
|
||||||
|
block_address_type = StructType::get(function_pointer, ir_builder->getInt32Ty(), nullptr);
|
||||||
|
block_address_not_present = ConstantStruct::get(block_address_type, ConstantPointerNull::get(function_pointer), ir_builder->getInt32(0), nullptr);
|
||||||
|
|
||||||
|
#if ARCHITECTURE_x86_64
|
||||||
|
auto get_block_address_function_type = FunctionType::get(block_address_type, ir_builder->getInt64Ty(), false);
|
||||||
|
#else
|
||||||
|
auto get_block_address_function_type = FunctionType::get(block_address_type, ir_builder->getInt32Ty(), false);
|
||||||
|
#endif
|
||||||
|
get_block_address_function = Function::Create(get_block_address_function_type, GlobalValue::PrivateLinkage, "GetBlockAddress", module);
|
||||||
|
|
||||||
|
auto can_run_function_type = FunctionType::get(ir_builder->getInt1Ty(), false);
|
||||||
|
can_run_function = Function::Create(can_run_function_type, GlobalValue::ExternalLinkage, "CanRun", module);
|
||||||
|
|
||||||
|
auto run_function_type = FunctionType::get(ir_builder->getVoidTy(), false);
|
||||||
|
run_function = Function::Create(run_function_type, GlobalValue::ExternalLinkage, "Run", module);
|
||||||
|
|
||||||
|
block_address_array_base = Loader::ROMCodeStart / 4;
|
||||||
|
block_address_array_size = Loader::ROMCodeSize / 4;
|
||||||
|
|
||||||
|
block_address_array_type = ArrayType::get(block_address_type, block_address_array_size);
|
||||||
|
block_address_array = new GlobalVariable(*module, block_address_array_type, true, GlobalValue::ExternalLinkage, nullptr, "BlockAddressArray");
|
||||||
|
|
||||||
|
// bool Verify - contains the value of verify for citra usage
|
||||||
|
new GlobalVariable(*module, ir_builder->getInt1Ty(), true, GlobalValue::ExternalLinkage, ir_builder->getInt1(verify), "Verify");
|
||||||
|
|
||||||
|
instruction_count = new GlobalVariable(*Module(), ir_builder->getInt32Ty(), false, GlobalValue::ExternalLinkage,
|
||||||
|
ir_builder->getInt32(0), "InstructionCount");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::GenerateBlockAddressArray()
|
||||||
|
{
|
||||||
|
auto local_block_address_array_values = std::make_unique<Constant*[]>(block_address_array_size);
|
||||||
|
std::fill(
|
||||||
|
local_block_address_array_values.get(),
|
||||||
|
local_block_address_array_values.get() + block_address_array_size,
|
||||||
|
block_address_not_present);
|
||||||
|
|
||||||
|
/*for (auto i = 0; i < instruction_blocks.size(); ++i)
|
||||||
|
{
|
||||||
|
auto &block = instruction_blocks[i];
|
||||||
|
auto entry_basic_block = block->GetEntryBasicBlock();
|
||||||
|
auto index = block->Address() / 4 - block_address_array_base;
|
||||||
|
auto color_index = 0;
|
||||||
|
local_block_address_array_values[index] = BConst
|
||||||
|
}*/
|
||||||
|
for (auto color = 0; color < block_colors->GetColorCount(); ++color)
|
||||||
|
{
|
||||||
|
auto function = block_colors->GetColorFunction(color);
|
||||||
|
for (auto i = 0; i < block_colors->GetColorInstructionCount(color); ++i)
|
||||||
|
{
|
||||||
|
auto block = block_colors->GetColorInstruction(color, i);
|
||||||
|
auto index = block->Address() / 4 - block_address_array_base;
|
||||||
|
auto value = ConstantStruct::get(block_address_type, function, ir_builder->getInt32(i), nullptr);
|
||||||
|
local_block_address_array_values[index] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto local_block_address_array_values_ref = ArrayRef<Constant*>(local_block_address_array_values.get(), block_address_array_size);
|
||||||
|
auto local_blocks_address_array = ConstantArray::get(block_address_array_type, local_block_address_array_values_ref);
|
||||||
|
block_address_array->setInitializer(local_blocks_address_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::GenerateGetBlockAddressFunction()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
entry_basic_block:
|
||||||
|
auto index = (pc - block_address_array_base) / 4;
|
||||||
|
if(((pc & 3) == 0) && index < block_address_array_size)
|
||||||
|
{
|
||||||
|
index_in_bounds_basic_block:
|
||||||
|
return block_address_array[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index_out_of_bounds_basic_block:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
auto pc = &*get_block_address_function->arg_begin();
|
||||||
|
auto entry_basic_block = BasicBlock::Create(getGlobalContext(), "Entry", get_block_address_function);
|
||||||
|
auto index_in_bounds_basic_block = BasicBlock::Create(getGlobalContext(), "IndexInBounds", get_block_address_function);
|
||||||
|
auto index_out_of_bounds_basic_block = BasicBlock::Create(getGlobalContext(), "IndexOutOfBounds", get_block_address_function);
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(entry_basic_block);
|
||||||
|
#define _BITAPPEND(a,b) a ## b
|
||||||
|
#if ARCHITECTURE_x86_64
|
||||||
|
#define BITAPPEND(c) _BITAPPEND(c,64)
|
||||||
|
#else
|
||||||
|
#define BITAPPEND(c) _BITAPPEND(c,32)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto index = ir_builder->CreateUDiv(pc, ir_builder->BITAPPEND(getInt)(4), "", true);
|
||||||
|
index = ir_builder->CreateSub(index, ir_builder->BITAPPEND(getInt)(block_address_array_base));
|
||||||
|
auto in_bounds_pred = ir_builder->CreateICmpULT(index, ir_builder->BITAPPEND(getInt)(block_address_array_size));
|
||||||
|
auto arm_pred = ir_builder->CreateICmpEQ(ir_builder->CreateAnd(pc, 3), ir_builder->BITAPPEND(getInt)(0));
|
||||||
|
auto pred = ir_builder->CreateAnd(in_bounds_pred, arm_pred);
|
||||||
|
ir_builder->CreateCondBr(pred, index_in_bounds_basic_block, index_out_of_bounds_basic_block);
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(index_in_bounds_basic_block);
|
||||||
|
Value *gep_values[] = { ir_builder->BITAPPEND(getInt)(0), index };
|
||||||
|
auto block_address = ir_builder->CreateLoad(ir_builder->CreateInBoundsGEP(block_address_array, gep_values));
|
||||||
|
tbaa->TagConst(block_address);
|
||||||
|
ir_builder->CreateRet(block_address);
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(index_out_of_bounds_basic_block);
|
||||||
|
ir_builder->CreateRet(block_address_not_present);
|
||||||
|
#undef BITAPPEND(a)
|
||||||
|
#undef _BITAPPEND(a,b)
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::GenerateCanRunFunction()
|
||||||
|
{
|
||||||
|
// return GetBlockAddress(Read(PC)).function != nullptr;
|
||||||
|
auto basic_block = BasicBlock::Create(getGlobalContext(), "Entry", can_run_function);
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(basic_block);
|
||||||
|
auto block_address = ir_builder->CreateCall(get_block_address_function, machine->ReadRegiser(Register::PC, true,true));
|
||||||
|
auto function = ir_builder->CreateExtractValue(block_address, 0);
|
||||||
|
ir_builder->CreateRet(ir_builder->CreateICmpNE(function,
|
||||||
|
ConstantPointerNull::get(cast<PointerType>(function->getType()))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::GenerateRunFunction()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
run_function_entry:
|
||||||
|
auto block = GetBlockAddress(Read(PC))
|
||||||
|
if(block_address != nullptr)
|
||||||
|
{
|
||||||
|
block_present_basic_block:
|
||||||
|
block.function(block.index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block_not_present_basic_block:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
run_function_entry = BasicBlock::Create(getGlobalContext(), "Entry", run_function);
|
||||||
|
auto block_present_basic_block = BasicBlock::Create(getGlobalContext(), "BlockPresent", run_function);
|
||||||
|
auto block_not_present_basic_block = BasicBlock::Create(getGlobalContext(), "BlockNotPresent", run_function);
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(run_function_entry);
|
||||||
|
auto block_address = ir_builder->CreateCall(get_block_address_function, Machine()->ReadRegiser(Register::PC, true,true));
|
||||||
|
auto function = ir_builder->CreateExtractValue(block_address, 0);
|
||||||
|
auto block_present_pred = ir_builder->CreateICmpNE(function,
|
||||||
|
ConstantPointerNull::get(cast<PointerType>(function->getType())));
|
||||||
|
ir_builder->CreateCondBr(block_present_pred, block_present_basic_block, block_not_present_basic_block);
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(block_present_basic_block);
|
||||||
|
auto index = ir_builder->CreateExtractValue(block_address, 1);
|
||||||
|
auto call = ir_builder->CreateCall(function, index);
|
||||||
|
call->setTailCall();
|
||||||
|
ir_builder->CreateRetVoid();
|
||||||
|
|
||||||
|
ir_builder->SetInsertPoint(block_not_present_basic_block);
|
||||||
|
ir_builder->CreateRetVoid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::DecodeInstructions()
|
||||||
|
{
|
||||||
|
size_t generated = 0;
|
||||||
|
size_t total = 0;
|
||||||
|
for (auto i = Loader::ROMCodeStart; i <= Loader::ROMCodeStart + Loader::ROMCodeSize - 4; i += 4)
|
||||||
|
{
|
||||||
|
++total;
|
||||||
|
auto bytes = Memory::Read32(i);
|
||||||
|
if (bytes == 0) continue;
|
||||||
|
auto instruction = Disassembler::Disassemble(bytes, i);
|
||||||
|
if (instruction == nullptr) continue;
|
||||||
|
++generated;
|
||||||
|
auto instruction_block = std::make_unique<InstructionBlock>(this, instruction.release());
|
||||||
|
instruction_blocks_by_pc[i] = instruction_block.get();
|
||||||
|
instruction_blocks.push_back(std::move(instruction_block));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(BinaryTranslator, "Generated % 8d blocks of % 8d = % 3.1f%%", generated, total, 100.0 * generated / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::GenerateInstructionsEntry()
|
||||||
|
{
|
||||||
|
for (auto &instruction : instruction_blocks)
|
||||||
|
{
|
||||||
|
instruction->GenerateEntryBlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::GenerateInstructionsCode()
|
||||||
|
{
|
||||||
|
for (auto &instruction : instruction_blocks)
|
||||||
|
{
|
||||||
|
instruction->GenerateCode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModuleGen::ColorBlocks()
|
||||||
|
{
|
||||||
|
for (auto &instruction : instruction_blocks)
|
||||||
|
{
|
||||||
|
block_colors->AddBlock(instruction.get());
|
||||||
|
}
|
||||||
|
block_colors->GenerateFunctions();
|
||||||
|
}
|
113
src/binary_translation/ModuleGen.h
Normal file
113
src/binary_translation/ModuleGen.h
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <llvm/IR/IRBuilder.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <common/common_types.h>
|
||||||
|
|
||||||
|
enum class Register;
|
||||||
|
|
||||||
|
class InstructionBlock;
|
||||||
|
class MachineState;
|
||||||
|
class TBAA;
|
||||||
|
class BlockColors;
|
||||||
|
|
||||||
|
namespace llvm
|
||||||
|
{
|
||||||
|
class Module;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ModuleGen
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Verify - produce a code that can be verified
|
||||||
|
* this is done by returning after every opcode
|
||||||
|
*/
|
||||||
|
explicit ModuleGen(llvm::Module *module, bool verify);
|
||||||
|
~ModuleGen();
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
void GenerateIncInstructionCount();
|
||||||
|
// Generate code to read pc and run all following instructions, used in cases of indirect branch
|
||||||
|
void BranchReadPC();
|
||||||
|
// Generate code to write to pc and run all following instructions, used in cases of direct branch
|
||||||
|
void BranchWritePCConst(InstructionBlock *current, u64 pc);
|
||||||
|
|
||||||
|
llvm::IRBuilder<> *IrBuilder() { return ir_builder.get(); }
|
||||||
|
llvm::Module *Module() { return module; }
|
||||||
|
MachineState *Machine() { return machine.get(); }
|
||||||
|
TBAA *GetTBAA() { return tbaa.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Generates the declarations of all the globals of the module
|
||||||
|
void GenerateGlobals();
|
||||||
|
void GenerateBlockAddressArray();
|
||||||
|
void GenerateGetBlockAddressFunction();
|
||||||
|
void GenerateCanRunFunction();
|
||||||
|
void GenerateRunFunction();
|
||||||
|
// Creates InstructionBlock for each instruction
|
||||||
|
void DecodeInstructions();
|
||||||
|
// Generates the entry basic blocks for each instruction
|
||||||
|
void GenerateInstructionsEntry();
|
||||||
|
// Generates the code of each instruction
|
||||||
|
void GenerateInstructionsCode();
|
||||||
|
// Must be run after the instruction code is generated since it depends on the
|
||||||
|
// inter block jumps
|
||||||
|
void ColorBlocks();
|
||||||
|
|
||||||
|
llvm::Module *module;
|
||||||
|
bool verify;
|
||||||
|
|
||||||
|
std::unique_ptr<MachineState> machine;
|
||||||
|
std::unique_ptr<TBAA> tbaa;
|
||||||
|
|
||||||
|
std::unique_ptr<llvm::IRBuilder<>> ir_builder;
|
||||||
|
|
||||||
|
size_t block_address_array_base;
|
||||||
|
size_t block_address_array_size;
|
||||||
|
/*
|
||||||
|
* struct BlockAddress
|
||||||
|
* {
|
||||||
|
* void (*function)(u32 index);
|
||||||
|
* u32 index;
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
llvm::StructType *block_address_type;
|
||||||
|
llvm::Constant *block_address_not_present;
|
||||||
|
/*
|
||||||
|
* i8 **BlockAddressArray;
|
||||||
|
* The array at [i/4 - block_address_array_base] contains the block address for the instruction at i
|
||||||
|
* or nullptr if it is not decoded
|
||||||
|
*/
|
||||||
|
llvm::ArrayType *block_address_array_type;
|
||||||
|
llvm::GlobalVariable *block_address_array;
|
||||||
|
/*
|
||||||
|
* i32 InstructionCount;
|
||||||
|
* The count of instructions executed
|
||||||
|
*/
|
||||||
|
llvm::GlobalVariable *instruction_count;
|
||||||
|
/*
|
||||||
|
* i8 *GetBlockAddress(u32 pc)
|
||||||
|
* Returns the address of the block for the instruction at pc
|
||||||
|
*/
|
||||||
|
llvm::Function *get_block_address_function;
|
||||||
|
/*
|
||||||
|
* bool CanRun()
|
||||||
|
* Returns whether there is a binary translation available for a PC
|
||||||
|
*/
|
||||||
|
llvm::Function *can_run_function;
|
||||||
|
/*
|
||||||
|
* void Run()
|
||||||
|
* Runs binary translated opcodes
|
||||||
|
*/
|
||||||
|
llvm::Function *run_function;
|
||||||
|
llvm::BasicBlock *run_function_entry;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All the instruction blocks
|
||||||
|
*/
|
||||||
|
std::vector<std::unique_ptr<InstructionBlock>> instruction_blocks;
|
||||||
|
std::unordered_map<u64, InstructionBlock *> instruction_blocks_by_pc;
|
||||||
|
|
||||||
|
std::unique_ptr<BlockColors> block_colors;
|
||||||
|
};
|
45
src/binary_translation/TBAA.cpp
Normal file
45
src/binary_translation/TBAA.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include "TBAA.h"
|
||||||
|
#include <llvm/IR/MDBuilder.h>
|
||||||
|
#include <llvm/IR/LLVMContext.h>
|
||||||
|
#include <llvm/IR/Metadata.h>
|
||||||
|
#include <sstream>
|
||||||
|
#include <llvm/IR/Instruction.h>
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
void TBAA::GenerateTags()
|
||||||
|
{
|
||||||
|
MDBuilder md_builder(getGlobalContext());
|
||||||
|
|
||||||
|
auto tbaa_root = md_builder.createTBAARoot("Root");
|
||||||
|
|
||||||
|
for (auto i = 0; i < RegisterCount; ++i)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Register_" << i;
|
||||||
|
register_nodes[i] = md_builder.createTBAAScalarTypeNode(ss.str(), tbaa_root);
|
||||||
|
}
|
||||||
|
const_node = md_builder.createTBAAScalarTypeNode("Readonly", tbaa_root);
|
||||||
|
instruction_count_node = md_builder.createTBAAScalarTypeNode("InstructionCount", tbaa_root);
|
||||||
|
memory_node = md_builder.createTBAAScalarTypeNode("Memory", tbaa_root);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TBAA::TagRegister(Instruction* instruction, Register reg)
|
||||||
|
{
|
||||||
|
instruction->setMetadata(LLVMContext::MD_tbaa, register_nodes[(int)reg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TBAA::TagConst(Instruction* instruction)
|
||||||
|
{
|
||||||
|
instruction->setMetadata(LLVMContext::MD_tbaa, const_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TBAA::TagInstructionCount(llvm::Instruction* instruction)
|
||||||
|
{
|
||||||
|
instruction->setMetadata(LLVMContext::MD_tbaa, instruction_count_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TBAA::TagMemory(llvm::Instruction* instruction)
|
||||||
|
{
|
||||||
|
instruction->setMetadata(LLVMContext::MD_tbaa, memory_node);
|
||||||
|
}
|
34
src/binary_translation/TBAA.h
Normal file
34
src/binary_translation/TBAA.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Instructions/Types.h"
|
||||||
|
#include <llvm/IR/Instructions.h>
|
||||||
|
|
||||||
|
namespace llvm
|
||||||
|
{
|
||||||
|
class Instruction;
|
||||||
|
class MDNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Manages TBAA.
|
||||||
|
A TBAA type is generated for each register and global.
|
||||||
|
It is a bit of an abuse of TBAA but because nothing aliases it is a good way
|
||||||
|
to notify LLVM of it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TBAA
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void GenerateTags();
|
||||||
|
|
||||||
|
void TagRegister(llvm::Instruction *instruction, Register reg);
|
||||||
|
void TagConst(llvm::Instruction *instruction);
|
||||||
|
void TagInstructionCount(llvm::Instruction *instruction);
|
||||||
|
void TagMemory(llvm::Instruction *instruction);
|
||||||
|
private:
|
||||||
|
llvm::MDNode *register_nodes[RegisterCount];
|
||||||
|
// Tag for everything that is never written.
|
||||||
|
// Since it is never written, one tag works
|
||||||
|
llvm::MDNode *const_node;
|
||||||
|
llvm::MDNode *instruction_count_node;
|
||||||
|
llvm::MDNode *memory_node;
|
||||||
|
};
|
48
src/binary_translation/main.cpp
Normal file
48
src/binary_translation/main.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#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/hle/kernel/memory.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
#include "codegen.h"
|
||||||
|
#include <llvm/Support/CommandLine.h>
|
||||||
|
|
||||||
|
namespace cl = llvm::cl;
|
||||||
|
|
||||||
|
cl::opt<std::string> InputFilename(cl::Positional, cl::Required, cl::desc("<input rom filename>"));
|
||||||
|
cl::opt<std::string> OutputFilename(cl::Positional, cl::Required, cl::desc("<output object filename>"));
|
||||||
|
cl::opt<std::string> DebugFilename(cl::Positional, cl::Optional, cl::desc("<debug filename>"));
|
||||||
|
cl::opt<bool> Verify("verify", cl::desc("<verify>"), cl::init(false));
|
||||||
|
|
||||||
|
int main(int argc, const char *const *argv)
|
||||||
|
{
|
||||||
|
// Remove all llvm options
|
||||||
|
llvm::StringMap<cl::Option *>& options = cl::getRegisteredOptions();
|
||||||
|
for (auto i = options.begin(); i != options.end(); ++i)
|
||||||
|
{
|
||||||
|
if (i->getValue() != &InputFilename && i->getValue() != &OutputFilename && i->getValue() != &DebugFilename)
|
||||||
|
{
|
||||||
|
i->getValue()->setHiddenFlag(cl::Hidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cl::ParseCommandLineOptions(argc, argv);
|
||||||
|
|
||||||
|
auto input_rom = InputFilename.c_str();
|
||||||
|
auto output_object = OutputFilename.c_str();
|
||||||
|
auto output_debug = DebugFilename.getNumOccurrences() ? DebugFilename.c_str() : nullptr;
|
||||||
|
bool verify = Verify;
|
||||||
|
|
||||||
|
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, verify);
|
||||||
|
code_generator.Run();
|
||||||
|
}
|
@ -19,6 +19,9 @@ link_directories(${GLFW_LIBRARY_DIRS})
|
|||||||
add_executable(citra ${SRCS} ${HEADERS})
|
add_executable(citra ${SRCS} ${HEADERS})
|
||||||
target_link_libraries(citra core video_core common)
|
target_link_libraries(citra core video_core common)
|
||||||
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih glad)
|
target_link_libraries(citra ${GLFW_LIBRARIES} ${OPENGL_gl_LIBRARY} inih glad)
|
||||||
|
if(ENABLE_BINARY_TRANSLATION)
|
||||||
|
target_link_libraries(citra ${llvm_libs})
|
||||||
|
endif()
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_link_libraries(citra getopt)
|
target_link_libraries(citra getopt)
|
||||||
endif()
|
endif()
|
||||||
|
@ -83,6 +83,9 @@ target_link_libraries(citra-qt core video_core common qhexedit)
|
|||||||
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
|
target_link_libraries(citra-qt ${OPENGL_gl_LIBRARY} ${CITRA_QT_LIBS})
|
||||||
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
|
target_link_libraries(citra-qt ${PLATFORM_LIBRARIES})
|
||||||
|
|
||||||
|
if(ENABLE_BINARY_TRANSLATION)
|
||||||
|
target_link_libraries(citra-qt ${llvm_libs})
|
||||||
|
endif()
|
||||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
|
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|FreeBSD|OpenBSD|NetBSD")
|
||||||
install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
install(TARGETS citra-qt RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
||||||
endif()
|
endif()
|
||||||
|
@ -58,7 +58,8 @@ namespace Log {
|
|||||||
CLS(Render) \
|
CLS(Render) \
|
||||||
SUB(Render, Software) \
|
SUB(Render, Software) \
|
||||||
SUB(Render, OpenGL) \
|
SUB(Render, OpenGL) \
|
||||||
CLS(Loader)
|
CLS(Loader) \
|
||||||
|
CLS(BinaryTranslator)
|
||||||
|
|
||||||
// GetClassName is a macro defined by Windows.h, grrr...
|
// GetClassName is a macro defined by Windows.h, grrr...
|
||||||
const char* GetLogClassName(Class log_class) {
|
const char* GetLogClassName(Class log_class) {
|
||||||
|
@ -74,6 +74,7 @@ enum class Class : ClassType {
|
|||||||
Render_Software, ///< Software renderer backend
|
Render_Software, ///< Software renderer backend
|
||||||
Render_OpenGL, ///< OpenGL backend
|
Render_OpenGL, ///< OpenGL backend
|
||||||
Loader, ///< ROM loader
|
Loader, ///< ROM loader
|
||||||
|
BinaryTranslator, ///< Binary translator
|
||||||
|
|
||||||
Count ///< Total number of logging classes
|
Count ///< Total number of logging classes
|
||||||
};
|
};
|
||||||
|
@ -253,7 +253,17 @@ set(HEADERS
|
|||||||
settings.h
|
settings.h
|
||||||
system.h
|
system.h
|
||||||
)
|
)
|
||||||
|
if(ENABLE_BINARY_TRANSLATION)
|
||||||
|
set(SRCS
|
||||||
|
${SRCS}
|
||||||
|
binary_translation/BinaryTranslationLoader.cpp
|
||||||
|
)
|
||||||
|
set(HEADERS
|
||||||
|
${HEADERS}
|
||||||
|
mem_map.h
|
||||||
|
binary_translation/BinaryTranslationLoader.h
|
||||||
|
)
|
||||||
|
endif()
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
|
|
||||||
add_library(core STATIC ${SRCS} ${HEADERS})
|
add_library(core STATIC ${SRCS} ${HEADERS})
|
||||||
|
@ -23,6 +23,10 @@
|
|||||||
#include "core/arm/skyeye_common/armsupp.h"
|
#include "core/arm/skyeye_common/armsupp.h"
|
||||||
#include "core/arm/skyeye_common/vfp/vfp.h"
|
#include "core/arm/skyeye_common/vfp/vfp.h"
|
||||||
|
|
||||||
|
#if ENABLE_BINARY_TRANSLATION
|
||||||
|
#include "core/binary_translation/BinaryTranslationLoader.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "core/gdbstub/gdbstub.h"
|
#include "core/gdbstub/gdbstub.h"
|
||||||
|
|
||||||
Common::Profiling::TimingCategory profile_execute("DynCom::Execute");
|
Common::Profiling::TimingCategory profile_execute("DynCom::Execute");
|
||||||
@ -36,7 +40,8 @@ enum {
|
|||||||
CALL = (1 << 4),
|
CALL = (1 << 4),
|
||||||
RET = (1 << 5),
|
RET = (1 << 5),
|
||||||
END_OF_PAGE = (1 << 6),
|
END_OF_PAGE = (1 << 6),
|
||||||
THUMB = (1 << 7)
|
THUMB = (1 << 7),
|
||||||
|
BINARY_TRANSLATED = (1 << 8)
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RM BITS(sht_oper, 0, 3)
|
#define RM BITS(sht_oper, 0, 3)
|
||||||
@ -3554,6 +3559,12 @@ static int InterpreterTranslate(ARMul_State* cpu, int& bb_start, u32 addr) {
|
|||||||
translated:
|
translated:
|
||||||
phys_addr += inst_size;
|
phys_addr += inst_size;
|
||||||
|
|
||||||
|
#if ENABLE_BINARY_TRANSLATION
|
||||||
|
if (BinaryTranslationLoader::CanRun(phys_addr, cpu->TFlag))
|
||||||
|
{
|
||||||
|
inst_base->br = BINARY_TRANSLATED;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if ((phys_addr & 0xfff) == 0) {
|
if ((phys_addr & 0xfff) == 0) {
|
||||||
inst_base->br = END_OF_PAGE;
|
inst_base->br = END_OF_PAGE;
|
||||||
}
|
}
|
||||||
@ -3585,6 +3596,10 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
|||||||
|
|
||||||
GDBStub::BreakpointAddress breakpoint_data;
|
GDBStub::BreakpointAddress breakpoint_data;
|
||||||
|
|
||||||
|
#if ENABLE_BINARY_TRANSLATION
|
||||||
|
BinaryTranslationLoader::SetCpuState(cpu);
|
||||||
|
#endif
|
||||||
|
|
||||||
#undef RM
|
#undef RM
|
||||||
#undef RS
|
#undef RS
|
||||||
|
|
||||||
@ -3621,17 +3636,24 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
|||||||
|
|
||||||
// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
|
// GCC and Clang have a C++ extension to support a lookup table of labels. Otherwise, fallback to a
|
||||||
// clunky switch statement.
|
// clunky switch statement.
|
||||||
|
#if ENABLE_BINARY_TRANSLATION
|
||||||
|
#define BINARY_TRANSLATION_VERIFY_CALLBACK BinaryTranslationLoader::VerifyCallback();
|
||||||
|
#else
|
||||||
|
#define BINARY_TRANSLATION_VERIFY_CALLBACK
|
||||||
|
#endif
|
||||||
#if defined __GNUC__ || defined __clang__
|
#if defined __GNUC__ || defined __clang__
|
||||||
#define GOTO_NEXT_INST \
|
#define GOTO_NEXT_INST \
|
||||||
GDB_BP_CHECK; \
|
GDB_BP_CHECK; \
|
||||||
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
|
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
|
||||||
num_instrs++; \
|
num_instrs++; \
|
||||||
|
BINARY_TRANSLATION_VERIFY_CALLBACK \
|
||||||
goto *InstLabel[inst_base->idx]
|
goto *InstLabel[inst_base->idx]
|
||||||
#else
|
#else
|
||||||
#define GOTO_NEXT_INST \
|
#define GOTO_NEXT_INST \
|
||||||
GDB_BP_CHECK; \
|
GDB_BP_CHECK; \
|
||||||
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
|
if (num_instrs >= cpu->NumInstrsToExecute) goto END; \
|
||||||
num_instrs++; \
|
num_instrs++; \
|
||||||
|
BINARY_TRANSLATION_VERIFY_CALLBACK \
|
||||||
switch(inst_base->idx) { \
|
switch(inst_base->idx) { \
|
||||||
case 0: goto VMLA_INST; \
|
case 0: goto VMLA_INST; \
|
||||||
case 1: goto VMLS_INST; \
|
case 1: goto VMLS_INST; \
|
||||||
@ -3905,6 +3927,9 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
|
|||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if ENABLE_BINARY_TRANSLATION
|
||||||
|
num_instrs = BinaryTranslationLoader::Run(num_instrs);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (cpu->TFlag)
|
if (cpu->TFlag)
|
||||||
cpu->Reg[15] &= 0xfffffffe;
|
cpu->Reg[15] &= 0xfffffffe;
|
||||||
|
262
src/core/binary_translation/BinaryTranslationLoader.cpp
Normal file
262
src/core/binary_translation/BinaryTranslationLoader.cpp
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
#include "BinaryTranslationLoader.h"
|
||||||
|
#include "core/arm/skyeye_common/armstate.h"
|
||||||
|
#include "core/arm/skyeye_common/arm_regformat.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include <llvm/Support/TargetSelect.h>
|
||||||
|
#include <llvm/ADT/STLExtras.h>
|
||||||
|
#include <llvm/Support/MemoryBuffer.h>
|
||||||
|
#include <llvm/Object/ObjectFile.h>
|
||||||
|
#include <llvm/ExecutionEngine/RuntimeDyld.h>
|
||||||
|
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
|
||||||
|
#include <core/mem_map.h>
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
bool g_enabled = false;
|
||||||
|
bool g_verify = false;
|
||||||
|
ARMul_State *g_state;
|
||||||
|
|
||||||
|
std::unique_ptr<SectionMemoryManager> g_memory_manager;
|
||||||
|
std::unique_ptr<RuntimeDyld> g_dyld;
|
||||||
|
std::unique_ptr<RuntimeDyld::LoadedObjectInfo> g_loaded_object_info;
|
||||||
|
|
||||||
|
void(*g_run_function)();
|
||||||
|
bool(*g_can_run_function)();
|
||||||
|
uint32_t *g_instruction_count;
|
||||||
|
|
||||||
|
// Used by the verifier
|
||||||
|
struct SavedState
|
||||||
|
{
|
||||||
|
SavedState() { }
|
||||||
|
SavedState(const ARMul_State &state)
|
||||||
|
{
|
||||||
|
memcpy(regs, &state.Reg[0], sizeof(regs));
|
||||||
|
memcpy(flags, &state.NFlag, sizeof(flags));
|
||||||
|
t_flag = state.TFlag;
|
||||||
|
}
|
||||||
|
void CopyTo(ARMul_State &state)
|
||||||
|
{
|
||||||
|
memcpy(&state.Reg[0], regs, sizeof(regs));
|
||||||
|
memcpy(&state.NFlag, flags, sizeof(flags));
|
||||||
|
t_flag = state.TFlag;
|
||||||
|
}
|
||||||
|
void SwapWith(ARMul_State &state)
|
||||||
|
{
|
||||||
|
SavedState arm_state = state;
|
||||||
|
std::swap(*this, arm_state);
|
||||||
|
arm_state.CopyTo(state);
|
||||||
|
}
|
||||||
|
void Print()
|
||||||
|
{
|
||||||
|
LOG_ERROR(BinaryTranslator, "%08x %08x %08x %08x %08x %08x %08x %08x",
|
||||||
|
regs[0], regs[1], regs[2], regs[3],
|
||||||
|
regs[4], regs[5], regs[6], regs[7]);
|
||||||
|
LOG_ERROR(BinaryTranslator, "%08x %08x %08x %08x %08x %08x %08x %08x",
|
||||||
|
regs[8], regs[9], regs[10], regs[11],
|
||||||
|
regs[12], regs[13], regs[14], regs[15]);
|
||||||
|
LOG_ERROR(BinaryTranslator, "%01x %01x %01x %01x %01x", flags[0], flags[1], flags[2], flags[3], t_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 regs[16];
|
||||||
|
u32 flags[4];
|
||||||
|
u32 t_flag;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const SavedState& lhs, const SavedState& rhs)
|
||||||
|
{
|
||||||
|
return memcmp(&lhs, &rhs, sizeof(lhs)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const SavedState& lhs, const SavedState& rhs)
|
||||||
|
{
|
||||||
|
return memcmp(&lhs, &rhs, sizeof(lhs)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool g_have_saved_state; // Whether there is a copied state
|
||||||
|
SavedState g_state_copy;
|
||||||
|
SavedState g_state_copy_before;
|
||||||
|
|
||||||
|
void BinaryTranslationLoader::Load(FileUtil::IOFile& file)
|
||||||
|
{
|
||||||
|
if (offsetof(ARMul_State, ZFlag) - offsetof(ARMul_State, NFlag) != 4 ||
|
||||||
|
offsetof(ARMul_State, CFlag) - offsetof(ARMul_State, NFlag) != 8 ||
|
||||||
|
offsetof(ARMul_State, VFlag) - offsetof(ARMul_State, NFlag) != 12)
|
||||||
|
{
|
||||||
|
LOG_WARNING(Loader, "Flags are unordered, cannot run optimized file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeNativeTarget();
|
||||||
|
|
||||||
|
g_memory_manager = make_unique<SectionMemoryManager>();
|
||||||
|
|
||||||
|
g_dyld = make_unique<RuntimeDyld>(*g_memory_manager.get(), *g_memory_manager.get());
|
||||||
|
|
||||||
|
auto size = file.GetSize();
|
||||||
|
auto buffer = make_unique<char[]>(size);
|
||||||
|
|
||||||
|
file.ReadBytes(buffer.get(), size);
|
||||||
|
if (!file.IsGood())
|
||||||
|
{
|
||||||
|
LOG_WARNING(Loader, "Cannot read optimized file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto object_file = object::ObjectFile::createObjectFile(MemoryBufferRef(StringRef(buffer.get(), size), ""));
|
||||||
|
if (!object_file)
|
||||||
|
{
|
||||||
|
LOG_WARNING(Loader, "Cannot load optimized file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_loaded_object_info = g_dyld->loadObject(*object_file->get());
|
||||||
|
if (g_dyld->hasError())
|
||||||
|
{
|
||||||
|
LOG_WARNING(Loader, "Cannot load optimized file, error %s", g_dyld->getErrorString().str().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_dyld->resolveRelocations();
|
||||||
|
g_dyld->registerEHFrames();
|
||||||
|
g_memory_manager->finalizeMemory();
|
||||||
|
|
||||||
|
g_run_function = static_cast<decltype(g_run_function)>((void*)g_dyld->getSymbol("Run").getAddress());
|
||||||
|
g_can_run_function = static_cast<decltype(g_can_run_function)>((void*)g_dyld->getSymbol("CanRun").getAddress());
|
||||||
|
auto verify_ptr = static_cast<bool*>((void*)g_dyld->getSymbol("Verify").getAddress());
|
||||||
|
g_instruction_count = static_cast<uint32_t *>((void*)g_dyld->getSymbol("InstructionCount").getAddress());
|
||||||
|
auto memory_read_32_ptr = static_cast<decltype(&Memory::Read32) *>((void*)g_dyld->getSymbol("Memory::Read32").getAddress());
|
||||||
|
auto memory_write_32_ptr = static_cast<decltype(&Memory::Write32) *>((void*)g_dyld->getSymbol("Memory::Write32").getAddress());
|
||||||
|
|
||||||
|
if (!g_run_function || !g_can_run_function || !verify_ptr || !g_instruction_count || !memory_read_32_ptr || !memory_write_32_ptr)
|
||||||
|
{
|
||||||
|
LOG_WARNING(Loader, "Cannot load optimized file, missing critical function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_verify = *verify_ptr;
|
||||||
|
*memory_read_32_ptr = &Memory::Read32;
|
||||||
|
*memory_write_32_ptr = &Memory::Write32;
|
||||||
|
|
||||||
|
g_enabled = true;
|
||||||
|
|
||||||
|
LOG_INFO(Loader, "Binary translation enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinaryTranslationLoader::SetCpuState(ARMul_State* state)
|
||||||
|
{
|
||||||
|
if (!g_enabled) return;
|
||||||
|
|
||||||
|
auto regs_ptr = static_cast<u32 **>((void*)g_dyld->getSymbol("Registers").getAddress());
|
||||||
|
auto flags_ptr = static_cast<u32 **>((void*)g_dyld->getSymbol("Flags").getAddress());
|
||||||
|
|
||||||
|
*regs_ptr = &state->Reg[0];
|
||||||
|
*flags_ptr = &state->NFlag;
|
||||||
|
g_have_saved_state = false;
|
||||||
|
g_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BinaryTranslationLoader::CanRun(bool specific_address)
|
||||||
|
{
|
||||||
|
if (!g_enabled) return false;
|
||||||
|
// Thumb not implemented
|
||||||
|
if (g_state->TFlag) return false;
|
||||||
|
if (specific_address)
|
||||||
|
if (!g_can_run_function()) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BinaryTranslationLoader::CanRun(u32 pc, bool tflag)
|
||||||
|
{
|
||||||
|
if (!g_enabled) return false;
|
||||||
|
if (tflag) return false;
|
||||||
|
std::swap(g_state->Reg[15], pc);
|
||||||
|
auto result = g_can_run_function();
|
||||||
|
std::swap(g_state->Reg[15], pc);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BinaryTranslationLoader::Run(uint32_t instruction_count)
|
||||||
|
{
|
||||||
|
// No need to check the PC, Run does it anyway
|
||||||
|
if (!CanRun(false)) return instruction_count;
|
||||||
|
// If verify is enabled, it will run opcodes
|
||||||
|
if (g_verify) return instruction_count;
|
||||||
|
|
||||||
|
return RunInternal(instruction_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BinaryTranslationLoader::RunInternal(uint32_t instruction_count)
|
||||||
|
{
|
||||||
|
*g_instruction_count = instruction_count;
|
||||||
|
g_run_function();
|
||||||
|
|
||||||
|
g_state->TFlag = g_state->Reg[15] & 1;
|
||||||
|
if (g_state->TFlag)
|
||||||
|
g_state->Reg[15] &= 0xfffffffe;
|
||||||
|
else
|
||||||
|
g_state->Reg[15] &= 0xfffffffc;
|
||||||
|
|
||||||
|
return *g_instruction_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Swap(void *a, void *b, size_t size)
|
||||||
|
{
|
||||||
|
auto a_char = (char *)a;
|
||||||
|
auto b_char = (char *)b;
|
||||||
|
for (auto i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
std::swap(a_char[i], b_char[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BinaryTranslationLoader::VerifyCallback()
|
||||||
|
{
|
||||||
|
if (!g_enabled || !g_verify) return;
|
||||||
|
|
||||||
|
// Swap the PC and TFlag to the old state before checking if it can run
|
||||||
|
std::swap(g_state_copy.regs[15], g_state->Reg[15]);
|
||||||
|
std::swap(g_state_copy.t_flag, g_state->TFlag);
|
||||||
|
auto can_run = CanRun(true);
|
||||||
|
std::swap(g_state_copy.regs[15], g_state->Reg[15]);
|
||||||
|
std::swap(g_state_copy.t_flag, g_state->TFlag);
|
||||||
|
|
||||||
|
if (g_have_saved_state && can_run)
|
||||||
|
{
|
||||||
|
// An opcode is finished, simulate it
|
||||||
|
|
||||||
|
// Copy the state before
|
||||||
|
g_state_copy_before = g_state_copy;
|
||||||
|
|
||||||
|
// Swap to the state before the opcode
|
||||||
|
g_state_copy.SwapWith(*g_state);
|
||||||
|
|
||||||
|
// Run the opcode
|
||||||
|
RunInternal(0);
|
||||||
|
|
||||||
|
// Test
|
||||||
|
auto current_as_saved_state = SavedState(*g_state);
|
||||||
|
if (current_as_saved_state != g_state_copy)
|
||||||
|
{
|
||||||
|
LOG_ERROR(BinaryTranslator, "Verify failed");
|
||||||
|
LOG_ERROR(BinaryTranslator, "Regs Before");
|
||||||
|
g_state_copy_before.Print();
|
||||||
|
LOG_ERROR(BinaryTranslator, "Regs OK");
|
||||||
|
g_state_copy.Print();
|
||||||
|
LOG_ERROR(BinaryTranslator, "Regs not OK");
|
||||||
|
current_as_saved_state.Print();
|
||||||
|
|
||||||
|
// Don't spam
|
||||||
|
g_enabled = false;
|
||||||
|
|
||||||
|
// Make sure it has a valid state to continue to run
|
||||||
|
g_state_copy.CopyTo(*g_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If this opcode is not translated or there is no saved state, just save the state and continue
|
||||||
|
g_state_copy = *g_state;
|
||||||
|
|
||||||
|
g_have_saved_state = true;
|
||||||
|
}
|
||||||
|
}
|
22
src/core/binary_translation/BinaryTranslationLoader.h
Normal file
22
src/core/binary_translation/BinaryTranslationLoader.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#include "common/file_util.h"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
struct ARMul_State;
|
||||||
|
|
||||||
|
class BinaryTranslationLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Load(FileUtil::IOFile& file);
|
||||||
|
static void SetCpuState(ARMul_State *state);
|
||||||
|
// Checks whether the cpu state can be run
|
||||||
|
// If specific_address, checks the specific PC too
|
||||||
|
static bool CanRun(bool specific_address);
|
||||||
|
// Checks whether the cpu state can run the specific address at the specific mode
|
||||||
|
static bool CanRun(u32 pc, bool tflag);
|
||||||
|
// Runs the state provided at SetCpuState.
|
||||||
|
// Returns instruction_count + number of instructions executed
|
||||||
|
static uint32_t Run(uint32_t instruction_count);
|
||||||
|
// Link between Run and VerifyCallback
|
||||||
|
static uint32_t RunInternal(uint32_t instruction_count);
|
||||||
|
static void VerifyCallback();
|
||||||
|
};
|
@ -14,6 +14,7 @@
|
|||||||
#include "core/loader/elf.h"
|
#include "core/loader/elf.h"
|
||||||
#include "core/loader/ncch.h"
|
#include "core/loader/ncch.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
#include "3dsx.h"
|
#include "3dsx.h"
|
||||||
|
|
||||||
@ -226,6 +227,11 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, Shared
|
|||||||
code_set->entrypoint = code_set->code.addr;
|
code_set->entrypoint = code_set->code.addr;
|
||||||
code_set->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
|
code_set->memory = std::make_shared<std::vector<u8>>(std::move(program_image));
|
||||||
|
|
||||||
|
Loader::ROMCodeStart = code_set->code.addr;
|
||||||
|
Loader::ROMCodeSize = code_set->code.size;
|
||||||
|
Loader::ROMReadOnlyDataStart = code_set->rodata.addr;
|
||||||
|
Loader::ROMReadOnlyDataSize = code_set->rodata.size;
|
||||||
|
|
||||||
LOG_DEBUG(Loader, "code size: 0x%X", loadinfo.seg_sizes[0]);
|
LOG_DEBUG(Loader, "code size: 0x%X", loadinfo.seg_sizes[0]);
|
||||||
LOG_DEBUG(Loader, "rodata size: 0x%X", loadinfo.seg_sizes[1]);
|
LOG_DEBUG(Loader, "rodata size: 0x%X", loadinfo.seg_sizes[1]);
|
||||||
LOG_DEBUG(Loader, "data size: 0x%X (including 0x%X of bss)", loadinfo.seg_sizes[2], hdr.bss_size);
|
LOG_DEBUG(Loader, "data size: 0x%X (including 0x%X of bss)", loadinfo.seg_sizes[2], hdr.bss_size);
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
#include "core/loader/3dsx.h"
|
#include "core/loader/3dsx.h"
|
||||||
#include "core/loader/elf.h"
|
#include "core/loader/elf.h"
|
||||||
#include "core/loader/ncch.h"
|
#include "core/loader/ncch.h"
|
||||||
|
#if ENABLE_BINARY_TRANSLATION
|
||||||
|
#include "core/binary_translation/BinaryTranslationLoader.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -26,6 +29,11 @@ const std::initializer_list<Kernel::AddressMapping> default_address_mappings = {
|
|||||||
{ 0x1F000000, 0x600000, false }, // entire VRAM
|
{ 0x1F000000, 0x600000, false }, // entire VRAM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u32 ROMCodeStart;
|
||||||
|
u32 ROMCodeSize;
|
||||||
|
u32 ROMReadOnlyDataStart;
|
||||||
|
u32 ROMReadOnlyDataSize;
|
||||||
|
|
||||||
FileType IdentifyFile(FileUtil::IOFile& file) {
|
FileType IdentifyFile(FileUtil::IOFile& file) {
|
||||||
FileType type;
|
FileType type;
|
||||||
|
|
||||||
@ -92,6 +100,10 @@ const char* GetFileTypeString(FileType type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ResultStatus LoadFile(const std::string& filename) {
|
ResultStatus LoadFile(const std::string& filename) {
|
||||||
|
ROMCodeStart = 0;
|
||||||
|
ROMCodeSize = 0;
|
||||||
|
ROMReadOnlyDataStart = 0;
|
||||||
|
ROMReadOnlyDataSize = 0;
|
||||||
FileUtil::IOFile file(filename, "rb");
|
FileUtil::IOFile file(filename, "rb");
|
||||||
if (!file.IsOpen()) {
|
if (!file.IsOpen()) {
|
||||||
LOG_ERROR(Loader, "Failed to load file %s", filename.c_str());
|
LOG_ERROR(Loader, "Failed to load file %s", filename.c_str());
|
||||||
@ -112,6 +124,7 @@ ResultStatus LoadFile(const std::string& filename) {
|
|||||||
|
|
||||||
LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
|
LOG_INFO(Loader, "Loading file %s as %s...", filename.c_str(), GetFileTypeString(type));
|
||||||
|
|
||||||
|
ResultStatus status = ResultStatus::Error;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
||||||
//3DSX file format...
|
//3DSX file format...
|
||||||
@ -121,14 +134,15 @@ ResultStatus LoadFile(const std::string& filename) {
|
|||||||
// Load application and RomFS
|
// Load application and RomFS
|
||||||
if (ResultStatus::Success == app_loader.Load()) {
|
if (ResultStatus::Success == app_loader.Load()) {
|
||||||
Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
|
Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
|
||||||
return ResultStatus::Success;
|
status = ResultStatus::Success;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standard ELF file format...
|
// Standard ELF file format...
|
||||||
case FileType::ELF:
|
case FileType::ELF:
|
||||||
return AppLoader_ELF(std::move(file), filename_filename).Load();
|
status = AppLoader_ELF(std::move(file), filename_filename).Load();
|
||||||
|
break;
|
||||||
|
|
||||||
// NCCH/NCSD container formats...
|
// NCCH/NCSD container formats...
|
||||||
case FileType::CXI:
|
case FileType::CXI:
|
||||||
@ -139,14 +153,15 @@ ResultStatus LoadFile(const std::string& filename) {
|
|||||||
// Load application and RomFS
|
// Load application and RomFS
|
||||||
if (ResultStatus::Success == app_loader.Load()) {
|
if (ResultStatus::Success == app_loader.Load()) {
|
||||||
Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
|
Service::FS::RegisterArchiveType(Common::make_unique<FileSys::ArchiveFactory_RomFS>(app_loader), Service::FS::ArchiveIdCode::RomFS);
|
||||||
return ResultStatus::Success;
|
status = ResultStatus::Success;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CIA file format...
|
// CIA file format...
|
||||||
case FileType::CIA:
|
case FileType::CIA:
|
||||||
return ResultStatus::ErrorNotImplemented;
|
status = ResultStatus::ErrorNotImplemented;
|
||||||
|
break;
|
||||||
|
|
||||||
// Error occurred durring IdentifyFile...
|
// Error occurred durring IdentifyFile...
|
||||||
case FileType::Error:
|
case FileType::Error:
|
||||||
@ -155,10 +170,24 @@ ResultStatus LoadFile(const std::string& filename) {
|
|||||||
case FileType::Unknown:
|
case FileType::Unknown:
|
||||||
{
|
{
|
||||||
LOG_CRITICAL(Loader, "File %s is of unknown type.", filename.c_str());
|
LOG_CRITICAL(Loader, "File %s is of unknown type.", filename.c_str());
|
||||||
return ResultStatus::ErrorInvalidFormat;
|
status = ResultStatus::ErrorInvalidFormat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ResultStatus::Error;
|
#if ENABLE_BINARY_TRANSLATION
|
||||||
|
if (status == ResultStatus::Success)
|
||||||
|
{
|
||||||
|
std::unique_ptr<FileUtil::IOFile> optimized_file(new FileUtil::IOFile(filename + ".obj", "rb"));
|
||||||
|
if (!optimized_file->IsOpen())
|
||||||
|
{
|
||||||
|
LOG_WARNING(Loader, "Failed to load optimized file %s.obj", filename.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BinaryTranslationLoader::Load(*optimized_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Loader
|
} // namespace Loader
|
||||||
|
@ -156,4 +156,12 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi
|
|||||||
*/
|
*/
|
||||||
ResultStatus LoadFile(const std::string& filename);
|
ResultStatus LoadFile(const std::string& filename);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Infomation about ROM
|
||||||
|
*/
|
||||||
|
extern u32 ROMCodeStart;
|
||||||
|
extern u32 ROMCodeSize;
|
||||||
|
extern u32 ROMReadOnlyDataStart;
|
||||||
|
extern u32 ROMReadOnlyDataSize;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -165,6 +165,10 @@ ResultStatus AppLoader_NCCH::LoadExec() {
|
|||||||
s32 priority = exheader_header.arm11_system_local_caps.priority;
|
s32 priority = exheader_header.arm11_system_local_caps.priority;
|
||||||
u32 stack_size = exheader_header.codeset_info.stack_size;
|
u32 stack_size = exheader_header.codeset_info.stack_size;
|
||||||
Kernel::g_current_process->Run(priority, stack_size);
|
Kernel::g_current_process->Run(priority, stack_size);
|
||||||
|
Loader::ROMCodeStart = exheader_header.codeset_info.text.address;
|
||||||
|
Loader::ROMCodeSize = exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE;
|
||||||
|
Loader::ROMReadOnlyDataStart = exheader_header.codeset_info.ro.address;
|
||||||
|
Loader::ROMReadOnlyDataSize = exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE;
|
||||||
return ResultStatus::Success;
|
return ResultStatus::Success;
|
||||||
}
|
}
|
||||||
return ResultStatus::Error;
|
return ResultStatus::Error;
|
||||||
|
1
src/core/mem_map.h
Normal file
1
src/core/mem_map.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "memory.h"
|
Loading…
Reference in New Issue
Block a user