More Speed
This commit is contained in:
parent
5fbfc6c155
commit
51448aa06d
16 changed files with 1066 additions and 955 deletions
|
@ -7,11 +7,13 @@ option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON)
|
||||||
# Compiler flags
|
# Compiler flags
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
add_compile_options(--std=c++14 -Wall -Werror -Wextra -pedantic -Wfatal-errors -Wno-unused-parameter -Wno-missing-braces)
|
add_compile_options(--std=c++14 -Wall -Werror -Wextra -pedantic -Wfatal-errors -Wno-unused-parameter -Wno-missing-braces)
|
||||||
|
add_compile_options(-DBOOST_SYSTEM_NO_DEPRECATED)
|
||||||
if (ARCHITECTURE_x86_64)
|
if (ARCHITECTURE_x86_64)
|
||||||
add_compile_options(-msse4.1)
|
add_compile_options(-msse4.1)
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
add_compile_options(/W3 /MP /Zi /Zo /EHsc /WX)
|
add_compile_options(/W3 /MP /Zi /Zo /EHsc /WX)
|
||||||
|
add_compile_options(/DBOOST_SYSTEM_NO_DEPRECATED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# This function should be passed a list of all files in a target. It will automatically generate
|
# This function should be passed a list of all files in a target. It will automatically generate
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,7 +23,7 @@ public:
|
||||||
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface)
|
EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* jit_interface)
|
||||||
: reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {}
|
: reg_alloc(code), code(code), routines(routines), cb(cb), jit_interface(jit_interface) {}
|
||||||
|
|
||||||
CodePtr Emit(const Arm::LocationDescriptor descriptor, const IR::Block& ir);
|
CodePtr Emit(const Arm::LocationDescriptor descriptor, IR::Block& ir);
|
||||||
|
|
||||||
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
CodePtr GetBasicBlock(Arm::LocationDescriptor descriptor) {
|
||||||
auto iter = basic_blocks.find(descriptor);
|
auto iter = basic_blocks.find(descriptor);
|
||||||
|
@ -34,53 +34,49 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Microinstruction emitters
|
// Microinstruction emitters
|
||||||
void EmitImmU1(IR::Value* value);
|
void EmitGetRegister(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitImmU8(IR::Value* value);
|
void EmitSetRegister(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitImmU32(IR::Value* value);
|
void EmitGetNFlag(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitImmRegRef(IR::Value* value);
|
void EmitSetNFlag(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitGetRegister(IR::Value* value);
|
void EmitGetZFlag(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitSetRegister(IR::Value* value);
|
void EmitSetZFlag(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitGetNFlag(IR::Value* value);
|
void EmitGetCFlag(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitSetNFlag(IR::Value* value);
|
void EmitSetCFlag(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitGetZFlag(IR::Value* value);
|
void EmitGetVFlag(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitSetZFlag(IR::Value* value);
|
void EmitSetVFlag(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitGetCFlag(IR::Value* value);
|
void EmitBXWritePC(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitSetCFlag(IR::Value* value);
|
void EmitCallSupervisor(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitGetVFlag(IR::Value* value);
|
void EmitGetCarryFromOp(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitSetVFlag(IR::Value* value);
|
void EmitGetOverflowFromOp(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitBXWritePC(IR::Value* value);
|
void EmitLeastSignificantHalf(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitCallSupervisor(IR::Value* value);
|
void EmitLeastSignificantByte(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitGetCarryFromOp(IR::Value* value);
|
void EmitMostSignificantBit(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitGetOverflowFromOp(IR::Value* value);
|
void EmitIsZero(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitLeastSignificantHalf(IR::Value* value);
|
void EmitLogicalShiftLeft(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitLeastSignificantByte(IR::Value* value);
|
void EmitLogicalShiftRight(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitMostSignificantBit(IR::Value* value);
|
void EmitArithmeticShiftRight(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitIsZero(IR::Value* value);
|
void EmitRotateRight(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitLogicalShiftLeft(IR::Value* value);
|
void EmitAddWithCarry(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitLogicalShiftRight(IR::Value* value);
|
void EmitSubWithCarry(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitArithmeticShiftRight(IR::Value* value);
|
void EmitAnd(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitRotateRight(IR::Value* value);
|
void EmitEor(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitAddWithCarry(IR::Value* value);
|
void EmitOr(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitSubWithCarry(IR::Value* value);
|
void EmitNot(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitAnd(IR::Value* value);
|
void EmitSignExtendHalfToWord(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitEor(IR::Value* value);
|
void EmitSignExtendByteToWord(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitOr(IR::Value* value);
|
void EmitZeroExtendHalfToWord(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitNot(IR::Value* value);
|
void EmitZeroExtendByteToWord(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitSignExtendHalfToWord(IR::Value* value);
|
void EmitByteReverseWord(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitSignExtendByteToWord(IR::Value* value);
|
void EmitByteReverseHalf(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitZeroExtendHalfToWord(IR::Value* value);
|
void EmitByteReverseDual(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitZeroExtendByteToWord(IR::Value* value);
|
void EmitReadMemory8(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitByteReverseWord(IR::Value* value);
|
void EmitReadMemory16(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitByteReverseHalf(IR::Value* value);
|
void EmitReadMemory32(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitByteReverseDual(IR::Value* value);
|
void EmitReadMemory64(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitReadMemory8(IR::Value* value);
|
void EmitWriteMemory8(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitReadMemory16(IR::Value* value);
|
void EmitWriteMemory16(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitReadMemory32(IR::Value* value);
|
void EmitWriteMemory32(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitReadMemory64(IR::Value* value);
|
void EmitWriteMemory64(IR::Block& block, IR::Inst* inst);
|
||||||
void EmitWriteMemory8(IR::Value* value);
|
|
||||||
void EmitWriteMemory16(IR::Value* value);
|
|
||||||
void EmitWriteMemory32(IR::Value* value);
|
|
||||||
void EmitWriteMemory64(IR::Value* value);
|
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
void EmitAddCycles(size_t cycles);
|
void EmitAddCycles(size_t cycles);
|
||||||
|
|
|
@ -14,36 +14,22 @@
|
||||||
namespace Dynarmic {
|
namespace Dynarmic {
|
||||||
namespace BackendX64 {
|
namespace BackendX64 {
|
||||||
|
|
||||||
// TODO: Just turn this into a function that indexes a std::array.
|
static Gen::X64Reg HostLocToX64(HostLoc loc) {
|
||||||
const static std::map<HostLoc, Gen::X64Reg> hostloc_to_x64 = {
|
DEBUG_ASSERT(HostLocIsRegister(loc));
|
||||||
{ HostLoc::RAX, Gen::RAX },
|
// HostLoc is ordered such that the numbers line up.
|
||||||
{ HostLoc::RBX, Gen::RBX },
|
return static_cast<Gen::X64Reg>(loc);
|
||||||
{ HostLoc::RCX, Gen::RCX },
|
}
|
||||||
{ HostLoc::RDX, Gen::RDX },
|
|
||||||
{ HostLoc::RSI, Gen::RSI },
|
|
||||||
{ HostLoc::RDI, Gen::RDI },
|
|
||||||
{ HostLoc::RBP, Gen::RBP },
|
|
||||||
{ HostLoc::RSP, Gen::RSP },
|
|
||||||
{ HostLoc::R8, Gen::R8 },
|
|
||||||
{ HostLoc::R9, Gen::R9 },
|
|
||||||
{ HostLoc::R10, Gen::R10 },
|
|
||||||
{ HostLoc::R11, Gen::R11 },
|
|
||||||
{ HostLoc::R12, Gen::R12 },
|
|
||||||
{ HostLoc::R13, Gen::R13 },
|
|
||||||
{ HostLoc::R14, Gen::R14 },
|
|
||||||
};
|
|
||||||
|
|
||||||
static Gen::OpArg SpillToOpArg(HostLoc loc) {
|
static Gen::OpArg SpillToOpArg(HostLoc loc) {
|
||||||
ASSERT(HostLocIsSpill(loc));
|
DEBUG_ASSERT(HostLocIsSpill(loc));
|
||||||
|
|
||||||
size_t i = static_cast<size_t>(loc) - static_cast<size_t>(HostLoc::FirstSpill);
|
size_t i = static_cast<size_t>(loc) - static_cast<size_t>(HostLoc::FirstSpill);
|
||||||
return Gen::MDisp(Gen::R15, static_cast<int>(offsetof(JitState, Spill) + i * sizeof(u32)));
|
return Gen::MDisp(Gen::R15, static_cast<int>(offsetof(JitState, Spill) + i * sizeof(u32)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Gen::X64Reg RegAlloc::DefRegister(IR::Value* def_value, std::initializer_list<HostLoc> desired_locations) {
|
Gen::X64Reg RegAlloc::DefRegister(IR::Inst* def_inst, std::initializer_list<HostLoc> desired_locations) {
|
||||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||||
ASSERT_MSG(remaining_uses.find(def_value) == remaining_uses.end(), "def_value has already been defined");
|
DEBUG_ASSERT_MSG(ValueLocations(def_inst).empty(), "def_inst has already been defined");
|
||||||
ASSERT_MSG(ValueLocations(def_value).empty(), "def_value has already been defined");
|
|
||||||
|
|
||||||
HostLoc location = SelectARegister(desired_locations);
|
HostLoc location = SelectARegister(desired_locations);
|
||||||
|
|
||||||
|
@ -52,43 +38,54 @@ Gen::X64Reg RegAlloc::DefRegister(IR::Value* def_value, std::initializer_list<Ho
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
hostloc_state[location] = HostLocState::Def;
|
hostloc_state[static_cast<size_t>(location)] = HostLocState::Def;
|
||||||
hostloc_to_value[location] = def_value;
|
hostloc_to_inst[static_cast<size_t>(location)] = def_inst;
|
||||||
remaining_uses[def_value] = def_value->NumUses();
|
|
||||||
|
|
||||||
return hostloc_to_x64.at(location);
|
return HostLocToX64(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gen::X64Reg RegAlloc::UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations) {
|
Gen::X64Reg RegAlloc::UseDefRegister(IR::Value use_value, IR::Inst* def_inst, std::initializer_list<HostLoc> desired_locations) {
|
||||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
if (!use_value.IsImmediate()) {
|
||||||
ASSERT_MSG(remaining_uses.find(def_value) == remaining_uses.end(), "def_value has already been defined");
|
return UseDefRegister(use_value.GetInst(), def_inst, desired_locations);
|
||||||
ASSERT_MSG(ValueLocations(def_value).empty(), "def_value has already been defined");
|
}
|
||||||
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
|
|
||||||
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
|
|
||||||
|
|
||||||
// TODO: Optimize the case when this is the last use_value use.
|
return LoadImmediateIntoRegister(use_value, DefRegister(def_inst, desired_locations));
|
||||||
Gen::X64Reg use_reg = UseRegister(use_value);
|
}
|
||||||
Gen::X64Reg def_reg = DefRegister(def_value, desired_locations);
|
|
||||||
|
Gen::X64Reg RegAlloc::UseDefRegister(IR::Inst* use_inst, IR::Inst* def_inst, std::initializer_list<HostLoc> desired_locations) {
|
||||||
|
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||||
|
DEBUG_ASSERT_MSG(ValueLocations(def_inst).empty(), "def_inst has already been defined");
|
||||||
|
DEBUG_ASSERT_MSG(!ValueLocations(use_inst).empty(), "use_inst has not been defined");
|
||||||
|
|
||||||
|
// TODO: Optimize the case when this is the last use_inst use.
|
||||||
|
Gen::X64Reg use_reg = UseRegister(use_inst);
|
||||||
|
Gen::X64Reg def_reg = DefRegister(def_inst, desired_locations);
|
||||||
code->MOV(32, Gen::R(def_reg), Gen::R(use_reg));
|
code->MOV(32, Gen::R(def_reg), Gen::R(use_reg));
|
||||||
return def_reg;
|
return def_reg;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations) {
|
Gen::X64Reg RegAlloc::UseRegister(IR::Value use_value, std::initializer_list<HostLoc> desired_locations) {
|
||||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
if (!use_value.IsImmediate()) {
|
||||||
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
|
return UseRegister(use_value.GetInst(), desired_locations);
|
||||||
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
|
}
|
||||||
ASSERT_MSG(remaining_uses[use_value] != 0, "use_value ran out of uses. (Use-d an IR::Value* too many times)");
|
|
||||||
|
|
||||||
HostLoc current_location = ValueLocations(use_value).front();
|
return LoadImmediateIntoRegister(use_value, ScratchRegister(desired_locations));
|
||||||
|
}
|
||||||
|
|
||||||
|
Gen::X64Reg RegAlloc::UseRegister(IR::Inst* use_inst, std::initializer_list<HostLoc> desired_locations) {
|
||||||
|
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||||
|
DEBUG_ASSERT_MSG(!ValueLocations(use_inst).empty(), "use_inst has not been defined");
|
||||||
|
|
||||||
|
HostLoc current_location = ValueLocations(use_inst).front();
|
||||||
auto iter = std::find(desired_locations.begin(), desired_locations.end(), current_location);
|
auto iter = std::find(desired_locations.begin(), desired_locations.end(), current_location);
|
||||||
if (iter != desired_locations.end()) {
|
if (iter != desired_locations.end()) {
|
||||||
ASSERT(hostloc_state[current_location] == HostLocState::Idle || hostloc_state[current_location] == HostLocState::Use);
|
ASSERT(hostloc_state[static_cast<size_t>(current_location)] == HostLocState::Idle || hostloc_state[static_cast<size_t>(current_location)] == HostLocState::Use);
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
hostloc_state[current_location] = HostLocState::Use;
|
hostloc_state[static_cast<size_t>(current_location)] = HostLocState::Use;
|
||||||
remaining_uses[use_value]--;
|
DecrementRemainingUses(use_inst);
|
||||||
|
|
||||||
return hostloc_to_x64.at(current_location);
|
return HostLocToX64(current_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
HostLoc new_location = SelectARegister(desired_locations);
|
HostLoc new_location = SelectARegister(desired_locations);
|
||||||
|
@ -98,33 +95,40 @@ Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list<Ho
|
||||||
SpillRegister(new_location);
|
SpillRegister(new_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), SpillToOpArg(current_location));
|
code->MOV(32, Gen::R(HostLocToX64(new_location)), SpillToOpArg(current_location));
|
||||||
|
|
||||||
hostloc_state[new_location] = HostLocState::Use;
|
hostloc_state[static_cast<size_t>(new_location)] = HostLocState::Use;
|
||||||
std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]);
|
std::swap(hostloc_to_inst[static_cast<size_t>(new_location)], hostloc_to_inst[static_cast<size_t>(current_location)]);
|
||||||
remaining_uses[use_value]--;
|
DecrementRemainingUses(use_inst);
|
||||||
} else if (HostLocIsRegister(current_location)) {
|
} else if (HostLocIsRegister(current_location)) {
|
||||||
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
|
ASSERT(hostloc_state[static_cast<size_t>(current_location)] == HostLocState::Idle);
|
||||||
|
|
||||||
code->XCHG(32, Gen::R(hostloc_to_x64.at(new_location)), Gen::R(hostloc_to_x64.at(current_location)));
|
code->XCHG(32, Gen::R(HostLocToX64(new_location)), Gen::R(HostLocToX64(current_location)));
|
||||||
|
|
||||||
hostloc_state[new_location] = HostLocState::Use;
|
hostloc_state[static_cast<size_t>(new_location)] = HostLocState::Use;
|
||||||
std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]);
|
std::swap(hostloc_to_inst[static_cast<size_t>(new_location)], hostloc_to_inst[static_cast<size_t>(current_location)]);
|
||||||
remaining_uses[use_value]--;
|
DecrementRemainingUses(use_inst);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_MSG(0, "Invalid current_location");
|
ASSERT_MSG(0, "Invalid current_location");
|
||||||
}
|
}
|
||||||
|
|
||||||
return hostloc_to_x64.at(new_location);
|
return HostLocToX64(new_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gen::X64Reg RegAlloc::UseScratchRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations) {
|
Gen::X64Reg RegAlloc::UseScratchRegister(IR::Value use_value, std::initializer_list<HostLoc> desired_locations) {
|
||||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
if (!use_value.IsImmediate()) {
|
||||||
ASSERT_MSG(remaining_uses.find(use_value) != remaining_uses.end(), "use_value has not been defined");
|
return UseScratchRegister(use_value.GetInst(), desired_locations);
|
||||||
ASSERT_MSG(!ValueLocations(use_value).empty(), "use_value has not been defined");
|
}
|
||||||
ASSERT_MSG(remaining_uses[use_value] != 0, "use_value ran out of uses. (Use-d an IR::Value* too many times)");
|
|
||||||
|
|
||||||
HostLoc current_location = ValueLocations(use_value).front();
|
return LoadImmediateIntoRegister(use_value, ScratchRegister(desired_locations));
|
||||||
|
}
|
||||||
|
|
||||||
|
Gen::X64Reg RegAlloc::UseScratchRegister(IR::Inst* use_inst, std::initializer_list<HostLoc> desired_locations) {
|
||||||
|
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||||
|
DEBUG_ASSERT_MSG(!ValueLocations(use_inst).empty(), "use_inst has not been defined");
|
||||||
|
ASSERT_MSG(use_inst->use_count != 0, "use_inst ran out of uses. (Use-d an IR::Inst* too many times)");
|
||||||
|
|
||||||
|
HostLoc current_location = ValueLocations(use_inst).front();
|
||||||
HostLoc new_location = SelectARegister(desired_locations);
|
HostLoc new_location = SelectARegister(desired_locations);
|
||||||
|
|
||||||
if (HostLocIsSpill(current_location)) {
|
if (HostLocIsSpill(current_location)) {
|
||||||
|
@ -132,34 +136,34 @@ Gen::X64Reg RegAlloc::UseScratchRegister(IR::Value* use_value, std::initializer_
|
||||||
SpillRegister(new_location);
|
SpillRegister(new_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), SpillToOpArg(current_location));
|
code->MOV(32, Gen::R(HostLocToX64(new_location)), SpillToOpArg(current_location));
|
||||||
|
|
||||||
hostloc_state[new_location] = HostLocState::Scratch;
|
hostloc_state[static_cast<size_t>(new_location)] = HostLocState::Scratch;
|
||||||
remaining_uses[use_value]--;
|
DecrementRemainingUses(use_inst);
|
||||||
} else if (HostLocIsRegister(current_location)) {
|
} else if (HostLocIsRegister(current_location)) {
|
||||||
ASSERT(hostloc_state[current_location] == HostLocState::Idle);
|
ASSERT(hostloc_state[static_cast<size_t>(current_location)] == HostLocState::Idle);
|
||||||
|
|
||||||
if (IsRegisterOccupied(new_location)) {
|
if (IsRegisterOccupied(new_location)) {
|
||||||
SpillRegister(new_location);
|
SpillRegister(new_location);
|
||||||
if (current_location != new_location) {
|
if (current_location != new_location) {
|
||||||
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), Gen::R(hostloc_to_x64.at(current_location)));
|
code->MOV(32, Gen::R(HostLocToX64(new_location)), Gen::R(HostLocToX64(current_location)));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
code->MOV(32, Gen::R(hostloc_to_x64.at(new_location)), Gen::R(hostloc_to_x64.at(current_location)));
|
code->MOV(32, Gen::R(HostLocToX64(new_location)), Gen::R(HostLocToX64(current_location)));
|
||||||
}
|
}
|
||||||
|
|
||||||
hostloc_state[new_location] = HostLocState::Scratch;
|
hostloc_state[static_cast<size_t>(new_location)] = HostLocState::Scratch;
|
||||||
remaining_uses[use_value]--;
|
DecrementRemainingUses(use_inst);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_MSG(0, "Invalid current_location");
|
ASSERT_MSG(0, "Invalid current_location");
|
||||||
}
|
}
|
||||||
|
|
||||||
return hostloc_to_x64.at(new_location);
|
return HostLocToX64(new_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list<HostLoc> desired_locations) {
|
Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list<HostLoc> desired_locations) {
|
||||||
ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
DEBUG_ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister));
|
||||||
|
|
||||||
HostLoc location = SelectARegister(desired_locations);
|
HostLoc location = SelectARegister(desired_locations);
|
||||||
|
|
||||||
|
@ -168,12 +172,32 @@ Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list<HostLoc> desired_loc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
hostloc_state[location] = HostLocState::Scratch;
|
hostloc_state[static_cast<size_t>(location)] = HostLocState::Scratch;
|
||||||
|
|
||||||
return hostloc_to_x64.at(location);
|
return HostLocToX64(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::HostCall(IR::Value* result_def, IR::Value* arg0_use, IR::Value* arg1_use, IR::Value* arg2_use, IR::Value* arg3_use) {
|
Gen::X64Reg RegAlloc::LoadImmediateIntoRegister(IR::Value imm, Gen::X64Reg reg) {
|
||||||
|
ASSERT_MSG(imm.IsImmediate(), "imm is not an immediate");
|
||||||
|
|
||||||
|
switch (imm.GetType()) {
|
||||||
|
case IR::Type::U1:
|
||||||
|
code->MOV(32, R(reg), Gen::Imm32(imm.GetU1()));
|
||||||
|
break;
|
||||||
|
case IR::Type::U8:
|
||||||
|
code->MOV(32, R(reg), Gen::Imm32(imm.GetU8()));
|
||||||
|
break;
|
||||||
|
case IR::Type::U32:
|
||||||
|
code->MOV(32, R(reg), Gen::Imm32(imm.GetU32()));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ASSERT_MSG(false, "This should never happen.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegAlloc::HostCall(IR::Inst* result_def, IR::Value arg0_use, IR::Value arg1_use, IR::Value arg2_use, IR::Value arg3_use) {
|
||||||
constexpr HostLoc AbiReturn = HostLoc::RAX;
|
constexpr HostLoc AbiReturn = HostLoc::RAX;
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
constexpr std::array<HostLoc, 4> AbiArgs = { HostLoc::RCX, HostLoc::RDX, HostLoc::R8, HostLoc::R9 };
|
constexpr std::array<HostLoc, 4> AbiArgs = { HostLoc::RCX, HostLoc::RDX, HostLoc::R8, HostLoc::R9 };
|
||||||
|
@ -185,7 +209,7 @@ void RegAlloc::HostCall(IR::Value* result_def, IR::Value* arg0_use, IR::Value* a
|
||||||
constexpr std::array<HostLoc, 4> OtherCallerSave = { HostLoc::R8, HostLoc::R9, HostLoc::R10, HostLoc::R11 };
|
constexpr std::array<HostLoc, 4> OtherCallerSave = { HostLoc::R8, HostLoc::R9, HostLoc::R10, HostLoc::R11 };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const std::array<IR::Value*, 4> args = {arg0_use, arg1_use, arg2_use, arg3_use};
|
const std::array<IR::Value*, 4> args = {&arg0_use, &arg1_use, &arg2_use, &arg3_use};
|
||||||
|
|
||||||
// TODO: This works but almost certainly leads to suboptimal generated code.
|
// TODO: This works but almost certainly leads to suboptimal generated code.
|
||||||
|
|
||||||
|
@ -200,8 +224,8 @@ void RegAlloc::HostCall(IR::Value* result_def, IR::Value* arg0_use, IR::Value* a
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < AbiArgs.size(); i++) {
|
for (size_t i = 0; i < AbiArgs.size(); i++) {
|
||||||
if (args[i]) {
|
if (!args[i]->IsEmpty()) {
|
||||||
UseScratchRegister(args[i], {AbiArgs[i]});
|
UseScratchRegister(*args[i], {AbiArgs[i]});
|
||||||
} else {
|
} else {
|
||||||
ScratchRegister({AbiArgs[i]});
|
ScratchRegister({AbiArgs[i]});
|
||||||
}
|
}
|
||||||
|
@ -231,36 +255,36 @@ HostLoc RegAlloc::SelectARegister(std::initializer_list<HostLoc> desired_locatio
|
||||||
return candidates.front();
|
return candidates.front();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HostLoc> RegAlloc::ValueLocations(IR::Value* value) const {
|
std::vector<HostLoc> RegAlloc::ValueLocations(IR::Inst* value) const {
|
||||||
std::vector<HostLoc> locations;
|
std::vector<HostLoc> locations;
|
||||||
|
|
||||||
for (const auto& iter : hostloc_to_value)
|
for (size_t i = 0; i < HostLocCount; i++)
|
||||||
if (iter.second == value)
|
if (hostloc_to_inst[i] == value)
|
||||||
locations.emplace_back(iter.first);
|
locations.emplace_back(static_cast<HostLoc>(i));
|
||||||
|
|
||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegAlloc::IsRegisterOccupied(HostLoc loc) const {
|
bool RegAlloc::IsRegisterOccupied(HostLoc loc) const {
|
||||||
return hostloc_to_value.find(loc) != hostloc_to_value.end() && hostloc_to_value.at(loc) != nullptr;
|
return hostloc_to_inst.at(static_cast<size_t>(loc)) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RegAlloc::IsRegisterAllocated(HostLoc loc) const {
|
bool RegAlloc::IsRegisterAllocated(HostLoc loc) const {
|
||||||
return hostloc_state.find(loc) != hostloc_state.end() && hostloc_state.at(loc) != HostLocState::Idle;
|
return hostloc_state.at(static_cast<size_t>(loc)) != HostLocState::Idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::SpillRegister(HostLoc loc) {
|
void RegAlloc::SpillRegister(HostLoc loc) {
|
||||||
ASSERT_MSG(HostLocIsRegister(loc), "Only registers can be spilled");
|
ASSERT_MSG(HostLocIsRegister(loc), "Only registers can be spilled");
|
||||||
ASSERT_MSG(hostloc_state[loc] == HostLocState::Idle, "Allocated registers cannot be spilled");
|
ASSERT_MSG(hostloc_state[static_cast<size_t>(loc)] == HostLocState::Idle, "Allocated registers cannot be spilled");
|
||||||
ASSERT_MSG(IsRegisterOccupied(loc), "There is no need to spill unoccupied registers");
|
ASSERT_MSG(IsRegisterOccupied(loc), "There is no need to spill unoccupied registers");
|
||||||
ASSERT_MSG(!IsRegisterAllocated(loc), "Registers that have been allocated must not be spilt");
|
ASSERT_MSG(!IsRegisterAllocated(loc), "Registers that have been allocated must not be spilt");
|
||||||
|
|
||||||
HostLoc new_loc = FindFreeSpill();
|
HostLoc new_loc = FindFreeSpill();
|
||||||
|
|
||||||
code->MOV(32, SpillToOpArg(new_loc), Gen::R(hostloc_to_x64.at(loc)));
|
code->MOV(32, SpillToOpArg(new_loc), Gen::R(HostLocToX64(loc)));
|
||||||
|
|
||||||
hostloc_to_value[new_loc] = hostloc_to_value[loc];
|
hostloc_to_inst[static_cast<size_t>(new_loc)] = hostloc_to_inst[static_cast<size_t>(loc)];
|
||||||
hostloc_to_value[loc] = nullptr;
|
hostloc_to_inst[static_cast<size_t>(loc)] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
HostLoc RegAlloc::FindFreeSpill() const {
|
HostLoc RegAlloc::FindFreeSpill() const {
|
||||||
|
@ -272,27 +296,25 @@ HostLoc RegAlloc::FindFreeSpill() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::EndOfAllocScope() {
|
void RegAlloc::EndOfAllocScope() {
|
||||||
hostloc_state.clear();
|
hostloc_state.fill(HostLocState::Idle);
|
||||||
|
|
||||||
for (auto& iter : hostloc_to_value)
|
for (auto& iter : hostloc_to_inst)
|
||||||
if (iter.second && remaining_uses[iter.second] == 0)
|
if (iter && iter->use_count == 0)
|
||||||
iter.second = nullptr;
|
iter = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::DecrementRemainingUses(IR::Value* value) {
|
void RegAlloc::DecrementRemainingUses(IR::Inst* value) {
|
||||||
ASSERT_MSG(remaining_uses.find(value) != remaining_uses.end(), "value does not exist");
|
ASSERT_MSG(value->use_count > 0, "value doesn't have any remaining uses");
|
||||||
ASSERT_MSG(remaining_uses[value] > 0, "value doesn't have any remaining uses");
|
value->use_count--;
|
||||||
remaining_uses[value]--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::AssertNoMoreUses() {
|
void RegAlloc::AssertNoMoreUses() {
|
||||||
ASSERT(std::all_of(hostloc_to_value.begin(), hostloc_to_value.end(), [](const auto& pair){ return !pair.second; }));
|
ASSERT(std::all_of(hostloc_to_inst.begin(), hostloc_to_inst.end(), [](const auto& inst){ return !inst; }));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegAlloc::Reset() {
|
void RegAlloc::Reset() {
|
||||||
hostloc_to_value.clear();
|
hostloc_to_inst.fill(nullptr);
|
||||||
hostloc_state.clear();
|
hostloc_state.fill(HostLocState::Idle);
|
||||||
remaining_uses.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
|
|
|
@ -17,11 +17,14 @@ namespace Dynarmic {
|
||||||
namespace BackendX64 {
|
namespace BackendX64 {
|
||||||
|
|
||||||
enum class HostLoc {
|
enum class HostLoc {
|
||||||
RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8, R9, R10, R11, R12, R13, R14,
|
// Ordering of the registers is intentional. See also: HostLocToX64.
|
||||||
|
RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14,
|
||||||
CF, PF, AF, ZF, SF, OF,
|
CF, PF, AF, ZF, SF, OF,
|
||||||
FirstSpill,
|
FirstSpill,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr size_t HostLocCount = static_cast<size_t>(HostLoc::FirstSpill) + SpillCount;
|
||||||
|
|
||||||
enum class HostLocState {
|
enum class HostLocState {
|
||||||
Idle, Def, Use, Scratch
|
Idle, Def, Use, Scratch
|
||||||
};
|
};
|
||||||
|
@ -66,22 +69,26 @@ public:
|
||||||
RegAlloc(Gen::XEmitter* code) : code(code) {}
|
RegAlloc(Gen::XEmitter* code) : code(code) {}
|
||||||
|
|
||||||
/// Late-def
|
/// Late-def
|
||||||
Gen::X64Reg DefRegister(IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
Gen::X64Reg DefRegister(IR::Inst* def_inst, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
/// Early-use, Late-def
|
/// Early-use, Late-def
|
||||||
Gen::X64Reg UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
Gen::X64Reg UseDefRegister(IR::Value use_value, IR::Inst* def_inst, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
|
Gen::X64Reg UseDefRegister(IR::Inst* use_inst, IR::Inst* def_inst, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
/// Early-use
|
/// Early-use
|
||||||
Gen::X64Reg UseRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
Gen::X64Reg UseRegister(IR::Value use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
|
Gen::X64Reg UseRegister(IR::Inst* use_inst, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
/// Early-use, Destroyed
|
/// Early-use, Destroyed
|
||||||
Gen::X64Reg UseScratchRegister(IR::Value* use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
Gen::X64Reg UseScratchRegister(IR::Value use_value, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
|
Gen::X64Reg UseScratchRegister(IR::Inst* use_inst, std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
/// Early-def, Late-use, single-use
|
/// Early-def, Late-use, single-use
|
||||||
Gen::X64Reg ScratchRegister(std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
Gen::X64Reg ScratchRegister(std::initializer_list<HostLoc> desired_locations = hostloc_any_register);
|
||||||
|
Gen::X64Reg LoadImmediateIntoRegister(IR::Value imm, Gen::X64Reg reg);
|
||||||
|
|
||||||
/// Late-def for result register, Early-use for all arguments, Each value is placed into registers according to host ABI.
|
/// Late-def for result register, Early-use for all arguments, Each value is placed into registers according to host ABI.
|
||||||
void HostCall(IR::Value* result_def = nullptr, IR::Value* arg0_use = nullptr, IR::Value* arg1_use = nullptr, IR::Value* arg2_use = nullptr, IR::Value* arg3_use = nullptr);
|
void HostCall(IR::Inst* result_def = nullptr, IR::Value arg0_use = {}, IR::Value arg1_use = {}, IR::Value arg2_use = {}, IR::Value arg3_use = {});
|
||||||
|
|
||||||
// TODO: Values in host flags
|
// TODO: Values in host flags
|
||||||
|
|
||||||
void DecrementRemainingUses(IR::Value* value);
|
void DecrementRemainingUses(IR::Inst* value);
|
||||||
|
|
||||||
void EndOfAllocScope();
|
void EndOfAllocScope();
|
||||||
|
|
||||||
|
@ -91,7 +98,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HostLoc SelectARegister(std::initializer_list<HostLoc> desired_locations) const;
|
HostLoc SelectARegister(std::initializer_list<HostLoc> desired_locations) const;
|
||||||
std::vector<HostLoc> ValueLocations(IR::Value* value) const;
|
std::vector<HostLoc> ValueLocations(IR::Inst* value) const;
|
||||||
bool IsRegisterOccupied(HostLoc loc) const;
|
bool IsRegisterOccupied(HostLoc loc) const;
|
||||||
bool IsRegisterAllocated(HostLoc loc) const;
|
bool IsRegisterAllocated(HostLoc loc) const;
|
||||||
|
|
||||||
|
@ -100,10 +107,9 @@ private:
|
||||||
|
|
||||||
Gen::XEmitter* code = nullptr;
|
Gen::XEmitter* code = nullptr;
|
||||||
|
|
||||||
using mapping_map_t = std::map<HostLoc, IR::Value*>;
|
using mapping_map_t = std::array<IR::Inst*, HostLocCount>;
|
||||||
mapping_map_t hostloc_to_value;
|
mapping_map_t hostloc_to_inst;
|
||||||
std::map<HostLoc, HostLocState> hostloc_state;
|
std::array<HostLocState, HostLocCount> hostloc_state;
|
||||||
std::map<IR::Value*, size_t> remaining_uses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace BackendX64
|
} // namespace BackendX64
|
||||||
|
|
|
@ -45,7 +45,7 @@ static void assert_noinline_call(const Fn& fn) {
|
||||||
#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
|
#define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__)
|
||||||
#else // not debug
|
#else // not debug
|
||||||
#define DEBUG_ASSERT(_a_)
|
#define DEBUG_ASSERT(_a_)
|
||||||
#define DEBUG_ASSERT_MSG(_a_, _desc_, ...)
|
#define DEBUG_ASSERT_MSG(_a_, ...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
|
#define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!")
|
||||||
|
|
|
@ -52,94 +52,69 @@ const char* GetNameOf(Opcode op) {
|
||||||
|
|
||||||
// Value class member definitions
|
// Value class member definitions
|
||||||
|
|
||||||
void Value::ReplaceUsesWith(ValuePtr replacement) {
|
Type Value::GetType() const {
|
||||||
while (!uses.empty()) {
|
return IsImmediate() ? type : inner.inst->GetType();
|
||||||
auto use = uses.front();
|
|
||||||
use.use_owner.lock()->ReplaceUseOfXWithY(use.value.lock(), replacement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ValuePtr> Value::GetUses() const {
|
|
||||||
std::vector<ValuePtr> result(uses.size());
|
|
||||||
std::transform(uses.begin(), uses.end(), result.begin(), [](const auto& use){ return use.use_owner.lock(); });
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Value::AddUse(ValuePtr owner) {
|
|
||||||
// There can be multiple uses from the same owner.
|
|
||||||
uses.push_back({ shared_from_this(), owner });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Value::RemoveUse(ValuePtr owner) {
|
|
||||||
// Remove only one use.
|
|
||||||
auto iter = std::find_if(uses.begin(), uses.end(), [&owner](auto use) { return use.use_owner.lock() == owner; });
|
|
||||||
ASSERT_MSG(iter != uses.end(), "RemoveUse without associated AddUse. Bug in use management code.");
|
|
||||||
uses.erase(iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Value::ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) {
|
|
||||||
// This should never be called. Use management is incorrect if this is ever called.
|
|
||||||
ASSERT_MSG(false, "This Value type doesn't use any values. Bug in use management code.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Value::AssertValid() {
|
|
||||||
ASSERT(std::all_of(uses.begin(), uses.end(), [](const auto& use) { return !use.use_owner.expired(); }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inst class member definitions
|
// Inst class member definitions
|
||||||
|
|
||||||
Inst::Inst(Opcode op_) : Value(op_) {
|
Value Inst::GetArg(size_t index) const {
|
||||||
args.resize(GetNumArgsOf(op));
|
DEBUG_ASSERT(index < GetNumArgsOf(op));
|
||||||
|
DEBUG_ASSERT(!args[index].IsEmpty());
|
||||||
|
|
||||||
|
return args[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inst::SetArg(size_t index, ValuePtr value) {
|
void Inst::SetArg(size_t index, Value value) {
|
||||||
auto this_ = shared_from_this();
|
DEBUG_ASSERT(index < GetNumArgsOf(op));
|
||||||
|
DEBUG_ASSERT(value.GetType() == GetArgTypeOf(op, index));
|
||||||
|
|
||||||
if (auto prev_value = args.at(index).lock()) {
|
if (!args[index].IsImmediate()) {
|
||||||
prev_value->RemoveUse(this_);
|
UndoUse(args[index]);
|
||||||
|
}
|
||||||
|
if (!value.IsImmediate()) {
|
||||||
|
Use(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(value->GetType() == GetArgTypeOf(op, index));
|
args[index] = value;
|
||||||
args.at(index) = value;
|
|
||||||
|
|
||||||
value->AddUse(this_);
|
|
||||||
}
|
|
||||||
|
|
||||||
ValuePtr Inst::GetArg(size_t index) const {
|
|
||||||
ASSERT_MSG(!args.at(index).expired(), "This should never happen. All Values should be owned by a MicroBlock.");
|
|
||||||
return args.at(index).lock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inst::Invalidate() {
|
void Inst::Invalidate() {
|
||||||
AssertValid();
|
for (auto& value : args) {
|
||||||
ASSERT(!HasUses());
|
if (!value.IsImmediate()) {
|
||||||
|
UndoUse(value);
|
||||||
auto this_ = shared_from_this();
|
|
||||||
for (auto& arg : args) {
|
|
||||||
arg.lock()->RemoveUse(this_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Inst::AssertValid() {
|
|
||||||
ASSERT(std::all_of(args.begin(), args.end(), [](const auto& arg) { return !arg.expired(); }));
|
|
||||||
Value::AssertValid();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Inst::ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) {
|
|
||||||
bool has_use = false;
|
|
||||||
auto this_ = shared_from_this();
|
|
||||||
|
|
||||||
// Note that there may be multiple uses of x.
|
|
||||||
for (auto& arg : args) {
|
|
||||||
if (arg.lock() == x) {
|
|
||||||
arg = y;
|
|
||||||
has_use = true;
|
|
||||||
x->RemoveUse(this_);
|
|
||||||
y->AddUse(this_);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT_MSG(has_use, "This Inst doesn't have x. Bug in use management code.");
|
void Inst::Use(Value& value) {
|
||||||
|
value.GetInst()->use_count++;
|
||||||
|
|
||||||
|
switch (op){
|
||||||
|
case Opcode::GetCarryFromOp:
|
||||||
|
value.GetInst()->carry_inst = this;
|
||||||
|
break;
|
||||||
|
case Opcode::GetOverflowFromOp:
|
||||||
|
value.GetInst()->overflow_inst = this;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Inst::UndoUse(Value& value) {
|
||||||
|
value.GetInst()->use_count--;
|
||||||
|
|
||||||
|
switch (op){
|
||||||
|
case Opcode::GetCarryFromOp:
|
||||||
|
value.GetInst()->carry_inst = nullptr;
|
||||||
|
break;
|
||||||
|
case Opcode::GetOverflowFromOp:
|
||||||
|
value.GetInst()->overflow_inst = nullptr;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DumpBlock(const IR::Block& block) {
|
std::string DumpBlock(const IR::Block& block) {
|
||||||
|
@ -160,65 +135,48 @@ std::string DumpBlock(const IR::Block& block) {
|
||||||
}
|
}
|
||||||
ret += "\n";
|
ret += "\n";
|
||||||
|
|
||||||
std::map<IR::Value*, size_t> value_to_index;
|
std::map<const IR::Inst*, size_t> inst_to_index;
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
|
|
||||||
const auto arg_to_string = [&value_to_index](IR::ValuePtr arg) -> std::string {
|
const auto arg_to_string = [&inst_to_index](const IR::Value& arg) -> std::string {
|
||||||
if (!arg) {
|
if (arg.IsEmpty()) {
|
||||||
return "<null>";
|
return "<null>";
|
||||||
|
} else if (!arg.IsImmediate()) {
|
||||||
|
return Common::StringFromFormat("%%%zu", inst_to_index.at(arg.GetInst()));
|
||||||
}
|
}
|
||||||
switch (arg->GetOpcode()) {
|
switch (arg.GetType()) {
|
||||||
case Opcode::ImmU1: {
|
case Type::U1:
|
||||||
auto inst = reinterpret_cast<ImmU1*>(arg.get());
|
return Common::StringFromFormat("#%s", arg.GetU1() ? "1" : "0");
|
||||||
return Common::StringFromFormat("#%s", inst->value ? "1" : "0");
|
case Type::U8:
|
||||||
}
|
return Common::StringFromFormat("#%u", arg.GetU8());
|
||||||
case Opcode::ImmU8: {
|
case Type::U32:
|
||||||
auto inst = reinterpret_cast<ImmU8*>(arg.get());
|
return Common::StringFromFormat("#%#x", arg.GetU32());
|
||||||
return Common::StringFromFormat("#%u", inst->value);
|
case Type::RegRef:
|
||||||
}
|
return Arm::RegToString(arg.GetRegRef());
|
||||||
case Opcode::ImmU32: {
|
default:
|
||||||
auto inst = reinterpret_cast<ImmU32*>(arg.get());
|
return "<unknown immediate type>";
|
||||||
return Common::StringFromFormat("#%#x", inst->value);
|
|
||||||
}
|
|
||||||
case Opcode::ImmRegRef: {
|
|
||||||
auto inst = reinterpret_cast<ImmRegRef*>(arg.get());
|
|
||||||
return Arm::RegToString(inst->value);
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return Common::StringFromFormat("%%%zu", value_to_index.at(arg.get()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto& inst_ptr : block.instructions) {
|
for (auto inst = block.instructions.begin(); inst != block.instructions.end(); ++inst) {
|
||||||
const Opcode op = inst_ptr->GetOpcode();
|
const Opcode op = inst->GetOpcode();
|
||||||
switch (op) {
|
|
||||||
case Opcode::ImmU1:
|
|
||||||
case Opcode::ImmU8:
|
|
||||||
case Opcode::ImmU32:
|
|
||||||
case Opcode::ImmRegRef:
|
|
||||||
break;
|
|
||||||
default: {
|
|
||||||
if (GetTypeOf(op) != Type::Void) {
|
|
||||||
ret += Common::StringFromFormat("%%%-5zu = ", index);
|
|
||||||
} else {
|
|
||||||
ret += " "; // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
|
|
||||||
}
|
|
||||||
|
|
||||||
ret += GetNameOf(op);
|
if (GetTypeOf(op) != Type::Void) {
|
||||||
|
ret += Common::StringFromFormat("%%%-5zu = ", index);
|
||||||
const size_t arg_count = GetNumArgsOf(op);
|
} else {
|
||||||
const auto inst = reinterpret_cast<Inst*>(inst_ptr.get());
|
ret += " "; // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
|
||||||
for (size_t arg_index = 0; arg_index < arg_count; arg_index++) {
|
|
||||||
ret += arg_index != 0 ? ", " : " ";
|
|
||||||
ret += arg_to_string(inst->GetArg(arg_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
ret += "\n";
|
|
||||||
value_to_index[inst_ptr.get()] = index++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret += GetNameOf(op);
|
||||||
|
|
||||||
|
const size_t arg_count = GetNumArgsOf(op);
|
||||||
|
for (size_t arg_index = 0; arg_index < arg_count; arg_index++) {
|
||||||
|
ret += arg_index != 0 ? ", " : " ";
|
||||||
|
ret += arg_to_string(inst->GetArg(arg_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
ret += "\n";
|
||||||
|
inst_to_index.at(&*inst) = index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -10,9 +10,12 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/variant.hpp>
|
#include <boost/pool/pool.hpp>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "frontend/arm_types.h"
|
#include "frontend/arm_types.h"
|
||||||
#include "frontend/ir/opcodes.h"
|
#include "frontend/ir/opcodes.h"
|
||||||
|
@ -47,22 +50,91 @@ const char* GetNameOf(Opcode op);
|
||||||
|
|
||||||
// Type declarations
|
// Type declarations
|
||||||
|
|
||||||
/// Base class for microinstructions to derive from.
|
/**
|
||||||
|
* A representation of a microinstruction. A single ARM/Thumb instruction may be
|
||||||
|
* converted into zero or more microinstructions.
|
||||||
|
*/
|
||||||
|
|
||||||
class Value;
|
struct Value;
|
||||||
using ValuePtr = std::shared_ptr<Value>;
|
class Inst;
|
||||||
using ValueWeakPtr = std::weak_ptr<Value>;
|
|
||||||
|
|
||||||
class Value : public std::enable_shared_from_this<Value> {
|
struct Value final {
|
||||||
public:
|
public:
|
||||||
virtual ~Value() = default;
|
Value() : type(Type::Void) {}
|
||||||
|
|
||||||
bool HasUses() const { return !uses.empty(); }
|
explicit Value(Inst* value) : type(Type::Opaque) {
|
||||||
bool HasOneUse() const { return uses.size() == 1; }
|
inner.inst = value;
|
||||||
bool HasManyUses() const { return uses.size() > 1; }
|
}
|
||||||
|
|
||||||
/// Replace all uses of this Value with `replacement`.
|
explicit Value(Arm::Reg value) : type(Type::RegRef) {
|
||||||
void ReplaceUsesWith(ValuePtr replacement);
|
inner.imm_regref = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Value(bool value) : type(Type::U1) {
|
||||||
|
inner.imm_u1 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Value(u8 value) : type(Type::U8) {
|
||||||
|
inner.imm_u8 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Value(u32 value) : type(Type::U32) {
|
||||||
|
inner.imm_u32 = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsEmpty() const {
|
||||||
|
return type == Type::Void;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsImmediate() const {
|
||||||
|
return type != Type::Opaque;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type GetType() const;
|
||||||
|
|
||||||
|
Inst* GetInst() const {
|
||||||
|
DEBUG_ASSERT(type == Type::Opaque);
|
||||||
|
return inner.inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
Arm::Reg GetRegRef() const {
|
||||||
|
DEBUG_ASSERT(type == Type::RegRef);
|
||||||
|
return inner.imm_regref;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetU1() const {
|
||||||
|
DEBUG_ASSERT(type == Type::U1);
|
||||||
|
return inner.imm_u1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 GetU8() const {
|
||||||
|
DEBUG_ASSERT(type == Type::U8);
|
||||||
|
return inner.imm_u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetU32() const {
|
||||||
|
DEBUG_ASSERT(type == Type::U32);
|
||||||
|
return inner.imm_u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
union {
|
||||||
|
Inst* inst; // type == Type::Opaque
|
||||||
|
Arm::Reg imm_regref;
|
||||||
|
bool imm_u1;
|
||||||
|
u8 imm_u8;
|
||||||
|
u32 imm_u32;
|
||||||
|
} inner;
|
||||||
|
};
|
||||||
|
|
||||||
|
using InstListLinkMode = boost::intrusive::link_mode<boost::intrusive::normal_link>;
|
||||||
|
class Inst final : public boost::intrusive::list_base_hook<InstListLinkMode> {
|
||||||
|
public:
|
||||||
|
Inst(Opcode op) : op(op) {}
|
||||||
|
|
||||||
|
bool HasUses() const { return use_count > 0; }
|
||||||
|
|
||||||
/// Get the microop this microinstruction represents.
|
/// Get the microop this microinstruction represents.
|
||||||
Opcode GetOpcode() const { return op; }
|
Opcode GetOpcode() const { return op; }
|
||||||
|
@ -70,99 +142,22 @@ public:
|
||||||
Type GetType() const { return GetTypeOf(op); }
|
Type GetType() const { return GetTypeOf(op); }
|
||||||
/// Get the number of arguments this instruction has.
|
/// Get the number of arguments this instruction has.
|
||||||
size_t NumArgs() const { return GetNumArgsOf(op); }
|
size_t NumArgs() const { return GetNumArgsOf(op); }
|
||||||
/// Get the number of uses this instruction has.
|
|
||||||
size_t NumUses() const { return uses.size(); }
|
|
||||||
|
|
||||||
std::vector<ValuePtr> GetUses() const;
|
Value GetArg(size_t index) const;
|
||||||
|
void SetArg(size_t index, Value value);
|
||||||
|
|
||||||
/// Prepare this Value for removal from the instruction stream.
|
void Invalidate();
|
||||||
virtual void Invalidate() {}
|
|
||||||
/// Assert that this Value is valid.
|
|
||||||
virtual void AssertValid();
|
|
||||||
|
|
||||||
intptr_t GetTag() const { return tag; }
|
size_t use_count = 0;
|
||||||
void SetTag(intptr_t tag_) { tag = tag_; }
|
Inst* carry_inst = nullptr;
|
||||||
|
Inst* overflow_inst = nullptr;
|
||||||
protected:
|
|
||||||
friend class Inst;
|
|
||||||
|
|
||||||
explicit Value(Opcode op_) : op(op_) {}
|
|
||||||
|
|
||||||
void AddUse(ValuePtr owner);
|
|
||||||
void RemoveUse(ValuePtr owner);
|
|
||||||
virtual void ReplaceUseOfXWithY(ValuePtr x, ValuePtr y);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Use(Value& value);
|
||||||
|
void UndoUse(Value& value);
|
||||||
|
|
||||||
Opcode op;
|
Opcode op;
|
||||||
|
std::array<Value, 3> args;
|
||||||
struct Use {
|
|
||||||
/// The instruction which is being used.
|
|
||||||
ValueWeakPtr value;
|
|
||||||
/// The instruction which is using `value`.
|
|
||||||
ValueWeakPtr use_owner;
|
|
||||||
};
|
|
||||||
std::list<Use> uses;
|
|
||||||
|
|
||||||
intptr_t tag = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Representation of a u1 immediate.
|
|
||||||
class ImmU1 final : public Value {
|
|
||||||
public:
|
|
||||||
explicit ImmU1(bool value_) : Value(Opcode::ImmU1), value(value_) {}
|
|
||||||
~ImmU1() override = default;
|
|
||||||
|
|
||||||
const bool value; ///< Literal value to load
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Representation of a u8 immediate.
|
|
||||||
class ImmU8 final : public Value {
|
|
||||||
public:
|
|
||||||
explicit ImmU8(u8 value_) : Value(Opcode::ImmU8), value(value_) {}
|
|
||||||
~ImmU8() override = default;
|
|
||||||
|
|
||||||
const u8 value; ///< Literal value to load
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Representation of a u32 immediate.
|
|
||||||
class ImmU32 final : public Value {
|
|
||||||
public:
|
|
||||||
explicit ImmU32(u32 value_) : Value(Opcode::ImmU32), value(value_) {}
|
|
||||||
~ImmU32() override = default;
|
|
||||||
|
|
||||||
const u32 value; ///< Literal value to load
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Representation of a GPR reference.
|
|
||||||
class ImmRegRef final : public Value {
|
|
||||||
public:
|
|
||||||
explicit ImmRegRef(Arm::Reg value_) : Value(Opcode::ImmRegRef), value(value_) {}
|
|
||||||
~ImmRegRef() override = default;
|
|
||||||
|
|
||||||
const Arm::Reg value; ///< Literal value to load
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A representation of a microinstruction. A single ARM/Thumb instruction may be
|
|
||||||
* converted into zero or more microinstructions.
|
|
||||||
*/
|
|
||||||
class Inst final : public Value {
|
|
||||||
public:
|
|
||||||
explicit Inst(Opcode op);
|
|
||||||
~Inst() override = default;
|
|
||||||
|
|
||||||
/// Set argument number `index` to `value`.
|
|
||||||
void SetArg(size_t index, ValuePtr value);
|
|
||||||
/// Get argument number `index`.
|
|
||||||
ValuePtr GetArg(size_t index) const;
|
|
||||||
|
|
||||||
void Invalidate() override;
|
|
||||||
void AssertValid() override;
|
|
||||||
protected:
|
|
||||||
void ReplaceUseOfXWithY(ValuePtr x, ValuePtr y) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<ValueWeakPtr> args;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace Term {
|
namespace Term {
|
||||||
|
@ -261,7 +256,9 @@ public:
|
||||||
boost::optional<Arm::LocationDescriptor> cond_failed = {};
|
boost::optional<Arm::LocationDescriptor> cond_failed = {};
|
||||||
|
|
||||||
/// List of instructions in this block.
|
/// List of instructions in this block.
|
||||||
std::list<ValuePtr> instructions;
|
boost::intrusive::list<Inst, InstListLinkMode> instructions;
|
||||||
|
/// Memory pool for instruction list
|
||||||
|
std::unique_ptr<boost::pool<>> instruction_alloc_pool = std::make_unique<boost::pool<>>(sizeof(Inst));
|
||||||
/// Terminal instruction of this block.
|
/// Terminal instruction of this block.
|
||||||
Terminal terminal = Term::Invalid{};
|
Terminal terminal = Term::Invalid{};
|
||||||
|
|
||||||
|
|
|
@ -24,138 +24,132 @@ u32 IREmitter::AlignPC(size_t alignment) {
|
||||||
return static_cast<u32>(pc - pc % alignment);
|
return static_cast<u32>(pc - pc % alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Imm1(bool value) {
|
IR::Value IREmitter::Imm1(bool imm1) {
|
||||||
auto imm1 = std::make_shared<IR::ImmU1>(value);
|
return IR::Value(imm1);
|
||||||
AddToBlock(imm1);
|
|
||||||
return imm1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Imm8(u8 i) {
|
IR::Value IREmitter::Imm8(u8 imm8) {
|
||||||
auto imm8 = std::make_shared<IR::ImmU8>(i);
|
return IR::Value(imm8);
|
||||||
AddToBlock(imm8);
|
|
||||||
return imm8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Imm32(u32 i) {
|
IR::Value IREmitter::Imm32(u32 imm32) {
|
||||||
auto imm32 = std::make_shared<IR::ImmU32>(i);
|
return IR::Value(imm32);
|
||||||
AddToBlock(imm32);
|
|
||||||
return imm32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::GetRegister(Reg reg) {
|
IR::Value IREmitter::GetRegister(Reg reg) {
|
||||||
if (reg == Reg::PC) {
|
if (reg == Reg::PC) {
|
||||||
return Imm32(PC());
|
return Imm32(PC());
|
||||||
}
|
}
|
||||||
return Inst(IR::Opcode::GetRegister, { RegRef(reg) });
|
return Inst(IR::Opcode::GetRegister, { IR::Value(reg) });
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::SetRegister(const Reg reg, IR::ValuePtr value) {
|
void IREmitter::SetRegister(const Reg reg, const IR::Value& value) {
|
||||||
ASSERT(reg != Reg::PC);
|
ASSERT(reg != Reg::PC);
|
||||||
Inst(IR::Opcode::SetRegister, { RegRef(reg), value });
|
Inst(IR::Opcode::SetRegister, { IR::Value(reg), value });
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::ALUWritePC(IR::ValuePtr value) {
|
void IREmitter::ALUWritePC(const IR::Value& value) {
|
||||||
// This behaviour is ARM version-dependent.
|
// This behaviour is ARM version-dependent.
|
||||||
// The below implementation is for ARMv6k
|
// The below implementation is for ARMv6k
|
||||||
BranchWritePC(value);
|
BranchWritePC(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::BranchWritePC(IR::ValuePtr value) {
|
void IREmitter::BranchWritePC(const IR::Value& value) {
|
||||||
if (!current_location.TFlag) {
|
if (!current_location.TFlag) {
|
||||||
auto new_pc = And(value, Imm32(0xFFFFFFFC));
|
auto new_pc = And(value, Imm32(0xFFFFFFFC));
|
||||||
Inst(IR::Opcode::SetRegister, { RegRef(Reg::PC), new_pc });
|
Inst(IR::Opcode::SetRegister, { IR::Value(Reg::PC), new_pc });
|
||||||
} else {
|
} else {
|
||||||
auto new_pc = And(value, Imm32(0xFFFFFFFE));
|
auto new_pc = And(value, Imm32(0xFFFFFFFE));
|
||||||
Inst(IR::Opcode::SetRegister, { RegRef(Reg::PC), new_pc });
|
Inst(IR::Opcode::SetRegister, { IR::Value(Reg::PC), new_pc });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::BXWritePC(IR::ValuePtr value) {
|
void IREmitter::BXWritePC(const IR::Value& value) {
|
||||||
Inst(IR::Opcode::BXWritePC, {value});
|
Inst(IR::Opcode::BXWritePC, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::LoadWritePC(IR::ValuePtr value) {
|
void IREmitter::LoadWritePC(const IR::Value& value) {
|
||||||
// This behaviour is ARM version-dependent.
|
// This behaviour is ARM version-dependent.
|
||||||
// The below implementation is for ARMv6k
|
// The below implementation is for ARMv6k
|
||||||
BXWritePC(value);
|
BXWritePC(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::CallSupervisor(IR::ValuePtr value) {
|
void IREmitter::CallSupervisor(const IR::Value& value) {
|
||||||
Inst(IR::Opcode::CallSupervisor, {value});
|
Inst(IR::Opcode::CallSupervisor, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::GetCFlag() {
|
IR::Value IREmitter::GetCFlag() {
|
||||||
return Inst(IR::Opcode::GetCFlag, {});
|
return Inst(IR::Opcode::GetCFlag, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::SetNFlag(IR::ValuePtr value) {
|
void IREmitter::SetNFlag(const IR::Value& value) {
|
||||||
Inst(IR::Opcode::SetNFlag, {value});
|
Inst(IR::Opcode::SetNFlag, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::SetZFlag(IR::ValuePtr value) {
|
void IREmitter::SetZFlag(const IR::Value& value) {
|
||||||
Inst(IR::Opcode::SetZFlag, {value});
|
Inst(IR::Opcode::SetZFlag, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::SetCFlag(IR::ValuePtr value) {
|
void IREmitter::SetCFlag(const IR::Value& value) {
|
||||||
Inst(IR::Opcode::SetCFlag, {value});
|
Inst(IR::Opcode::SetCFlag, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::SetVFlag(IR::ValuePtr value) {
|
void IREmitter::SetVFlag(const IR::Value& value) {
|
||||||
Inst(IR::Opcode::SetVFlag, {value});
|
Inst(IR::Opcode::SetVFlag, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::LeastSignificantHalf(IR::ValuePtr value) {
|
IR::Value IREmitter::LeastSignificantHalf(const IR::Value& value) {
|
||||||
return Inst(IR::Opcode::LeastSignificantHalf, {value});
|
return Inst(IR::Opcode::LeastSignificantHalf, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::LeastSignificantByte(IR::ValuePtr value) {
|
IR::Value IREmitter::LeastSignificantByte(const IR::Value& value) {
|
||||||
return Inst(IR::Opcode::LeastSignificantByte, {value});
|
return Inst(IR::Opcode::LeastSignificantByte, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::MostSignificantBit(IR::ValuePtr value) {
|
IR::Value IREmitter::MostSignificantBit(const IR::Value& value) {
|
||||||
return Inst(IR::Opcode::MostSignificantBit, {value});
|
return Inst(IR::Opcode::MostSignificantBit, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::IsZero(IR::ValuePtr value) {
|
IR::Value IREmitter::IsZero(const IR::Value& value) {
|
||||||
return Inst(IR::Opcode::IsZero, {value});
|
return Inst(IR::Opcode::IsZero, {value});
|
||||||
}
|
}
|
||||||
|
|
||||||
IREmitter::ResultAndCarry IREmitter::LogicalShiftLeft(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
|
IREmitter::ResultAndCarry IREmitter::LogicalShiftLeft(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in) {
|
||||||
auto result = Inst(IR::Opcode::LogicalShiftLeft, {value_in, shift_amount, carry_in});
|
auto result = Inst(IR::Opcode::LogicalShiftLeft, {value_in, shift_amount, carry_in});
|
||||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||||
return {result, carry_out};
|
return {result, carry_out};
|
||||||
}
|
}
|
||||||
|
|
||||||
IREmitter::ResultAndCarry IREmitter::LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
|
IREmitter::ResultAndCarry IREmitter::LogicalShiftRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in) {
|
||||||
auto result = Inst(IR::Opcode::LogicalShiftRight, {value_in, shift_amount, carry_in});
|
auto result = Inst(IR::Opcode::LogicalShiftRight, {value_in, shift_amount, carry_in});
|
||||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||||
return {result, carry_out};
|
return {result, carry_out};
|
||||||
}
|
}
|
||||||
|
|
||||||
IREmitter::ResultAndCarry IREmitter::ArithmeticShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
|
IREmitter::ResultAndCarry IREmitter::ArithmeticShiftRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in) {
|
||||||
auto result = Inst(IR::Opcode::ArithmeticShiftRight, {value_in, shift_amount, carry_in});
|
auto result = Inst(IR::Opcode::ArithmeticShiftRight, {value_in, shift_amount, carry_in});
|
||||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||||
return {result, carry_out};
|
return {result, carry_out};
|
||||||
}
|
}
|
||||||
|
|
||||||
IREmitter::ResultAndCarry IREmitter::RotateRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in) {
|
IREmitter::ResultAndCarry IREmitter::RotateRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in) {
|
||||||
auto result = Inst(IR::Opcode::RotateRight, {value_in, shift_amount, carry_in});
|
auto result = Inst(IR::Opcode::RotateRight, {value_in, shift_amount, carry_in});
|
||||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||||
return {result, carry_out};
|
return {result, carry_out};
|
||||||
}
|
}
|
||||||
|
|
||||||
IREmitter::ResultAndCarryAndOverflow IREmitter::AddWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in) {
|
IREmitter::ResultAndCarryAndOverflow IREmitter::AddWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in) {
|
||||||
auto result = Inst(IR::Opcode::AddWithCarry, {a, b, carry_in});
|
auto result = Inst(IR::Opcode::AddWithCarry, {a, b, carry_in});
|
||||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||||
auto overflow = Inst(IR::Opcode::GetOverflowFromOp, {result});
|
auto overflow = Inst(IR::Opcode::GetOverflowFromOp, {result});
|
||||||
return {result, carry_out, overflow};
|
return {result, carry_out, overflow};
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Add(IR::ValuePtr a, IR::ValuePtr b) {
|
IR::Value IREmitter::Add(const IR::Value& a, const IR::Value& b) {
|
||||||
return Inst(IR::Opcode::AddWithCarry, {a, b, Imm1(0)});
|
return Inst(IR::Opcode::AddWithCarry, {a, b, Imm1(0)});
|
||||||
}
|
}
|
||||||
|
|
||||||
IREmitter::ResultAndCarryAndOverflow IREmitter::SubWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in) {
|
IREmitter::ResultAndCarryAndOverflow IREmitter::SubWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in) {
|
||||||
// This is equivalent to AddWithCarry(a, Not(b), carry_in).
|
// This is equivalent to AddWithCarry(a, Not(b), carry_in).
|
||||||
auto result = Inst(IR::Opcode::SubWithCarry, {a, b, carry_in});
|
auto result = Inst(IR::Opcode::SubWithCarry, {a, b, carry_in});
|
||||||
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
auto carry_out = Inst(IR::Opcode::GetCarryFromOp, {result});
|
||||||
|
@ -163,96 +157,102 @@ IREmitter::ResultAndCarryAndOverflow IREmitter::SubWithCarry(IR::ValuePtr a, IR:
|
||||||
return {result, carry_out, overflow};
|
return {result, carry_out, overflow};
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Sub(IR::ValuePtr a, IR::ValuePtr b) {
|
IR::Value IREmitter::Sub(const IR::Value& a, const IR::Value& b) {
|
||||||
return Inst(IR::Opcode::SubWithCarry, {a, b, Imm1(1)});
|
return Inst(IR::Opcode::SubWithCarry, {a, b, Imm1(1)});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::And(IR::ValuePtr a, IR::ValuePtr b) {
|
IR::Value IREmitter::And(const IR::Value& a, const IR::Value& b) {
|
||||||
return Inst(IR::Opcode::And, {a, b});
|
return Inst(IR::Opcode::And, {a, b});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Eor(IR::ValuePtr a, IR::ValuePtr b) {
|
IR::Value IREmitter::Eor(const IR::Value& a, const IR::Value& b) {
|
||||||
return Inst(IR::Opcode::Eor, {a, b});
|
return Inst(IR::Opcode::Eor, {a, b});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Or(IR::ValuePtr a, IR::ValuePtr b) {
|
IR::Value IREmitter::Or(const IR::Value& a, const IR::Value& b) {
|
||||||
return Inst(IR::Opcode::Or, {a, b});
|
return Inst(IR::Opcode::Or, {a, b});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Not(IR::ValuePtr a) {
|
IR::Value IREmitter::Not(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::Not, {a});
|
return Inst(IR::Opcode::Not, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::SignExtendHalfToWord(IR::ValuePtr a) {
|
IR::Value IREmitter::SignExtendHalfToWord(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::SignExtendHalfToWord, {a});
|
return Inst(IR::Opcode::SignExtendHalfToWord, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::SignExtendByteToWord(IR::ValuePtr a) {
|
IR::Value IREmitter::SignExtendByteToWord(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::SignExtendByteToWord, {a});
|
return Inst(IR::Opcode::SignExtendByteToWord, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ZeroExtendHalfToWord(IR::ValuePtr a) {
|
IR::Value IREmitter::ZeroExtendHalfToWord(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::ZeroExtendHalfToWord, {a});
|
return Inst(IR::Opcode::ZeroExtendHalfToWord, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ZeroExtendByteToWord(IR::ValuePtr a) {
|
IR::Value IREmitter::ZeroExtendByteToWord(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::ZeroExtendByteToWord, {a});
|
return Inst(IR::Opcode::ZeroExtendByteToWord, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ByteReverseWord(IR::ValuePtr a) {
|
IR::Value IREmitter::ByteReverseWord(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::ByteReverseWord, {a});
|
return Inst(IR::Opcode::ByteReverseWord, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ByteReverseHalf(IR::ValuePtr a) {
|
IR::Value IREmitter::ByteReverseHalf(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::ByteReverseHalf, {a});
|
return Inst(IR::Opcode::ByteReverseHalf, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ByteReverseDual(IR::ValuePtr a) {
|
IR::Value IREmitter::ByteReverseDual(const IR::Value& a) {
|
||||||
return Inst(IR::Opcode::ByteReverseDual, {a});
|
return Inst(IR::Opcode::ByteReverseDual, {a});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ReadMemory8(IR::ValuePtr vaddr) {
|
IR::Value IREmitter::ReadMemory8(const IR::Value& vaddr) {
|
||||||
return Inst(IR::Opcode::ReadMemory8, {vaddr});
|
return Inst(IR::Opcode::ReadMemory8, {vaddr});
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ReadMemory16(IR::ValuePtr vaddr) {
|
IR::Value IREmitter::ReadMemory16(const IR::Value& vaddr) {
|
||||||
auto value = Inst(IR::Opcode::ReadMemory16, {vaddr});
|
auto value = Inst(IR::Opcode::ReadMemory16, {vaddr});
|
||||||
return current_location.EFlag ? ByteReverseHalf(value) : value;
|
return current_location.EFlag ? ByteReverseHalf(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ReadMemory32(IR::ValuePtr vaddr) {
|
IR::Value IREmitter::ReadMemory32(const IR::Value& vaddr) {
|
||||||
auto value = Inst(IR::Opcode::ReadMemory32, {vaddr});
|
auto value = Inst(IR::Opcode::ReadMemory32, {vaddr});
|
||||||
return current_location.EFlag ? ByteReverseWord(value) : value;
|
return current_location.EFlag ? ByteReverseWord(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::ReadMemory64(IR::ValuePtr vaddr) {
|
IR::Value IREmitter::ReadMemory64(const IR::Value& vaddr) {
|
||||||
auto value = Inst(IR::Opcode::ReadMemory64, {vaddr});
|
auto value = Inst(IR::Opcode::ReadMemory64, {vaddr});
|
||||||
return current_location.EFlag ? ByteReverseDual(value) : value;
|
return current_location.EFlag ? ByteReverseDual(value) : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory8(IR::ValuePtr vaddr, IR::ValuePtr value) {
|
void IREmitter::WriteMemory8(const IR::Value& vaddr, const IR::Value& value) {
|
||||||
Inst(IR::Opcode::WriteMemory8, {vaddr, value});
|
Inst(IR::Opcode::WriteMemory8, {vaddr, value});
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory16(IR::ValuePtr vaddr, IR::ValuePtr value) {
|
void IREmitter::WriteMemory16(const IR::Value& vaddr, const IR::Value& value) {
|
||||||
if (current_location.EFlag) {
|
if (current_location.EFlag) {
|
||||||
value = ByteReverseHalf(value);
|
auto v = ByteReverseHalf(value);
|
||||||
|
Inst(IR::Opcode::WriteMemory16, {vaddr, v});
|
||||||
|
} else {
|
||||||
|
Inst(IR::Opcode::WriteMemory16, {vaddr, value});
|
||||||
}
|
}
|
||||||
Inst(IR::Opcode::WriteMemory16, {vaddr, value});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory32(IR::ValuePtr vaddr, IR::ValuePtr value) {
|
void IREmitter::WriteMemory32(const IR::Value& vaddr, const IR::Value& value) {
|
||||||
if (current_location.EFlag) {
|
if (current_location.EFlag) {
|
||||||
value = ByteReverseWord(value);
|
auto v = ByteReverseWord(value);
|
||||||
|
Inst(IR::Opcode::WriteMemory32, {vaddr, v});
|
||||||
|
} else {
|
||||||
|
Inst(IR::Opcode::WriteMemory32, {vaddr, value});
|
||||||
}
|
}
|
||||||
Inst(IR::Opcode::WriteMemory32, {vaddr, value});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::WriteMemory64(IR::ValuePtr vaddr, IR::ValuePtr value) {
|
void IREmitter::WriteMemory64(const IR::Value& vaddr, const IR::Value& value) {
|
||||||
if (current_location.EFlag) {
|
if (current_location.EFlag) {
|
||||||
value = ByteReverseDual(value);
|
auto v = ByteReverseDual(value);
|
||||||
|
Inst(IR::Opcode::WriteMemory64, {vaddr, v});
|
||||||
|
} else {
|
||||||
|
Inst(IR::Opcode::WriteMemory64, {vaddr, value});
|
||||||
}
|
}
|
||||||
Inst(IR::Opcode::WriteMemory64, {vaddr, value});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IREmitter::SetTerm(const IR::Terminal& terminal) {
|
void IREmitter::SetTerm(const IR::Terminal& terminal) {
|
||||||
|
@ -260,28 +260,18 @@ void IREmitter::SetTerm(const IR::Terminal& terminal) {
|
||||||
block.terminal = terminal;
|
block.terminal = terminal;
|
||||||
}
|
}
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args) {
|
IR::Value IREmitter::Inst(IR::Opcode op, std::initializer_list<IR::Value> args) {
|
||||||
auto inst = std::make_shared<IR::Inst>(op);
|
IR::Inst* inst = new(block.instruction_alloc_pool->malloc()) IR::Inst(op);
|
||||||
assert(args.size() == inst->NumArgs());
|
DEBUG_ASSERT(args.size() == inst->NumArgs());
|
||||||
|
|
||||||
std::for_each(args.begin(), args.end(), [&inst, op, index = size_t(0)](const auto& v) mutable {
|
std::for_each(args.begin(), args.end(), [&inst, op, index = size_t(0)](const auto& v) mutable {
|
||||||
assert(IR::GetArgTypeOf(op, index) == v->GetType());
|
DEBUG_ASSERT(IR::GetArgTypeOf(op, index) == v.GetType());
|
||||||
inst->SetArg(index, v);
|
inst->SetArg(index, v);
|
||||||
index++;
|
index++;
|
||||||
});
|
});
|
||||||
|
|
||||||
AddToBlock(inst);
|
block.instructions.push_back(*inst);
|
||||||
return inst;
|
return IR::Value(inst);
|
||||||
}
|
|
||||||
|
|
||||||
IR::ValuePtr IREmitter::RegRef(Reg reg) {
|
|
||||||
auto regref = std::make_shared<IR::ImmRegRef>(reg);
|
|
||||||
AddToBlock(regref);
|
|
||||||
return regref;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IREmitter::AddToBlock(IR::ValuePtr value) {
|
|
||||||
block.instructions.emplace_back(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Arm
|
} // namespace Arm
|
||||||
|
|
|
@ -21,79 +21,77 @@ public:
|
||||||
LocationDescriptor current_location;
|
LocationDescriptor current_location;
|
||||||
|
|
||||||
struct ResultAndCarry {
|
struct ResultAndCarry {
|
||||||
IR::ValuePtr result;
|
IR::Value result;
|
||||||
IR::ValuePtr carry;
|
IR::Value carry;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ResultAndCarryAndOverflow {
|
struct ResultAndCarryAndOverflow {
|
||||||
IR::ValuePtr result;
|
IR::Value result;
|
||||||
IR::ValuePtr carry;
|
IR::Value carry;
|
||||||
IR::ValuePtr overflow;
|
IR::Value overflow;
|
||||||
};
|
};
|
||||||
|
|
||||||
void Unimplemented();
|
void Unimplemented();
|
||||||
u32 PC();
|
u32 PC();
|
||||||
u32 AlignPC(size_t alignment);
|
u32 AlignPC(size_t alignment);
|
||||||
|
|
||||||
IR::ValuePtr Imm1(bool value);
|
IR::Value Imm1(bool value);
|
||||||
IR::ValuePtr Imm8(u8 value);
|
IR::Value Imm8(u8 value);
|
||||||
IR::ValuePtr Imm32(u32 value);
|
IR::Value Imm32(u32 value);
|
||||||
|
|
||||||
IR::ValuePtr GetRegister(Reg source_reg);
|
IR::Value GetRegister(Reg source_reg);
|
||||||
void SetRegister(const Reg dest_reg, IR::ValuePtr value);
|
void SetRegister(const Reg dest_reg, const IR::Value& value);
|
||||||
|
|
||||||
void ALUWritePC(IR::ValuePtr value);
|
void ALUWritePC(const IR::Value& value);
|
||||||
void BranchWritePC(IR::ValuePtr value);
|
void BranchWritePC(const IR::Value& value);
|
||||||
void BXWritePC(IR::ValuePtr value);
|
void BXWritePC(const IR::Value& value);
|
||||||
void LoadWritePC(IR::ValuePtr value);
|
void LoadWritePC(const IR::Value& value);
|
||||||
void CallSupervisor(IR::ValuePtr value);
|
void CallSupervisor(const IR::Value& value);
|
||||||
|
|
||||||
IR::ValuePtr GetCFlag();
|
IR::Value GetCFlag();
|
||||||
void SetNFlag(IR::ValuePtr value);
|
void SetNFlag(const IR::Value& value);
|
||||||
void SetZFlag(IR::ValuePtr value);
|
void SetZFlag(const IR::Value& value);
|
||||||
void SetCFlag(IR::ValuePtr value);
|
void SetCFlag(const IR::Value& value);
|
||||||
void SetVFlag(IR::ValuePtr value);
|
void SetVFlag(const IR::Value& value);
|
||||||
|
|
||||||
IR::ValuePtr LeastSignificantHalf(IR::ValuePtr value);
|
IR::Value LeastSignificantHalf(const IR::Value& value);
|
||||||
IR::ValuePtr LeastSignificantByte(IR::ValuePtr value);
|
IR::Value LeastSignificantByte(const IR::Value& value);
|
||||||
IR::ValuePtr MostSignificantBit(IR::ValuePtr value);
|
IR::Value MostSignificantBit(const IR::Value& value);
|
||||||
IR::ValuePtr IsZero(IR::ValuePtr value);
|
IR::Value IsZero(const IR::Value& value);
|
||||||
|
|
||||||
ResultAndCarry LogicalShiftLeft(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
ResultAndCarry LogicalShiftLeft(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in);
|
||||||
ResultAndCarry LogicalShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
ResultAndCarry LogicalShiftRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in);
|
||||||
ResultAndCarry ArithmeticShiftRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
ResultAndCarry ArithmeticShiftRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in);
|
||||||
ResultAndCarry RotateRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in);
|
ResultAndCarry RotateRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in);
|
||||||
ResultAndCarryAndOverflow AddWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in);
|
ResultAndCarryAndOverflow AddWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in);
|
||||||
IR::ValuePtr Add(IR::ValuePtr a, IR::ValuePtr b);
|
IR::Value Add(const IR::Value& a, const IR::Value& b);
|
||||||
ResultAndCarryAndOverflow SubWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in);
|
ResultAndCarryAndOverflow SubWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in);
|
||||||
IR::ValuePtr Sub(IR::ValuePtr a, IR::ValuePtr b);
|
IR::Value Sub(const IR::Value& a, const IR::Value& b);
|
||||||
IR::ValuePtr And(IR::ValuePtr a, IR::ValuePtr b);
|
IR::Value And(const IR::Value& a, const IR::Value& b);
|
||||||
IR::ValuePtr Eor(IR::ValuePtr a, IR::ValuePtr b);
|
IR::Value Eor(const IR::Value& a, const IR::Value& b);
|
||||||
IR::ValuePtr Or(IR::ValuePtr a, IR::ValuePtr b);
|
IR::Value Or(const IR::Value& a, const IR::Value& b);
|
||||||
IR::ValuePtr Not(IR::ValuePtr a);
|
IR::Value Not(const IR::Value& a);
|
||||||
IR::ValuePtr SignExtendHalfToWord(IR::ValuePtr a);
|
IR::Value SignExtendHalfToWord(const IR::Value& a);
|
||||||
IR::ValuePtr SignExtendByteToWord(IR::ValuePtr a);
|
IR::Value SignExtendByteToWord(const IR::Value& a);
|
||||||
IR::ValuePtr ZeroExtendHalfToWord(IR::ValuePtr a);
|
IR::Value ZeroExtendHalfToWord(const IR::Value& a);
|
||||||
IR::ValuePtr ZeroExtendByteToWord(IR::ValuePtr a);
|
IR::Value ZeroExtendByteToWord(const IR::Value& a);
|
||||||
IR::ValuePtr ByteReverseWord(IR::ValuePtr a);
|
IR::Value ByteReverseWord(const IR::Value& a);
|
||||||
IR::ValuePtr ByteReverseHalf(IR::ValuePtr a);
|
IR::Value ByteReverseHalf(const IR::Value& a);
|
||||||
IR::ValuePtr ByteReverseDual(IR::ValuePtr a);
|
IR::Value ByteReverseDual(const IR::Value& a);
|
||||||
|
|
||||||
IR::ValuePtr ReadMemory8(IR::ValuePtr vaddr);
|
IR::Value ReadMemory8(const IR::Value& vaddr);
|
||||||
IR::ValuePtr ReadMemory16(IR::ValuePtr vaddr);
|
IR::Value ReadMemory16(const IR::Value& vaddr);
|
||||||
IR::ValuePtr ReadMemory32(IR::ValuePtr vaddr);
|
IR::Value ReadMemory32(const IR::Value& vaddr);
|
||||||
IR::ValuePtr ReadMemory64(IR::ValuePtr vaddr);
|
IR::Value ReadMemory64(const IR::Value& vaddr);
|
||||||
void WriteMemory8(IR::ValuePtr vaddr, IR::ValuePtr value);
|
void WriteMemory8(const IR::Value& vaddr, const IR::Value& value);
|
||||||
void WriteMemory16(IR::ValuePtr vaddr, IR::ValuePtr value);
|
void WriteMemory16(const IR::Value& vaddr, const IR::Value& value);
|
||||||
void WriteMemory32(IR::ValuePtr vaddr, IR::ValuePtr value);
|
void WriteMemory32(const IR::Value& vaddr, const IR::Value& value);
|
||||||
void WriteMemory64(IR::ValuePtr vaddr, IR::ValuePtr value);
|
void WriteMemory64(const IR::Value& vaddr, const IR::Value& value);
|
||||||
|
|
||||||
void SetTerm(const IR::Terminal& terminal);
|
void SetTerm(const IR::Terminal& terminal);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IR::ValuePtr Inst(IR::Opcode op, std::initializer_list<IR::ValuePtr> args);
|
IR::Value Inst(IR::Opcode op, std::initializer_list<IR::Value> args);
|
||||||
IR::ValuePtr RegRef(Reg reg);
|
|
||||||
void AddToBlock(IR::ValuePtr value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Arm
|
} // namespace Arm
|
||||||
|
|
|
@ -1,11 +1,5 @@
|
||||||
// opcode name, return type, arg1 type, arg2 type, arg3 type, ...
|
// opcode name, return type, arg1 type, arg2 type, arg3 type, ...
|
||||||
|
|
||||||
// Immediate values
|
|
||||||
OPCODE(ImmU1, T::U1, )
|
|
||||||
OPCODE(ImmU8, T::U8, )
|
|
||||||
OPCODE(ImmU32, T::U32, )
|
|
||||||
OPCODE(ImmRegRef, T::RegRef, )
|
|
||||||
|
|
||||||
// ARM Context getters/setters
|
// ARM Context getters/setters
|
||||||
OPCODE(GetRegister, T::U32, T::RegRef )
|
OPCODE(GetRegister, T::U32, T::RegRef )
|
||||||
OPCODE(SetRegister, T::Void, T::RegRef, T::U32 )
|
OPCODE(SetRegister, T::Void, T::RegRef, T::U32 )
|
||||||
|
|
|
@ -340,7 +340,7 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor
|
||||||
visitor.ir.block.cond_failed = { visitor.ir.current_location };
|
visitor.ir.block.cond_failed = { visitor.ir.current_location };
|
||||||
}
|
}
|
||||||
|
|
||||||
return visitor.ir.block;
|
return std::move(visitor.ir.block);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Arm
|
} // namespace Arm
|
||||||
|
|
|
@ -888,7 +888,7 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryRead32FuncType mem
|
||||||
visitor.ir.block.cycle_count++;
|
visitor.ir.block.cycle_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return visitor.ir.block;
|
return std::move(visitor.ir.block);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Arm
|
} // namespace Arm
|
||||||
|
|
|
@ -26,8 +26,8 @@ void DeadCodeElimination(IR::Block& block) {
|
||||||
auto iter = block.instructions.end();
|
auto iter = block.instructions.end();
|
||||||
do {
|
do {
|
||||||
--iter;
|
--iter;
|
||||||
if (!(*iter)->HasUses() && is_side_effect_free((*iter)->GetOpcode())) {
|
if (!iter->HasUses() && is_side_effect_free(iter->GetOpcode())) {
|
||||||
(*iter)->Invalidate();
|
iter->Invalidate();
|
||||||
iter = block.instructions.erase(iter);
|
iter = block.instructions.erase(iter);
|
||||||
}
|
}
|
||||||
} while (iter != block.instructions.begin());
|
} while (iter != block.instructions.begin());
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace Dynarmic {
|
||||||
namespace Optimization {
|
namespace Optimization {
|
||||||
|
|
||||||
void GetSetElimination(IR::Block& block) {
|
void GetSetElimination(IR::Block& block) {
|
||||||
|
#if 0
|
||||||
using Iterator = decltype(block.instructions.begin());
|
using Iterator = decltype(block.instructions.begin());
|
||||||
struct RegisterInfo {
|
struct RegisterInfo {
|
||||||
IR::ValuePtr register_value = nullptr;
|
IR::ValuePtr register_value = nullptr;
|
||||||
|
@ -102,6 +103,7 @@ void GetSetElimination(IR::Block& block) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Optimization
|
} // namespace Optimization
|
||||||
|
|
|
@ -12,9 +12,11 @@ namespace Dynarmic {
|
||||||
namespace Optimization {
|
namespace Optimization {
|
||||||
|
|
||||||
void VerificationPass(const IR::Block& block) {
|
void VerificationPass(const IR::Block& block) {
|
||||||
|
#if 0
|
||||||
for (const auto& inst : block.instructions) {
|
for (const auto& inst : block.instructions) {
|
||||||
inst->AssertValid();
|
inst->AssertValid();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Optimization
|
} // namespace Optimization
|
||||||
|
|
Loading…
Add table
Reference in a new issue