mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 12:31:05 +00:00
Added conditional execution
24% of instructions in 3dscraft
This commit is contained in:
parent
7882bb2c4c
commit
66f70e7321
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() { return BinarySearch(min, mid); }
|
||||
BinarySearch r() { 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); }
|
||||
};
|
@ -18,6 +18,7 @@ set(HEADERS
|
||||
InstructionBlock.h
|
||||
MachineState.h
|
||||
TBAA.h
|
||||
BinarySearch.h
|
||||
|
||||
Instructions/Types.h
|
||||
Instructions/Instruction.h
|
||||
|
@ -10,7 +10,7 @@ InstructionBlock::InstructionBlock(ModuleGen* module, Instruction* instruction)
|
||||
instruction(std::unique_ptr<Instruction>(instruction))
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::setfill('0') << std::setw(8) << instruction->Address();
|
||||
ss << std::hex << std::setfill('0') << std::setw(8) << instruction->Address() << "_";
|
||||
address_string = ss.str();
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ InstructionBlock::~InstructionBlock()
|
||||
|
||||
void InstructionBlock::GenerateEntryBlock()
|
||||
{
|
||||
entry_basic_block = llvm::BasicBlock::Create(llvm::getGlobalContext(), address_string + "_Entry");
|
||||
entry_basic_block = CreateBasicBlock("Entry");
|
||||
}
|
||||
|
||||
void InstructionBlock::GenerateCode()
|
||||
@ -29,14 +29,6 @@ void InstructionBlock::GenerateCode()
|
||||
ir_builder->SetInsertPoint(entry_basic_block);
|
||||
|
||||
instruction->GenerateCode(this);
|
||||
|
||||
auto basic_block = ir_builder->GetInsertBlock();
|
||||
// If the basic block is terminated there has been a jump
|
||||
// If not, jump to the next instruction
|
||||
if (!basic_block->getTerminator())
|
||||
{
|
||||
Module()->BranchWritePCConst(Address() + 4);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::Value *InstructionBlock::Read(Register reg)
|
||||
@ -49,6 +41,11 @@ 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);
|
||||
}
|
||||
|
||||
u32 InstructionBlock::Address()
|
||||
{
|
||||
return instruction->Address();
|
||||
|
@ -44,6 +44,11 @@ public:
|
||||
*/
|
||||
llvm::Value *Write(Register reg, llvm::Value *value);
|
||||
|
||||
/*
|
||||
* Creates a basic block for use by instructions
|
||||
*/
|
||||
llvm::BasicBlock *CreateBasicBlock(const char *name);
|
||||
|
||||
u32 Address();
|
||||
ModuleGen *Module() { return module; }
|
||||
|
||||
|
@ -8,17 +8,14 @@ static RegisterInstruction<Branch> register_instruction;
|
||||
bool Branch::Decode()
|
||||
{
|
||||
// B imm, BL imm
|
||||
if (ReadFields({ FieldDef<4>(&cond), FieldDef<3>(5), FieldDef<1>(&link), FieldDef<24>(&imm24) }))
|
||||
if (ReadFields({ CondDef(), FieldDef<3>(5), FieldDef<1>(&link), FieldDef<24>(&imm24) }))
|
||||
{
|
||||
if (cond != Condition::AL) return false;
|
||||
|
||||
form = Form::Immediate;
|
||||
return true;
|
||||
}
|
||||
// BLX reg
|
||||
if (ReadFields({ FieldDef<4>(&cond), FieldDef<24>(0x12fff3), FieldDef<4>(&rm)}))
|
||||
if (ReadFields({ CondDef(), FieldDef<24>(0x12fff3), FieldDef<4>(&rm) }))
|
||||
{
|
||||
if (cond != Condition::AL) return false;
|
||||
if (rm == Register::PC) return false;
|
||||
|
||||
link = true;
|
||||
@ -28,7 +25,7 @@ bool Branch::Decode()
|
||||
return false;
|
||||
}
|
||||
|
||||
void Branch::GenerateCode(InstructionBlock* instruction_block)
|
||||
void Branch::GenerateInstructionCode(InstructionBlock* instruction_block)
|
||||
{
|
||||
auto ir_builder = instruction_block->Module()->IrBuilder();
|
||||
if (link)
|
||||
|
@ -16,10 +16,9 @@ public:
|
||||
|
||||
public:
|
||||
virtual bool Decode() override;
|
||||
void GenerateCode(InstructionBlock* instruction_block) override;
|
||||
void GenerateInstructionCode(InstructionBlock* instruction_block) override;
|
||||
private:
|
||||
Form form;
|
||||
Condition cond;
|
||||
bool link;
|
||||
u32 imm24;
|
||||
Register rm;
|
||||
|
@ -8,17 +8,16 @@ static RegisterInstruction<DataProcessing> register_instruction;
|
||||
bool DataProcessing::Decode()
|
||||
{
|
||||
// Mov and shifts must have zeroes at some operands of different data processing instructions
|
||||
if (ReadFields({ FieldDef<4>(&cond), FieldDef<3>(0), FieldDef<4>((u32)ShortOpType::MoveAndShifts), FieldDef<1>(&s), FieldDef<4>(0),
|
||||
if (ReadFields({ CondDef(), FieldDef<3>(0), FieldDef<4>((u32)ShortOpType::MoveAndShifts), FieldDef<1>(&s), FieldDef<4>(0),
|
||||
FieldDef<4>(&rd), FieldDef<5>(&imm5), FieldDef<3>(0), FieldDef<4>(&rm) }))
|
||||
{
|
||||
form = Form::Register;
|
||||
if (cond != Condition::AL) return false;
|
||||
if (imm5 != 0) return false; // Shifts
|
||||
if (s != 0) return false; // Set flags
|
||||
if (rm == Register::PC) return false; // Jump
|
||||
return true;
|
||||
}
|
||||
if (ReadFields({ FieldDef<4>(&cond), FieldDef<3>(1), FieldDef<4>(&short_op), FieldDef<1>(&s), FieldDef<4>(&rn),
|
||||
if (ReadFields({ CondDef(), FieldDef<3>(1), FieldDef<4>(&short_op), FieldDef<1>(&s), FieldDef<4>(&rn),
|
||||
FieldDef<4>(&rd), FieldDef<12>(&imm12) }))
|
||||
{
|
||||
// TODO: not implemented
|
||||
@ -28,7 +27,7 @@ bool DataProcessing::Decode()
|
||||
return false;
|
||||
}
|
||||
|
||||
void DataProcessing::GenerateCode(InstructionBlock* instruction_block)
|
||||
void DataProcessing::GenerateInstructionCode(InstructionBlock* instruction_block)
|
||||
{
|
||||
// Currently supports only mov reg, reg
|
||||
|
||||
|
@ -25,10 +25,9 @@ public:
|
||||
|
||||
public:
|
||||
virtual bool Decode() override;
|
||||
void GenerateCode(InstructionBlock* instruction_block) override;
|
||||
void GenerateInstructionCode(InstructionBlock* instruction_block) override;
|
||||
private:
|
||||
Form form;
|
||||
Condition cond;
|
||||
ShortOpType short_op;
|
||||
bool s;
|
||||
Register rn;
|
||||
|
@ -1,5 +1,10 @@
|
||||
#include "Instruction.h"
|
||||
#include "common/logging/log.h"
|
||||
#include <cassert>
|
||||
#include "InstructionBlock.h"
|
||||
#include "ModuleGen.h"
|
||||
#include "MachineState.h"
|
||||
#include "BinarySearch.h"
|
||||
|
||||
Instruction::Instruction()
|
||||
{
|
||||
@ -14,7 +19,45 @@ bool Instruction::Read(u32 instruction, u32 address)
|
||||
this->instruction = instruction;
|
||||
this->address = address;
|
||||
// Call the read of derived class
|
||||
return Decode();
|
||||
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(Address() + 4);
|
||||
}
|
||||
}
|
||||
|
||||
bool Instruction::ReadFields(const std::initializer_list<FieldDefObject> &fields)
|
||||
@ -36,6 +79,11 @@ bool Instruction::ReadFields(const std::initializer_list<FieldDefObject> &fields
|
||||
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)
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include "common/common_types.h"
|
||||
#include <initializer_list>
|
||||
#include "Types.h"
|
||||
|
||||
class InstructionBlock;
|
||||
|
||||
@ -19,10 +20,9 @@ public:
|
||||
bool Read(u32 instruction, u32 address);
|
||||
|
||||
/*
|
||||
* Generates code for the instruction into the instruction block
|
||||
* Derived classes must override this
|
||||
* Generates non instruction specific code, and then calls GenerateInstructionCode
|
||||
*/
|
||||
virtual void GenerateCode(InstructionBlock *instruction_block) = 0;
|
||||
void GenerateCode(InstructionBlock *instruction_block);
|
||||
|
||||
u32 Address() { return address; }
|
||||
protected:
|
||||
@ -30,6 +30,11 @@ 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
|
||||
@ -46,6 +51,10 @@ protected:
|
||||
*/
|
||||
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
|
||||
@ -57,6 +66,8 @@ private:
|
||||
u32 instruction;
|
||||
// Instruction address
|
||||
u32 address;
|
||||
|
||||
Condition cond;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -24,6 +24,25 @@ void MachineState::GenerateGlobals()
|
||||
false, GlobalValue::ExternalLinkage, flags_global_initializer, "Flags");
|
||||
}
|
||||
|
||||
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);
|
||||
return module->IrBuilder()->CreateConstInBoundsGEP1_32(base, index);
|
||||
}
|
||||
|
||||
Value* MachineState::ReadRegiser(Register reg)
|
||||
{
|
||||
auto load = module->IrBuilder()->CreateAlignedLoad(GetRegisterPtr(reg), 4);
|
||||
@ -38,21 +57,33 @@ Value* MachineState::WriteRegiser(Register reg, Value *value)
|
||||
return store;
|
||||
}
|
||||
|
||||
Value *MachineState::GetRegisterPtr(Register reg)
|
||||
Value* MachineState::ConditionPassed(Condition cond)
|
||||
{
|
||||
Value *global;
|
||||
unsigned index;
|
||||
if (reg <= Register::PC)
|
||||
auto ir_builder = module->IrBuilder();
|
||||
Value *pred = nullptr;
|
||||
auto not = false;
|
||||
switch (cond)
|
||||
{
|
||||
global = registers_global;
|
||||
index = static_cast<unsigned>(reg)-static_cast<unsigned>(Register::R0);
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
||||
switch (cond)
|
||||
{
|
||||
global = flags_global;
|
||||
index = static_cast<unsigned>(reg)-static_cast<unsigned>(Register::N);
|
||||
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");
|
||||
}
|
||||
auto base = module->IrBuilder()->CreateAlignedLoad(global, 4);
|
||||
module->GetTBAA()->TagConst(base);
|
||||
return module->IrBuilder()->CreateConstInBoundsGEP1_32(base, index);
|
||||
|
||||
if (not) pred = ir_builder->CreateNot(pred);
|
||||
return pred;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
|
||||
enum class Condition;
|
||||
enum class Register;
|
||||
class ModuleGen;
|
||||
|
||||
@ -20,7 +21,7 @@ public:
|
||||
void GenerateGlobals();
|
||||
llvm::Value *ReadRegiser(Register reg);
|
||||
llvm::Value *WriteRegiser(Register reg, llvm::Value *value);
|
||||
|
||||
llvm::Value* ConditionPassed(Condition cond);
|
||||
private:
|
||||
// Returns the address of a register or a flag
|
||||
llvm::Value *GetRegisterPtr(Register reg);
|
||||
|
Loading…
Reference in New Issue
Block a user