diff --git a/src/binary_translation/ARMFuncs.cpp b/src/binary_translation/ARMFuncs.cpp new file mode 100644 index 000000000..9608fba31 --- /dev/null +++ b/src/binary_translation/ARMFuncs.cpp @@ -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 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 }; +} \ No newline at end of file diff --git a/src/binary_translation/ARMFuncs.h b/src/binary_translation/ARMFuncs.h new file mode 100644 index 000000000..fc6f55d34 --- /dev/null +++ b/src/binary_translation/ARMFuncs.h @@ -0,0 +1,42 @@ +#include + +/* + * 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); +}; \ No newline at end of file diff --git a/src/binary_translation/CMakeLists.txt b/src/binary_translation/CMakeLists.txt index 5a22c469b..a770c8e87 100644 --- a/src/binary_translation/CMakeLists.txt +++ b/src/binary_translation/CMakeLists.txt @@ -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 diff --git a/src/binary_translation/InstructionBlock.h b/src/binary_translation/InstructionBlock.h index 4b170c991..0f312ef69 100644 --- a/src/binary_translation/InstructionBlock.h +++ b/src/binary_translation/InstructionBlock.h @@ -1,6 +1,8 @@ #include #include #include +#include +#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: diff --git a/src/binary_translation/Instructions/DataProcessing.cpp b/src/binary_translation/Instructions/DataProcessing.cpp index 1c61bddb5..879629534 100644 --- a/src/binary_translation/Instructions/DataProcessing.cpp +++ b/src/binary_translation/Instructions/DataProcessing.cpp @@ -2,6 +2,7 @@ #include "Disassembler.h" #include "InstructionBlock.h" #include "ModuleGen.h" +#include "ARMFuncs.h" static RegisterInstruction 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) { diff --git a/src/binary_translation/Instructions/DataProcessing.h b/src/binary_translation/Instructions/DataProcessing.h index 05426bd9a..84a13f0fd 100644 --- a/src/binary_translation/Instructions/DataProcessing.h +++ b/src/binary_translation/Instructions/DataProcessing.h @@ -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; }; \ No newline at end of file diff --git a/src/binary_translation/MachineState.cpp b/src/binary_translation/MachineState.cpp index adaff7e9d..127b73ad2 100644 --- a/src/binary_translation/MachineState.cpp +++ b/src/binary_translation/MachineState.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "TBAA.h" using namespace llvm; diff --git a/src/binary_translation/MachineState.h b/src/binary_translation/MachineState.h index 77c3568ce..e4d9ddac4 100644 --- a/src/binary_translation/MachineState.h +++ b/src/binary_translation/MachineState.h @@ -6,6 +6,7 @@ class ModuleGen; namespace llvm { class Value; + class Instruction; class GlobalVariable; } diff --git a/src/binary_translation/ModuleGen.cpp b/src/binary_translation/ModuleGen.cpp index 0e27e9a1a..b3a0db027 100644 --- a/src/binary_translation/ModuleGen.cpp +++ b/src/binary_translation/ModuleGen.cpp @@ -9,6 +9,7 @@ #include #include #include "MachineState.h" +#include "TBAA.h" using namespace llvm; diff --git a/src/binary_translation/ModuleGen.h b/src/binary_translation/ModuleGen.h index 85fb3d178..befa17cbf 100644 --- a/src/binary_translation/ModuleGen.h +++ b/src/binary_translation/ModuleGen.h @@ -1,12 +1,13 @@ +#pragma once #include #include #include -#include "TBAA.h" enum class Register; class InstructionBlock; class MachineState; +class TBAA; namespace llvm { diff --git a/src/binary_translation/TBAA.h b/src/binary_translation/TBAA.h index d689f98d5..0a4e2c766 100644 --- a/src/binary_translation/TBAA.h +++ b/src/binary_translation/TBAA.h @@ -1,3 +1,4 @@ +#pragma once #include "Instructions/Types.h" namespace llvm