mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 11:41:04 +00:00
Added some shifts
This commit is contained in:
parent
66f70e7321
commit
22fbe294be
168
src/binary_translation/ARMFuncs.cpp
Normal file
168
src/binary_translation/ARMFuncs.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
#include "ARMFuncs.h"
|
||||
#include "InstructionBlock.h"
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
ARMFuncs::SRType ARMFuncs::DecodeRegShift(u32 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 0: return SRType::LSL;
|
||||
case 1: return SRType::LSR;
|
||||
case 2: return SRType::ASR;
|
||||
case 3: return SRType::ROR;
|
||||
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();
|
||||
|
||||
// amount_zero_basic_block will not recieve any code, it used only for the phi
|
||||
auto amount_zero_basic_block = instruction->CreateBasicBlock("ShiftCAmount0");
|
||||
auto amount_not_zero_basic_block = instruction->CreateBasicBlock("ShiftCAmountNot0");
|
||||
auto phi_basic_block = instruction->CreateBasicBlock("ShiftCPhi");
|
||||
|
||||
ir_builder->CreateCondBr(ir_builder->CreateICmpEQ(amount, ir_builder->getInt32(0)), amount_zero_basic_block, amount_not_zero_basic_block);
|
||||
|
||||
ir_builder->SetInsertPoint(amount_zero_basic_block);
|
||||
ir_builder->CreateBr(phi_basic_block);
|
||||
|
||||
ir_builder->SetInsertPoint(amount_not_zero_basic_block);
|
||||
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 pred = ir_builder->GetInsertBlock(); // The basic block might have changed and needs to be current for the phi
|
||||
ir_builder->CreateBr(phi_basic_block);
|
||||
|
||||
ir_builder->SetInsertPoint(phi_basic_block);
|
||||
auto result_phi = ir_builder->CreatePHI(ir_builder->getInt32Ty(), 2);
|
||||
auto carry_phi = ir_builder->CreatePHI(ir_builder->getInt1Ty(), 2);
|
||||
|
||||
result_phi->addIncoming(value, amount_zero_basic_block);
|
||||
result_phi->addIncoming(result_amount_not_zero.result, pred);
|
||||
carry_phi->addIncoming(carry_in, amount_zero_basic_block);
|
||||
carry_phi->addIncoming(result_amount_not_zero.carry, pred);
|
||||
|
||||
return{ result_phi, carry_phi };
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// amount_zero_basic_block will not recieve any code, it used only for the phi
|
||||
auto amount_zero_basic_block = instruction->CreateBasicBlock("ShiftZeroCheckAmount0");
|
||||
auto amount_not_zero_basic_block = instruction->CreateBasicBlock("ShiftZeroCheckAmountNot0");
|
||||
auto phi_basic_block = instruction->CreateBasicBlock("ShiftZeroCheckPhi");
|
||||
|
||||
ir_builder->CreateCondBr(ir_builder->CreateICmpEQ(shift, ir_builder->getInt32(0)), amount_zero_basic_block, amount_not_zero_basic_block);
|
||||
|
||||
ir_builder->SetInsertPoint(amount_zero_basic_block);
|
||||
ir_builder->CreateBr(phi_basic_block);
|
||||
|
||||
ir_builder->SetInsertPoint(amount_not_zero_basic_block);
|
||||
auto result_amount_not_zero = non_zero_function(instruction, x, shift);
|
||||
auto pred = ir_builder->GetInsertBlock(); // The basic block might have changed and needs to be current for the phi
|
||||
ir_builder->CreateBr(phi_basic_block);
|
||||
|
||||
ir_builder->SetInsertPoint(phi_basic_block);
|
||||
auto phi = ir_builder->CreatePHI(ir_builder->getInt32Ty(), 2);
|
||||
|
||||
phi->addIncoming(x, amount_zero_basic_block);
|
||||
phi->addIncoming(result_amount_not_zero.result, pred);
|
||||
|
||||
return phi;
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
42
src/binary_translation/ARMFuncs.h
Normal file
42
src/binary_translation/ARMFuncs.h
Normal file
@ -0,0 +1,42 @@
|
||||
#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
|
||||
*/
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
static ShiftTN DecodeImmShift(InstructionBlock *instruction, u32 type, u32 imm5);
|
||||
static SRType DecodeRegShift(u32 type);
|
||||
|
||||
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);
|
||||
};
|
@ -6,6 +6,7 @@ set(SRCS
|
||||
InstructionBlock.cpp
|
||||
MachineState.cpp
|
||||
TBAA.cpp
|
||||
ARMFuncs.cpp
|
||||
|
||||
Instructions/Instruction.cpp
|
||||
Instructions/DataProcessing.cpp
|
||||
@ -19,6 +20,7 @@ set(HEADERS
|
||||
MachineState.h
|
||||
TBAA.h
|
||||
BinarySearch.h
|
||||
ARMFuncs.h
|
||||
|
||||
Instructions/Types.h
|
||||
Instructions/Instruction.h
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <common/common_types.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include "ModuleGen.h"
|
||||
|
||||
namespace llvm
|
||||
{
|
||||
@ -8,7 +10,6 @@ namespace llvm
|
||||
class BasicBlock;
|
||||
}
|
||||
|
||||
class ModuleGen;
|
||||
class Instruction;
|
||||
|
||||
enum class Register;
|
||||
@ -51,6 +52,7 @@ public:
|
||||
|
||||
u32 Address();
|
||||
ModuleGen *Module() { return module; }
|
||||
llvm::IRBuilder<> *IrBuilder() { return module->IrBuilder(); }
|
||||
|
||||
llvm::BasicBlock *GetEntryBasicBlock() { return entry_basic_block; }
|
||||
private:
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Disassembler.h"
|
||||
#include "InstructionBlock.h"
|
||||
#include "ModuleGen.h"
|
||||
#include "ARMFuncs.h"
|
||||
|
||||
static RegisterInstruction<DataProcessing> register_instruction;
|
||||
|
||||
@ -9,12 +10,11 @@ bool DataProcessing::Decode()
|
||||
{
|
||||
// Mov and shifts must have zeroes at some operands of different data processing instructions
|
||||
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) }))
|
||||
FieldDef<4>(&rd), FieldDef<5>(&imm5), FieldDef<2>(&op2), FieldDef<1>(0), FieldDef<4>(&rm) }))
|
||||
{
|
||||
form = Form::Register;
|
||||
if (imm5 != 0) return false; // Shifts
|
||||
if (s != 0) return false; // Set flags
|
||||
if (rm == Register::PC) return false; // Jump
|
||||
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<3>(1), FieldDef<4>(&short_op), FieldDef<1>(&s), FieldDef<4>(&rn),
|
||||
@ -29,10 +29,51 @@ bool DataProcessing::Decode()
|
||||
|
||||
void DataProcessing::GenerateInstructionCode(InstructionBlock* instruction_block)
|
||||
{
|
||||
// Currently supports only mov reg, reg
|
||||
auto ir_builder = instruction_block->IrBuilder();
|
||||
|
||||
auto value = instruction_block->Read(rm);
|
||||
instruction_block->Write(rd, value);
|
||||
auto original_carry = instruction_block->Read(Register::C);
|
||||
ARMFuncs::ResultCarry result = { instruction_block->Read(rm), original_carry };
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 != original_carry)
|
||||
instruction_block->Write(Register::C, result.carry);
|
||||
}
|
||||
|
||||
if (rd == Register::PC)
|
||||
{
|
||||
|
@ -18,6 +18,10 @@ public:
|
||||
// Compare, Test, Misc
|
||||
BitwiseOr = 12, MoveAndShifts, BitwiseBitClear, BitwiseNot
|
||||
};
|
||||
enum class Op2Type
|
||||
{
|
||||
MoveAndLSL, LSR, ASR, RRXAndROR
|
||||
};
|
||||
enum class Form
|
||||
{
|
||||
Register, RegisterShiftedRegister, Immediate
|
||||
@ -35,4 +39,5 @@ private:
|
||||
Register rm;
|
||||
u32 imm12;
|
||||
u32 imm5;
|
||||
Op2Type op2;
|
||||
};
|
@ -5,6 +5,7 @@
|
||||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/GlobalVariable.h>
|
||||
#include "TBAA.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
|
@ -6,6 +6,7 @@ class ModuleGen;
|
||||
namespace llvm
|
||||
{
|
||||
class Value;
|
||||
class Instruction;
|
||||
class GlobalVariable;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <llvm/IR/GlobalVariable.h>
|
||||
#include <stack>
|
||||
#include "MachineState.h"
|
||||
#include "TBAA.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <unordered_map>
|
||||
#include <common/common_types.h>
|
||||
#include "TBAA.h"
|
||||
|
||||
enum class Register;
|
||||
|
||||
class InstructionBlock;
|
||||
class MachineState;
|
||||
class TBAA;
|
||||
|
||||
namespace llvm
|
||||
{
|
||||
|
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
#include "Instructions/Types.h"
|
||||
|
||||
namespace llvm
|
||||
|
Loading…
Reference in New Issue
Block a user