Implement terminal instructions
This commit is contained in:
parent
14388ea690
commit
5711e62419
11 changed files with 269 additions and 36 deletions
|
@ -8,6 +8,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "backend_x64/emit_x64.h"
|
#include "backend_x64/emit_x64.h"
|
||||||
|
#include "common/x64/abi.h"
|
||||||
#include "common/x64/emitter.h"
|
#include "common/x64/emitter.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
|
|
||||||
|
@ -20,6 +21,14 @@ using namespace Gen;
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
namespace BackendX64 {
|
namespace BackendX64 {
|
||||||
|
|
||||||
|
static OpArg MJitStateReg(Arm::Reg reg) {
|
||||||
|
return MDisp(R15, offsetof(JitState, Reg) + sizeof(u32) * static_cast<size_t>(reg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static OpArg MJitStateCpsr() {
|
||||||
|
return MDisp(R15, offsetof(JitState, Cpsr));
|
||||||
|
}
|
||||||
|
|
||||||
// Mapping from opcode to Emit* member function.
|
// Mapping from opcode to Emit* member function.
|
||||||
const static std::map<IR::Opcode, void (EmitX64::*)(IR::Value*)> emit_fns {
|
const static std::map<IR::Opcode, void (EmitX64::*)(IR::Value*)> emit_fns {
|
||||||
#define OPCODE(name, type, ...) { IR::Opcode::name, &EmitX64::Emit##name },
|
#define OPCODE(name, type, ...) { IR::Opcode::name, &EmitX64::Emit##name },
|
||||||
|
@ -51,7 +60,7 @@ CodePtr EmitX64::Emit(Arm::LocationDescriptor descriptor, Dynarmic::IR::Block bl
|
||||||
}
|
}
|
||||||
|
|
||||||
EmitAddCycles(block.cycle_count);
|
EmitAddCycles(block.cycle_count);
|
||||||
EmitReturnToDispatch();
|
EmitTerminal(block.terminal, block.location);
|
||||||
|
|
||||||
return code_ptr;
|
return code_ptr;
|
||||||
}
|
}
|
||||||
|
@ -86,7 +95,7 @@ void EmitX64::EmitGetRegister(IR::Value* value_) {
|
||||||
|
|
||||||
X64Reg result = reg_alloc.DefRegister(value);
|
X64Reg result = reg_alloc.DefRegister(value);
|
||||||
|
|
||||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Reg) + static_cast<size_t>(regref->value) * sizeof(u32)));
|
code->MOV(32, R(result), MJitStateReg(regref->value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitSetRegister(IR::Value* value_) {
|
void EmitX64::EmitSetRegister(IR::Value* value_) {
|
||||||
|
@ -95,7 +104,7 @@ void EmitX64::EmitSetRegister(IR::Value* value_) {
|
||||||
|
|
||||||
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(1).get());
|
X64Reg to_store = reg_alloc.UseRegister(value->GetArg(1).get());
|
||||||
|
|
||||||
code->MOV(32, MDisp(R15, offsetof(JitState, Reg) + static_cast<size_t>(regref->value) * sizeof(u32)), R(to_store));
|
code->MOV(32, MJitStateReg(regref->value), R(to_store));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitGetNFlag(IR::Value* value_) {
|
void EmitX64::EmitGetNFlag(IR::Value* value_) {
|
||||||
|
@ -105,7 +114,7 @@ void EmitX64::EmitGetNFlag(IR::Value* value_) {
|
||||||
|
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
|
||||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
|
code->MOV(32, R(result), MJitStateCpsr());
|
||||||
code->SHR(32, R(result), Imm8(31));
|
code->SHR(32, R(result), Imm8(31));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +126,8 @@ void EmitX64::EmitSetNFlag(IR::Value* value_) {
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
|
||||||
code->SHL(32, R(to_store), Imm8(31));
|
code->SHL(32, R(to_store), Imm8(31));
|
||||||
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 31)));
|
code->AND(32, MJitStateCpsr(), Imm32(~static_cast<u32>(1 << 31)));
|
||||||
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
|
code->OR(32, MJitStateCpsr(), R(to_store));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitGetZFlag(IR::Value* value_) {
|
void EmitX64::EmitGetZFlag(IR::Value* value_) {
|
||||||
|
@ -128,7 +137,7 @@ void EmitX64::EmitGetZFlag(IR::Value* value_) {
|
||||||
|
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
|
||||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
|
code->MOV(32, R(result), MJitStateCpsr());
|
||||||
code->SHR(32, R(result), Imm8(30));
|
code->SHR(32, R(result), Imm8(30));
|
||||||
code->AND(32, R(result), Imm32(1));
|
code->AND(32, R(result), Imm32(1));
|
||||||
}
|
}
|
||||||
|
@ -141,8 +150,8 @@ void EmitX64::EmitSetZFlag(IR::Value* value_) {
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
|
||||||
code->SHL(32, R(to_store), Imm8(30));
|
code->SHL(32, R(to_store), Imm8(30));
|
||||||
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 30)));
|
code->AND(32, MJitStateCpsr(), Imm32(~static_cast<u32>(1 << 30)));
|
||||||
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
|
code->OR(32, MJitStateCpsr(), R(to_store));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitGetCFlag(IR::Value* value_) {
|
void EmitX64::EmitGetCFlag(IR::Value* value_) {
|
||||||
|
@ -152,7 +161,7 @@ void EmitX64::EmitGetCFlag(IR::Value* value_) {
|
||||||
|
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
|
||||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
|
code->MOV(32, R(result), MJitStateCpsr());
|
||||||
code->SHR(32, R(result), Imm8(29));
|
code->SHR(32, R(result), Imm8(29));
|
||||||
code->AND(32, R(result), Imm32(1));
|
code->AND(32, R(result), Imm32(1));
|
||||||
}
|
}
|
||||||
|
@ -165,8 +174,8 @@ void EmitX64::EmitSetCFlag(IR::Value* value_) {
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
|
||||||
code->SHL(32, R(to_store), Imm8(29));
|
code->SHL(32, R(to_store), Imm8(29));
|
||||||
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 29)));
|
code->AND(32, MJitStateCpsr(), Imm32(~static_cast<u32>(1 << 29)));
|
||||||
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
|
code->OR(32, MJitStateCpsr(), R(to_store));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitGetVFlag(IR::Value* value_) {
|
void EmitX64::EmitGetVFlag(IR::Value* value_) {
|
||||||
|
@ -176,7 +185,7 @@ void EmitX64::EmitGetVFlag(IR::Value* value_) {
|
||||||
|
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
|
||||||
code->MOV(32, R(result), MDisp(R15, offsetof(JitState, Cpsr)));
|
code->MOV(32, R(result), MJitStateCpsr());
|
||||||
code->SHR(32, R(result), Imm8(28));
|
code->SHR(32, R(result), Imm8(28));
|
||||||
code->AND(32, R(result), Imm32(1));
|
code->AND(32, R(result), Imm32(1));
|
||||||
}
|
}
|
||||||
|
@ -189,8 +198,8 @@ void EmitX64::EmitSetVFlag(IR::Value* value_) {
|
||||||
// TODO: Flag optimization
|
// TODO: Flag optimization
|
||||||
|
|
||||||
code->SHL(32, R(to_store), Imm8(28));
|
code->SHL(32, R(to_store), Imm8(28));
|
||||||
code->AND(32, MDisp(R15, offsetof(JitState, Cpsr)), Imm32(~static_cast<u32>(1 << 28)));
|
code->AND(32, MJitStateCpsr(), Imm32(~static_cast<u32>(1 << 28)));
|
||||||
code->OR(32, MDisp(R15, offsetof(JitState, Cpsr)), R(to_store));
|
code->OR(32, MJitStateCpsr(), R(to_store));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitGetCarryFromOp(IR::Value*) {
|
void EmitX64::EmitGetCarryFromOp(IR::Value*) {
|
||||||
|
@ -385,14 +394,69 @@ void EmitX64::EmitArithmeticShiftRight(IR::Value* value_) {
|
||||||
|
|
||||||
void EmitX64::EmitAddCycles(size_t cycles) {
|
void EmitX64::EmitAddCycles(size_t cycles) {
|
||||||
ASSERT(cycles < std::numeric_limits<u32>::max());
|
ASSERT(cycles < std::numeric_limits<u32>::max());
|
||||||
code->SUB(64, MDisp(R15, offsetof(JitState, cycles_remaining)), Imm32(cycles));
|
code->SUB(64, MDisp(R15, offsetof(JitState, cycles_remaining)), Imm32(static_cast<u32>(cycles)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitX64::EmitReturnToDispatch() {
|
void EmitX64::EmitTerminal(IR::Terminal terminal, Arm::LocationDescriptor initial_location) {
|
||||||
// TODO: Update cycle counts
|
switch (terminal.which()) {
|
||||||
|
case 1:
|
||||||
|
EmitTerminalInterpret(boost::get<IR::Term::Interpret>(terminal), initial_location);
|
||||||
|
return;
|
||||||
|
case 2:
|
||||||
|
EmitTerminalReturnToDispatch(boost::get<IR::Term::ReturnToDispatch>(terminal), initial_location);
|
||||||
|
return;
|
||||||
|
case 3:
|
||||||
|
EmitTerminalLinkBlock(boost::get<IR::Term::LinkBlock>(terminal), initial_location);
|
||||||
|
return;
|
||||||
|
case 4:
|
||||||
|
EmitTerminalLinkBlockFast(boost::get<IR::Term::LinkBlockFast>(terminal), initial_location);
|
||||||
|
return;
|
||||||
|
case 5:
|
||||||
|
EmitTerminalPopRSBHint(boost::get<IR::Term::PopRSBHint>(terminal), initial_location);
|
||||||
|
return;
|
||||||
|
case 6:
|
||||||
|
EmitTerminalIf(boost::get<IR::Term::If>(terminal), initial_location);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
ASSERT_MSG(0, "Invalid Terminal. Bad programmer.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitTerminalInterpret(IR::Term::Interpret terminal, Arm::LocationDescriptor initial_location) {
|
||||||
|
ASSERT_MSG(terminal.next.TFlag == initial_location.TFlag, "Unimplemented");
|
||||||
|
ASSERT_MSG(terminal.next.EFlag == initial_location.EFlag, "Unimplemented");
|
||||||
|
|
||||||
|
code->MOV(64, R(ABI_PARAM1), Imm64(terminal.next.arm_pc));
|
||||||
|
code->MOV(64, R(ABI_PARAM2), Imm64(reinterpret_cast<u64>(jit_interface)));
|
||||||
|
code->MOV(32, MJitStateReg(Arm::Reg::PC), R(ABI_PARAM1));
|
||||||
|
code->CALL(reinterpret_cast<void*>(cb.InterpreterFallback));
|
||||||
|
code->JMP(routines->RunCodeReturnAddress(), true); // TODO: Check cycles
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitTerminalReturnToDispatch(IR::Term::ReturnToDispatch, Arm::LocationDescriptor initial_location) {
|
||||||
code->JMP(routines->RunCodeReturnAddress(), true);
|
code->JMP(routines->RunCodeReturnAddress(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitTerminalLinkBlock(IR::Term::LinkBlock terminal, Arm::LocationDescriptor initial_location) {
|
||||||
|
ASSERT_MSG(terminal.next.TFlag == initial_location.TFlag, "Unimplemented");
|
||||||
|
ASSERT_MSG(terminal.next.EFlag == initial_location.EFlag, "Unimplemented");
|
||||||
|
|
||||||
|
code->MOV(32, MJitStateReg(Arm::Reg::PC), Imm32(terminal.next.arm_pc));
|
||||||
|
code->JMP(routines->RunCodeReturnAddress(), true); // TODO: Check cycles, Properly do a link
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitTerminalLinkBlockFast(IR::Term::LinkBlockFast terminal, Arm::LocationDescriptor initial_location) {
|
||||||
|
EmitTerminalLinkBlock(IR::Term::LinkBlock{terminal.next}, initial_location); // TODO: Implement
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitTerminalPopRSBHint(IR::Term::PopRSBHint, Arm::LocationDescriptor initial_location) {
|
||||||
|
EmitTerminalReturnToDispatch({}, initial_location); // TODO: Implement RSB
|
||||||
|
}
|
||||||
|
|
||||||
|
void EmitX64::EmitTerminalIf(IR::Term::If terminal, Arm::LocationDescriptor initial_location) {
|
||||||
|
ASSERT_MSG(0, "Unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
} // namespace Dynarmic
|
} // namespace Dynarmic
|
||||||
|
|
|
@ -20,8 +20,8 @@ namespace BackendX64 {
|
||||||
|
|
||||||
class EmitX64 final {
|
class EmitX64 final {
|
||||||
public:
|
public:
|
||||||
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb)
|
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface)
|
||||||
: reg_alloc(code), code(code), routines(routines), cb(cb) {}
|
: reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {}
|
||||||
|
|
||||||
CodePtr Emit(Arm::LocationDescriptor descriptor, IR::Block ir);
|
CodePtr Emit(Arm::LocationDescriptor descriptor, IR::Block ir);
|
||||||
|
|
||||||
|
@ -53,7 +53,14 @@ public:
|
||||||
void EmitArithmeticShiftRight(IR::Value* value);
|
void EmitArithmeticShiftRight(IR::Value* value);
|
||||||
|
|
||||||
void EmitAddCycles(size_t cycles);
|
void EmitAddCycles(size_t cycles);
|
||||||
void EmitReturnToDispatch();
|
|
||||||
|
void EmitTerminal(IR::Terminal terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
void EmitTerminalInterpret(IR::Term::Interpret terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
void EmitTerminalReturnToDispatch(IR::Term::ReturnToDispatch terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
void EmitTerminalLinkBlock(IR::Term::LinkBlock terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
void EmitTerminalLinkBlockFast(IR::Term::LinkBlockFast terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
void EmitTerminalPopRSBHint(IR::Term::PopRSBHint terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
void EmitTerminalIf(IR::Term::If terminal, Arm::LocationDescriptor initial_location);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::set<IR::Value*> inhibit_emission;
|
std::set<IR::Value*> inhibit_emission;
|
||||||
|
@ -62,6 +69,7 @@ private:
|
||||||
Gen::XEmitter* code;
|
Gen::XEmitter* code;
|
||||||
Routines* routines;
|
Routines* routines;
|
||||||
UserCallbacks cb;
|
UserCallbacks cb;
|
||||||
|
Jit* jit_interface;
|
||||||
std::unordered_map<Arm::LocationDescriptor, CodePtr, Arm::LocationDescriptorHash> basic_blocks;
|
std::unordered_map<Arm::LocationDescriptor, CodePtr, Arm::LocationDescriptorHash> basic_blocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ struct BlockOfCode : Gen::XCodeBlock {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Jit::Impl {
|
struct Jit::Impl {
|
||||||
Impl(UserCallbacks callbacks) : emitter(&block_of_code, &routines, callbacks), callbacks(callbacks) {}
|
Impl(Jit* jit, UserCallbacks callbacks) : emitter(&block_of_code, &routines, callbacks, jit), callbacks(callbacks) {}
|
||||||
|
|
||||||
JitState jit_state{};
|
JitState jit_state{};
|
||||||
Routines routines{};
|
Routines routines{};
|
||||||
|
@ -57,7 +57,7 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Jit::Jit(UserCallbacks callbacks) : callbacks(callbacks), impl(std::make_unique<Impl>(callbacks)) {}
|
Jit::Jit(UserCallbacks callbacks) : callbacks(callbacks), impl(std::make_unique<Impl>(this, callbacks)) {}
|
||||||
|
|
||||||
Jit::~Jit() {}
|
Jit::~Jit() {}
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ static const std::array<Thumb1Matcher<V>, 7> g_thumb1_instruction_table {{
|
||||||
}};
|
}};
|
||||||
|
|
||||||
template<typename Visitor>
|
template<typename Visitor>
|
||||||
boost::optional<const Thumb1Matcher<Visitor>&> DecodeThumb1(u16 instruction) {
|
boost::optional<const Thumb1Matcher<Visitor>&> DecodeThumb16(u16 instruction) {
|
||||||
const auto& table = g_thumb1_instruction_table<Visitor>;
|
const auto& table = g_thumb1_instruction_table<Visitor>;
|
||||||
auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
|
auto matches_instruction = [instruction](const auto& matcher){ return matcher.Matches(instruction); };
|
||||||
|
|
||||||
|
|
|
@ -149,6 +149,84 @@ private:
|
||||||
std::vector<ValueWeakPtr> args;
|
std::vector<ValueWeakPtr> args;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace Term {
|
||||||
|
|
||||||
|
struct Invalid {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction calls the interpreter, starting at `next`.
|
||||||
|
* The interpreter must interpret at least 1 instruction but may choose to interpret more.
|
||||||
|
*/
|
||||||
|
struct Interpret {
|
||||||
|
explicit Interpret(const Arm::LocationDescriptor& next_) : next(next_) {}
|
||||||
|
Arm::LocationDescriptor next; ///< Location at which interpretation starts.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction returns control to the dispatcher.
|
||||||
|
* The dispatcher will use the value in R15 to determine what comes next.
|
||||||
|
*/
|
||||||
|
struct ReturnToDispatch {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction jumps to the basic block described by `next` if we have enough
|
||||||
|
* cycles remaining. If we do not have enough cycles remaining, we return to the
|
||||||
|
* dispatcher, which will return control to the host.
|
||||||
|
*/
|
||||||
|
struct LinkBlock {
|
||||||
|
explicit LinkBlock(const Arm::LocationDescriptor& next_) : next(next_) {}
|
||||||
|
Arm::LocationDescriptor next; ///< Location descriptor for next block.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction jumps to the basic block described by `next` unconditionally.
|
||||||
|
* This is an optimization and MUST only be emitted when this is guaranteed not to result
|
||||||
|
* in hanging, even in the face of other optimizations. (In practice, this means that only
|
||||||
|
* forward jumps to short-ish blocks would use this instruction.)
|
||||||
|
* A backend that doesn't support this optimization may choose to implement this exactly
|
||||||
|
* as LinkBlock.
|
||||||
|
*/
|
||||||
|
struct LinkBlockFast {
|
||||||
|
explicit LinkBlockFast(const Arm::LocationDescriptor& next_) : next(next_) {}
|
||||||
|
Arm::LocationDescriptor next; ///< Location descriptor for next block.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction checks the top of the Return Stack Buffer against R15.
|
||||||
|
* If RSB lookup fails, control is returned to the dispatcher.
|
||||||
|
* This is an optimization for faster function calls. A backend that doesn't support
|
||||||
|
* this optimization or doesn't have a RSB may choose to implement this exactly as
|
||||||
|
* ReturnToDispatch.
|
||||||
|
*/
|
||||||
|
struct PopRSBHint {};
|
||||||
|
|
||||||
|
struct If;
|
||||||
|
/// A Terminal is the terminal instruction in a MicroBlock.
|
||||||
|
using Terminal = boost::variant<
|
||||||
|
Invalid,
|
||||||
|
Interpret,
|
||||||
|
ReturnToDispatch,
|
||||||
|
LinkBlock,
|
||||||
|
LinkBlockFast,
|
||||||
|
PopRSBHint,
|
||||||
|
boost::recursive_wrapper<If>
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This terminal instruction conditionally executes one terminal or another depending
|
||||||
|
* on the run-time state of the ARM flags.
|
||||||
|
*/
|
||||||
|
struct If {
|
||||||
|
If(Arm::Cond if_, Terminal then_, Terminal else_) : if_(if_), then_(then_), else_(else_) {}
|
||||||
|
Arm::Cond if_;
|
||||||
|
Terminal then_;
|
||||||
|
Terminal else_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Term
|
||||||
|
|
||||||
|
using Term::Terminal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic block. It consists of zero or more instructions followed by exactly one terminal.
|
* A basic block. It consists of zero or more instructions followed by exactly one terminal.
|
||||||
* Note that this is a linear IR and not a pure tree-based IR: i.e.: there is an ordering to
|
* Note that this is a linear IR and not a pure tree-based IR: i.e.: there is an ordering to
|
||||||
|
@ -161,6 +239,7 @@ public:
|
||||||
|
|
||||||
Arm::LocationDescriptor location;
|
Arm::LocationDescriptor location;
|
||||||
std::list<ValuePtr> instructions;
|
std::list<ValuePtr> instructions;
|
||||||
|
Terminal terminal = Term::Invalid{};
|
||||||
size_t cycle_count = 0;
|
size_t cycle_count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ir_emitter.h"
|
#include "common/assert.h"
|
||||||
|
#include "frontend/ir_emitter.h"
|
||||||
|
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
namespace Arm {
|
namespace Arm {
|
||||||
|
@ -73,6 +74,11 @@ IREmitter::ResultAndCarry IREmitter::ArithmeticShiftRight(IR::ValuePtr value_in,
|
||||||
return {result, carry_out};
|
return {result, carry_out};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IREmitter::SetTerm(const IR::Terminal& terminal) {
|
||||||
|
ASSERT_MSG(block.terminal.which() == 0, "Terminal has already been set.");
|
||||||
|
block.terminal = terminal;
|
||||||
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args) {
|
IR::ValuePtr IREmitter::Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args) {
|
||||||
auto inst = std::make_shared<IR::Inst>(op);
|
auto inst = std::make_shared<IR::Inst>(op);
|
||||||
assert(args.size() == inst->NumArgs());
|
assert(args.size() == inst->NumArgs());
|
||||||
|
|
|
@ -45,6 +45,8 @@ public:
|
||||||
ResultAndCarry LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
ResultAndCarry LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
||||||
ResultAndCarry ArithmeticShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
ResultAndCarry ArithmeticShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
||||||
|
|
||||||
|
void SetTerm(const IR::Terminal& terminal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IR::ValuePtr Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args);
|
IR::ValuePtr Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args);
|
||||||
IR::ValuePtr RegRef(Reg reg);
|
IR::ValuePtr RegRef(Reg reg);
|
||||||
|
|
|
@ -22,6 +22,11 @@ struct TranslatorVisitor final {
|
||||||
|
|
||||||
IREmitter ir;
|
IREmitter ir;
|
||||||
|
|
||||||
|
bool TranslateThisInstruction() {
|
||||||
|
ir.SetTerm(IR::Term::Interpret(ir.current_location));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool thumb1_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
bool thumb1_LSL_imm(Imm5 imm5, Reg m, Reg d) {
|
||||||
u8 shift_n = imm5;
|
u8 shift_n = imm5;
|
||||||
// LSLS <Rd>, <Rm>, #<imm5>
|
// LSLS <Rd>, <Rm>, #<imm5>
|
||||||
|
@ -94,7 +99,7 @@ struct TranslatorVisitor final {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool thumb1_UDF() {
|
bool thumb1_UDF() {
|
||||||
return false;
|
return TranslateThisInstruction();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,12 +113,13 @@ static std::tuple<u32, ThumbInstSize> ReadThumbInstruction(u32 arm_pc, MemoryRea
|
||||||
first_part >>= 16;
|
first_part >>= 16;
|
||||||
first_part &= 0xFFFF;
|
first_part &= 0xFFFF;
|
||||||
|
|
||||||
if ((first_part & 0xF800) != 0xE800 && (first_part & 0xF000) != 0xF000) {
|
if ((first_part & 0xF800) <= 0xE800) {
|
||||||
// 16-bit thumb instruction
|
// 16-bit thumb instruction
|
||||||
return std::make_tuple(first_part, ThumbInstSize::Thumb16);
|
return std::make_tuple(first_part, ThumbInstSize::Thumb16);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 32-bit thumb instruction
|
// 32-bit thumb instruction
|
||||||
|
// These always start with 0b11101, 0b11110 or 0b11111.
|
||||||
|
|
||||||
u32 second_part = (*memory_read_32)((arm_pc+2) & 0xFFFFFFFC);
|
u32 second_part = (*memory_read_32)((arm_pc+2) & 0xFFFFFFFC);
|
||||||
if (((arm_pc+2) & 0x2) != 0)
|
if (((arm_pc+2) & 0x2) != 0)
|
||||||
|
@ -135,7 +141,7 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryRead32FuncType mem
|
||||||
std::tie(thumb_instruction, inst_size) = ReadThumbInstruction(arm_pc, memory_read_32);
|
std::tie(thumb_instruction, inst_size) = ReadThumbInstruction(arm_pc, memory_read_32);
|
||||||
|
|
||||||
if (inst_size == ThumbInstSize::Thumb16) {
|
if (inst_size == ThumbInstSize::Thumb16) {
|
||||||
auto decoder = DecodeThumb1<TranslatorVisitor>(static_cast<u16>(thumb_instruction));
|
auto decoder = DecodeThumb16<TranslatorVisitor>(static_cast<u16>(thumb_instruction));
|
||||||
if (decoder) {
|
if (decoder) {
|
||||||
should_continue = decoder->call(visitor, static_cast<u16>(thumb_instruction));
|
should_continue = decoder->call(visitor, static_cast<u16>(thumb_instruction));
|
||||||
} else {
|
} else {
|
||||||
|
@ -151,7 +157,7 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryRead32FuncType mem
|
||||||
ASSERT_MSG(0, "Unimplemented");
|
ASSERT_MSG(0, "Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor.ir.current_location.arm_pc += inst_size == ThumbInstSize::Thumb16 ? 2 : 4;
|
visitor.ir.current_location.arm_pc += (inst_size == ThumbInstSize::Thumb16) ? 2 : 4;
|
||||||
visitor.ir.block.cycle_count++;
|
visitor.ir.block.cycle_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@ set(HEADERS
|
||||||
skyeye_interpreter/dyncom/arm_dyncom_interpreter.h
|
skyeye_interpreter/dyncom/arm_dyncom_interpreter.h
|
||||||
skyeye_interpreter/dyncom/arm_dyncom_run.h
|
skyeye_interpreter/dyncom/arm_dyncom_run.h
|
||||||
skyeye_interpreter/dyncom/arm_dyncom_thumb.h
|
skyeye_interpreter/dyncom/arm_dyncom_thumb.h
|
||||||
|
skyeye_interpreter/skyeye_common/arm_regformat.h
|
||||||
skyeye_interpreter/skyeye_common/armstate.h
|
skyeye_interpreter/skyeye_common/armstate.h
|
||||||
skyeye_interpreter/skyeye_common/armsupp.h
|
skyeye_interpreter/skyeye_common/armsupp.h
|
||||||
skyeye_interpreter/skyeye_common/arm_regformat.h
|
|
||||||
skyeye_interpreter/skyeye_common/vfp/asm_vfp.h
|
skyeye_interpreter/skyeye_common/vfp/asm_vfp.h
|
||||||
skyeye_interpreter/skyeye_common/vfp/vfp.h
|
skyeye_interpreter/skyeye_common/vfp/vfp.h
|
||||||
skyeye_interpreter/skyeye_common/vfp/vfp_helper.h
|
skyeye_interpreter/skyeye_common/vfp/vfp_helper.h
|
||||||
|
|
|
@ -4,5 +4,47 @@
|
||||||
* General Public License version 2 or any later version.
|
* General Public License version 2 or any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "interface/interface.h"
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "interface/interface.h"
|
||||||
|
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||||
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
|
|
||||||
|
std::array<u16, 1024> code_mem{};
|
||||||
|
|
||||||
|
u32 MemoryRead32(u32 vaddr);
|
||||||
|
void InterpreterFallback(u32 pc, Dynarmic::Jit* jit);
|
||||||
|
Dynarmic::UserCallbacks GetUserCallbacks();
|
||||||
|
|
||||||
|
u32 MemoryRead32(u32 vaddr) {
|
||||||
|
if (vaddr < code_mem.size() * sizeof(u16)) {
|
||||||
|
size_t index = vaddr / sizeof(u16);
|
||||||
|
return code_mem[index] | (code_mem[index+1] << 16);
|
||||||
|
}
|
||||||
|
return vaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterpreterFallback(u32 pc, Dynarmic::Jit* jit) {
|
||||||
|
ARMul_State interp_state{USER32MODE};
|
||||||
|
interp_state.user_callbacks = GetUserCallbacks();
|
||||||
|
interp_state.NumInstrsToExecute = 1;
|
||||||
|
|
||||||
|
interp_state.Reg = jit->Regs();
|
||||||
|
interp_state.Cpsr = jit->Cpsr();
|
||||||
|
interp_state.Reg[15] = pc;
|
||||||
|
|
||||||
|
InterpreterClearCache();
|
||||||
|
InterpreterMainLoop(&interp_state);
|
||||||
|
|
||||||
|
jit->Regs() = interp_state.Reg;
|
||||||
|
jit->Cpsr() = interp_state.Cpsr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dynarmic::UserCallbacks GetUserCallbacks() {
|
||||||
|
Dynarmic::UserCallbacks user_callbacks{};
|
||||||
|
user_callbacks.MemoryRead32 = &MemoryRead32;
|
||||||
|
user_callbacks.InterpreterFallback = &InterpreterFallback;
|
||||||
|
return user_callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,19 +8,43 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "interface/interface.h"
|
#include "interface/interface.h"
|
||||||
|
#include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h"
|
||||||
|
#include "skyeye_interpreter/skyeye_common/armstate.h"
|
||||||
|
|
||||||
std::array<u32, 1024> code_mem{};
|
std::array<u16, 1024> code_mem{};
|
||||||
|
|
||||||
|
u32 MemoryRead32(u32 vaddr);
|
||||||
|
void InterpreterFallback(u32 pc, Dynarmic::Jit* jit);
|
||||||
|
Dynarmic::UserCallbacks GetUserCallbacks();
|
||||||
|
|
||||||
u32 MemoryRead32(u32 vaddr) {
|
u32 MemoryRead32(u32 vaddr) {
|
||||||
if (vaddr < code_mem.size() * sizeof(u32)) {
|
if (vaddr < code_mem.size() * sizeof(u16)) {
|
||||||
return code_mem[vaddr / sizeof(u32)];
|
size_t index = vaddr / sizeof(u16);
|
||||||
|
return code_mem[index] | (code_mem[index+1] << 16);
|
||||||
}
|
}
|
||||||
return vaddr;
|
return vaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InterpreterFallback(u32 pc, Dynarmic::Jit* jit) {
|
||||||
|
ARMul_State interp_state{USER32MODE};
|
||||||
|
interp_state.user_callbacks = GetUserCallbacks();
|
||||||
|
interp_state.NumInstrsToExecute = 1;
|
||||||
|
|
||||||
|
interp_state.Reg = jit->Regs();
|
||||||
|
interp_state.Cpsr = jit->Cpsr();
|
||||||
|
interp_state.Reg[15] = pc;
|
||||||
|
|
||||||
|
InterpreterClearCache();
|
||||||
|
InterpreterMainLoop(&interp_state);
|
||||||
|
|
||||||
|
jit->Regs() = interp_state.Reg;
|
||||||
|
jit->Cpsr() = interp_state.Cpsr;
|
||||||
|
}
|
||||||
|
|
||||||
Dynarmic::UserCallbacks GetUserCallbacks() {
|
Dynarmic::UserCallbacks GetUserCallbacks() {
|
||||||
Dynarmic::UserCallbacks user_callbacks{};
|
Dynarmic::UserCallbacks user_callbacks{};
|
||||||
user_callbacks.MemoryRead32 = &MemoryRead32;
|
user_callbacks.MemoryRead32 = &MemoryRead32;
|
||||||
|
user_callbacks.InterpreterFallback = &InterpreterFallback;
|
||||||
return user_callbacks;
|
return user_callbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +52,7 @@ TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
|
||||||
Dynarmic::Jit jit{GetUserCallbacks()};
|
Dynarmic::Jit jit{GetUserCallbacks()};
|
||||||
code_mem.fill({});
|
code_mem.fill({});
|
||||||
code_mem[0] = 0x0088; // lsls r0, r1, #2
|
code_mem[0] = 0x0088; // lsls r0, r1, #2
|
||||||
code_mem[1] = 0xDE00; // udf #0
|
code_mem[1] = 0xE7FE; // b +#0
|
||||||
|
|
||||||
jit.Regs()[0] = 1;
|
jit.Regs()[0] = 1;
|
||||||
jit.Regs()[1] = 2;
|
jit.Regs()[1] = 2;
|
||||||
|
@ -39,6 +63,7 @@ TEST_CASE( "thumb: lsls r0, r1, #2", "[thumb]" ) {
|
||||||
|
|
||||||
REQUIRE( jit.Regs()[0] == 8 );
|
REQUIRE( jit.Regs()[0] == 8 );
|
||||||
REQUIRE( jit.Regs()[1] == 2 );
|
REQUIRE( jit.Regs()[1] == 2 );
|
||||||
|
REQUIRE( jit.Regs()[15] == 2 );
|
||||||
REQUIRE( jit.Cpsr() == 0x00000030 );
|
REQUIRE( jit.Cpsr() == 0x00000030 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +71,7 @@ TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
|
||||||
Dynarmic::Jit jit{GetUserCallbacks()};
|
Dynarmic::Jit jit{GetUserCallbacks()};
|
||||||
code_mem.fill({});
|
code_mem.fill({});
|
||||||
code_mem[0] = 0x07C8; // lsls r0, r1, #31
|
code_mem[0] = 0x07C8; // lsls r0, r1, #31
|
||||||
code_mem[1] = 0xDE00; // udf #0
|
code_mem[1] = 0xE7FE; // b +#0
|
||||||
|
|
||||||
jit.Regs()[0] = 1;
|
jit.Regs()[0] = 1;
|
||||||
jit.Regs()[1] = 0xFFFFFFFF;
|
jit.Regs()[1] = 0xFFFFFFFF;
|
||||||
|
@ -57,5 +82,6 @@ TEST_CASE( "thumb: lsls r0, r1, #31", "[thumb]" ) {
|
||||||
|
|
||||||
REQUIRE( jit.Regs()[0] == 0x80000000 );
|
REQUIRE( jit.Regs()[0] == 0x80000000 );
|
||||||
REQUIRE( jit.Regs()[1] == 0xffffffff );
|
REQUIRE( jit.Regs()[1] == 0xffffffff );
|
||||||
|
REQUIRE( jit.Regs()[15] == 2 );
|
||||||
REQUIRE( jit.Cpsr() == 0x20000030 ); // C flag, Thumb, User-mode
|
REQUIRE( jit.Cpsr() == 0x20000030 ); // C flag, Thumb, User-mode
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue