mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 14:31:04 +00:00
Added arithmetic instructions (add, sub, and, or, ...)
This commit is contained in:
parent
840ab06561
commit
3ae4d8b646
@ -131,6 +131,13 @@ ARMFuncs::ResultCarry ARMFuncs::RRX_C(InstructionBlock* instruction, llvm::Value
|
|||||||
return{ result, carry };
|
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)
|
ARMFuncs::ResultCarry ARMFuncs::ARMExpandImm_C(InstructionBlock *instruction, u32 imm12, llvm::Value* carry)
|
||||||
{
|
{
|
||||||
auto ir_builder = instruction->IrBuilder();
|
auto ir_builder = instruction->IrBuilder();
|
||||||
@ -139,3 +146,26 @@ ARMFuncs::ResultCarry ARMFuncs::ARMExpandImm_C(InstructionBlock *instruction, u3
|
|||||||
auto shift = ir_builder->getInt32(2 * (imm12 >> 8));
|
auto shift = ir_builder->getInt32(2 * (imm12 >> 8));
|
||||||
return Shift_C(instruction, value, SRType::ROR, shift, carry);
|
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 };
|
||||||
|
}
|
@ -27,6 +27,10 @@ public:
|
|||||||
{
|
{
|
||||||
llvm::Value *result, *carry;
|
llvm::Value *result, *carry;
|
||||||
};
|
};
|
||||||
|
struct ResultCarryOverflow
|
||||||
|
{
|
||||||
|
llvm::Value *result, *carry, *overflow;
|
||||||
|
};
|
||||||
|
|
||||||
static ShiftTN DecodeImmShift(InstructionBlock *instruction, u32 type, u32 imm5);
|
static ShiftTN DecodeImmShift(InstructionBlock *instruction, u32 type, u32 imm5);
|
||||||
static SRType DecodeRegShift(u32 type);
|
static SRType DecodeRegShift(u32 type);
|
||||||
@ -41,5 +45,8 @@ public:
|
|||||||
static ResultCarry ROR_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 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 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);
|
||||||
};
|
};
|
@ -82,10 +82,24 @@ void BlockColors::AddBasicBlocksToFunction(Function* function, BasicBlock* basic
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_block->insertInto(function);
|
std::stack<BasicBlock *> basic_blocks;
|
||||||
auto terminator = basic_block->getTerminator();
|
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)
|
for (auto i = 0; i < terminator->getNumSuccessors(); ++i)
|
||||||
{
|
{
|
||||||
AddBasicBlocksToFunction(function, terminator->getSuccessor(i));
|
auto next = terminator->getSuccessor(i);
|
||||||
|
if (next->getParent())
|
||||||
|
{
|
||||||
|
assert(next->getParent() == function);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
basic_blocks.push(next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,6 +12,7 @@ set(SRCS
|
|||||||
Instructions/Instruction.cpp
|
Instructions/Instruction.cpp
|
||||||
Instructions/MovShift.cpp
|
Instructions/MovShift.cpp
|
||||||
Instructions/Branch.cpp
|
Instructions/Branch.cpp
|
||||||
|
Instructions/Arithmetic.cpp
|
||||||
)
|
)
|
||||||
set(HEADERS
|
set(HEADERS
|
||||||
CodeGen.h
|
CodeGen.h
|
||||||
@ -28,6 +29,7 @@ set(HEADERS
|
|||||||
Instructions/Instruction.h
|
Instructions/Instruction.h
|
||||||
Instructions/MovShift.h
|
Instructions/MovShift.h
|
||||||
Instructions/Branch.h
|
Instructions/Branch.h
|
||||||
|
Instructions/Arithmetic.h
|
||||||
)
|
)
|
||||||
|
|
||||||
create_directory_groups(${SRCS} ${HEADERS})
|
create_directory_groups(${SRCS} ${HEADERS})
|
||||||
|
128
src/binary_translation/Instructions/Arithmetic.cpp
Normal file
128
src/binary_translation/Instructions/Arithmetic.cpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
#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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
36
src/binary_translation/Instructions/Arithmetic.h
Normal file
36
src/binary_translation/Instructions/Arithmetic.h
Normal file
@ -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;
|
||||||
|
};
|
@ -1,11 +1,6 @@
|
|||||||
#include "Instruction.h"
|
#include "Instruction.h"
|
||||||
#include "Types.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
|
class Branch : public Instruction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -243,7 +243,9 @@ void ModuleGen::DecodeInstructions()
|
|||||||
for (auto i = Loader::ROMCodeStart; i <= Loader::ROMCodeStart + Loader::ROMCodeSize - 4; i += 4)
|
for (auto i = Loader::ROMCodeStart; i <= Loader::ROMCodeStart + Loader::ROMCodeSize - 4; i += 4)
|
||||||
{
|
{
|
||||||
++total;
|
++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;
|
if (instruction == nullptr) continue;
|
||||||
++generated;
|
++generated;
|
||||||
auto instruction_block = std::make_unique<InstructionBlock>(this, instruction.release());
|
auto instruction_block = std::make_unique<InstructionBlock>(this, instruction.release());
|
||||||
|
Loading…
Reference in New Issue
Block a user