From 3ae4d8b6463fd2fd9db004e5e7965ffc091e69ad Mon Sep 17 00:00:00 2001 From: Dani Messerman Date: Tue, 5 May 2015 22:15:48 +0300 Subject: [PATCH] Added arithmetic instructions (add, sub, and, or, ...) --- src/binary_translation/ARMFuncs.cpp | 30 ++++ src/binary_translation/ARMFuncs.h | 7 + src/binary_translation/BlockColors.cpp | 26 +++- src/binary_translation/CMakeLists.txt | 2 + .../Instructions/Arithmetic.cpp | 128 ++++++++++++++++++ .../Instructions/Arithmetic.h | 36 +++++ src/binary_translation/Instructions/Branch.h | 5 - src/binary_translation/ModuleGen.cpp | 4 +- 8 files changed, 226 insertions(+), 12 deletions(-) create mode 100644 src/binary_translation/Instructions/Arithmetic.cpp create mode 100644 src/binary_translation/Instructions/Arithmetic.h diff --git a/src/binary_translation/ARMFuncs.cpp b/src/binary_translation/ARMFuncs.cpp index 243eae5c5..936221e54 100644 --- a/src/binary_translation/ARMFuncs.cpp +++ b/src/binary_translation/ARMFuncs.cpp @@ -131,6 +131,13 @@ ARMFuncs::ResultCarry ARMFuncs::RRX_C(InstructionBlock* instruction, llvm::Value 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(); @@ -138,4 +145,27 @@ ARMFuncs::ResultCarry ARMFuncs::ARMExpandImm_C(InstructionBlock *instruction, u3 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 }; } \ No newline at end of file diff --git a/src/binary_translation/ARMFuncs.h b/src/binary_translation/ARMFuncs.h index 86ba43ab9..7f3d9ab34 100644 --- a/src/binary_translation/ARMFuncs.h +++ b/src/binary_translation/ARMFuncs.h @@ -27,6 +27,10 @@ public: { llvm::Value *result, *carry; }; + struct ResultCarryOverflow + { + llvm::Value *result, *carry, *overflow; + }; static ShiftTN DecodeImmShift(InstructionBlock *instruction, u32 type, u32 imm5); static SRType DecodeRegShift(u32 type); @@ -41,5 +45,8 @@ public: 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); }; \ No newline at end of file diff --git a/src/binary_translation/BlockColors.cpp b/src/binary_translation/BlockColors.cpp index 64d58fcc2..67a5b4ef4 100644 --- a/src/binary_translation/BlockColors.cpp +++ b/src/binary_translation/BlockColors.cpp @@ -82,10 +82,24 @@ void BlockColors::AddBasicBlocksToFunction(Function* function, BasicBlock* basic return; } - basic_block->insertInto(function); - auto terminator = basic_block->getTerminator(); - for (auto i = 0; i < terminator->getNumSuccessors(); ++i) - { - AddBasicBlocksToFunction(function, terminator->getSuccessor(i)); - } + std::stack 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); + } + } } \ No newline at end of file diff --git a/src/binary_translation/CMakeLists.txt b/src/binary_translation/CMakeLists.txt index a057311d6..000392281 100644 --- a/src/binary_translation/CMakeLists.txt +++ b/src/binary_translation/CMakeLists.txt @@ -12,6 +12,7 @@ set(SRCS Instructions/Instruction.cpp Instructions/MovShift.cpp Instructions/Branch.cpp + Instructions/Arithmetic.cpp ) set(HEADERS CodeGen.h @@ -28,6 +29,7 @@ set(HEADERS Instructions/Instruction.h Instructions/MovShift.h Instructions/Branch.h + Instructions/Arithmetic.h ) create_directory_groups(${SRCS} ${HEADERS}) diff --git a/src/binary_translation/Instructions/Arithmetic.cpp b/src/binary_translation/Instructions/Arithmetic.cpp new file mode 100644 index 000000000..32368c7ce --- /dev/null +++ b/src/binary_translation/Instructions/Arithmetic.cpp @@ -0,0 +1,128 @@ +#include "Arithmetic.h" +#include "Disassembler.h" +#include +#include + +static RegisterInstruction 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) +{ + switch (op) + { + case Arithmetic::Op::BitwiseAnd: return true; + case Arithmetic::Op::BitwiseXor: return true; + case Arithmetic::Op::BitwiseOr: return true; + case Arithmetic::Op::BitwiseBitClear: return true; + default: break; + } +} + +bool Arithmetic::Decode() +{ + if (ReadFields({ CondDef(), FieldDef<3>(0), FieldDef<4>(&op), FieldDef<1>(&s), 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 && s) 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>(&s), FieldDef<4>(&rn), + FieldDef<4>(&rd), FieldDef<12>(&imm12) })) + { + form = Form::Immediate; + if (rd == Register::PC && s) 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 (s) + { + 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); + } +} \ No newline at end of file diff --git a/src/binary_translation/Instructions/Arithmetic.h b/src/binary_translation/Instructions/Arithmetic.h new file mode 100644 index 000000000..8fb360a5f --- /dev/null +++ b/src/binary_translation/Instructions/Arithmetic.h @@ -0,0 +1,36 @@ +#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 + }; + +public: + virtual bool Decode() override; + void GenerateInstructionCode(InstructionBlock* instruction_block) override; +private: + Form form; + Op op; + bool s; + Register rn; + Register rd; + u32 imm5; + u32 imm12; + u32 type; + Register rm; +}; \ No newline at end of file diff --git a/src/binary_translation/Instructions/Branch.h b/src/binary_translation/Instructions/Branch.h index 469e64eb9..341467893 100644 --- a/src/binary_translation/Instructions/Branch.h +++ b/src/binary_translation/Instructions/Branch.h @@ -1,11 +1,6 @@ #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 Branch : public Instruction { public: diff --git a/src/binary_translation/ModuleGen.cpp b/src/binary_translation/ModuleGen.cpp index e0e828131..51d32237e 100644 --- a/src/binary_translation/ModuleGen.cpp +++ b/src/binary_translation/ModuleGen.cpp @@ -243,7 +243,9 @@ void ModuleGen::DecodeInstructions() for (auto i = Loader::ROMCodeStart; i <= Loader::ROMCodeStart + Loader::ROMCodeSize - 4; i += 4) { ++total; - auto instruction = Disassembler::Disassemble(Memory::Read32(i), i); + 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(this, instruction.release());