mirror of
https://github.com/citra-emu/citra.git
synced 2024-11-24 12:11: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 };
|
||||
}
|
||||
|
||||
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();
|
||||
@ -139,3 +146,26 @@ ARMFuncs::ResultCarry ARMFuncs::ARMExpandImm_C(InstructionBlock *instruction, u3
|
||||
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 };
|
||||
}
|
@ -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);
|
||||
};
|
@ -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<BasicBlock *> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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})
|
||||
|
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 "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:
|
||||
|
@ -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<InstructionBlock>(this, instruction.release());
|
||||
|
Loading…
Reference in New Issue
Block a user