From 51448aa06d3f731054ae3b33a9e203426fba57d8 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Fri, 22 Jul 2016 23:55:00 +0100 Subject: [PATCH] More Speed --- CMakeLists.txt | 2 + src/backend_x64/emit_x64.cpp | 996 ++++++++++++--------- src/backend_x64/emit_x64.h | 92 +- src/backend_x64/reg_alloc.cpp | 222 ++--- src/backend_x64/reg_alloc.h | 30 +- src/common/assert.h | 2 +- src/frontend/ir/ir.cpp | 204 ++--- src/frontend/ir/ir.h | 197 ++-- src/frontend/ir/ir_emitter.cpp | 152 ++-- src/frontend/ir/ir_emitter.h | 106 ++- src/frontend/ir/opcodes.inc | 6 - src/frontend/translate/translate_arm.cpp | 2 +- src/frontend/translate/translate_thumb.cpp | 2 +- src/ir_opt/dead_code_elimination_pass.cpp | 4 +- src/ir_opt/get_set_elimination_pass.cpp | 2 + src/ir_opt/verification_pass.cpp | 2 + 16 files changed, 1066 insertions(+), 955 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ceee9a56..5f0aae0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,13 @@ option(DYNARMIC_USE_SYSTEM_BOOST "Use the system boost libraries" ON) # Compiler flags if (NOT MSVC) 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) add_compile_options(-msse4.1) endif() else() add_compile_options(/W3 /MP /Zi /Zo /EHsc /WX) + add_compile_options(/DBOOST_SYSTEM_NO_DEPRECATED) endif() # This function should be passed a list of all files in a target. It will automatically generate diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 5de7839c..3e824b79 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "backend_x64/emit_x64.h" #include "common/x64/abi.h" @@ -30,14 +31,24 @@ static OpArg MJitStateCpsr() { } static IR::Inst* FindUseWithOpcode(IR::Inst* inst, IR::Opcode opcode) { - // Gets first found use. - auto uses = inst->GetUses(); - auto iter = std::find_if(uses.begin(), uses.end(), [opcode](const auto& use){ return use->GetOpcode() == opcode; }); - ASSERT(std::count_if(uses.begin(), uses.end(), [opcode](const auto& use){ return use->GetOpcode() == opcode; }) <= 1); - return iter == uses.end() ? nullptr : reinterpret_cast(iter->get()); + switch (opcode) { + case IR::Opcode::GetCarryFromOp: + return inst->carry_inst; + case IR::Opcode::GetOverflowFromOp: + return inst->overflow_inst; + default: + break; + } + + ASSERT_MSG(false, "unreachable"); + return nullptr; } -CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, const Dynarmic::IR::Block& block) { +static void EraseInstruction(IR::Block& block, IR::Inst* inst) { + block.instructions.erase(block.instructions.iterator_to(*inst)); +} + +CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, Dynarmic::IR::Block& block) { inhibit_emission.clear(); reg_alloc.Reset(); @@ -47,22 +58,21 @@ CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, const Dynarmic:: EmitCondPrelude(block.cond, block.cond_failed, block.location); - for (const auto& value : block.instructions) { - if (inhibit_emission.count(value.get()) != 0) - continue; + for (auto iter = block.instructions.begin(); iter != block.instructions.end(); ++iter) { + IR::Inst* inst = &*iter; // Call the relevant Emit* member function. - switch (value->GetOpcode()) { + switch (inst->GetOpcode()) { -#define OPCODE(name, type, ...) \ - case IR::Opcode::name: \ - EmitX64::Emit##name(value.get()); \ +#define OPCODE(name, type, ...) \ + case IR::Opcode::name: \ + EmitX64::Emit##name(block, inst); \ break; #include "frontend/ir/opcodes.inc" #undef OPCODE default: - ASSERT_MSG(false, "Invalid opcode %zu", static_cast(value->GetOpcode())); + ASSERT_MSG(false, "Invalid opcode %zu", static_cast(inst->GetOpcode())); break; } @@ -77,153 +87,129 @@ CodePtr EmitX64::Emit(const Arm::LocationDescriptor descriptor, const Dynarmic:: return code_ptr; } -void EmitX64::EmitImmU1(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.DefRegister(value); - - code->MOV(32, R(result), Imm32(value->value)); +void EmitX64::EmitGetRegister(IR::Block&, IR::Inst* inst) { + Arm::Reg reg = inst->GetArg(0).GetRegRef(); + X64Reg result = reg_alloc.DefRegister(inst); + code->MOV(32, R(result), MJitStateReg(reg)); } -void EmitX64::EmitImmU8(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.DefRegister(value); - - code->MOV(32, R(result), Imm32(value->value)); +void EmitX64::EmitSetRegister(IR::Block&, IR::Inst* inst) { + Arm::Reg reg = inst->GetArg(0).GetRegRef(); + IR::Value arg = inst->GetArg(1); + if (arg.IsImmediate()) { + code->MOV(32, MJitStateReg(reg), Imm32(arg.GetU32())); + } else { + X64Reg to_store = reg_alloc.UseRegister(arg.GetInst()); + code->MOV(32, MJitStateReg(reg), R(to_store)); + } } -void EmitX64::EmitImmU32(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.DefRegister(value); - - code->MOV(32, R(result), Imm32(value->value)); -} - -void EmitX64::EmitImmRegRef(IR::Value*) { - return; // No need to do anything. -} - -void EmitX64::EmitGetRegister(IR::Value* value_) { - auto value = reinterpret_cast(value_); - auto regref = reinterpret_cast(value->GetArg(0).get()); - - X64Reg result = reg_alloc.DefRegister(value); - - code->MOV(32, R(result), MJitStateReg(regref->value)); -} - -void EmitX64::EmitSetRegister(IR::Value* value_) { - auto value = reinterpret_cast(value_); - auto regref = reinterpret_cast(value->GetArg(0).get()); - - X64Reg to_store = reg_alloc.UseRegister(value->GetArg(1).get()); - - code->MOV(32, MJitStateReg(regref->value), R(to_store)); -} - -void EmitX64::EmitGetNFlag(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.DefRegister(value); - - // TODO: Flag optimization - +void EmitX64::EmitGetNFlag(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.DefRegister(inst); code->MOV(32, R(result), MJitStateCpsr()); code->SHR(32, R(result), Imm8(31)); } -void EmitX64::EmitSetNFlag(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitSetNFlag(IR::Block&, IR::Inst* inst) { + constexpr size_t flag_bit = 31; + constexpr u32 flag_mask = 1u << flag_bit; + IR::Value arg = inst->GetArg(0); + if (arg.IsImmediate()) { + if (arg.GetU1()) { + code->OR(32, MJitStateCpsr(), Imm32(flag_mask)); + } else { + code->AND(32, MJitStateCpsr(), Imm32(~flag_mask)); + } + } else { + X64Reg to_store = reg_alloc.UseScratchRegister(arg.GetInst()); - X64Reg to_store = reg_alloc.UseScratchRegister(value->GetArg(0).get()); - - // TODO: Flag optimization - - code->SHL(32, R(to_store), Imm8(31)); - code->AND(32, MJitStateCpsr(), Imm32(~static_cast(1 << 31))); - code->OR(32, MJitStateCpsr(), R(to_store)); + code->SHL(32, R(to_store), Imm8(flag_bit)); + code->AND(32, MJitStateCpsr(), Imm32(~flag_mask)); + code->OR(32, MJitStateCpsr(), R(to_store)); + } } -void EmitX64::EmitGetZFlag(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.DefRegister(value); - - // TODO: Flag optimization - +void EmitX64::EmitGetZFlag(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.DefRegister(inst); code->MOV(32, R(result), MJitStateCpsr()); code->SHR(32, R(result), Imm8(30)); code->AND(32, R(result), Imm32(1)); } -void EmitX64::EmitSetZFlag(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitSetZFlag(IR::Block&, IR::Inst* inst) { + constexpr size_t flag_bit = 30; + constexpr u32 flag_mask = 1u << flag_bit; + IR::Value arg = inst->GetArg(0); + if (arg.IsImmediate()) { + if (arg.GetU1()) { + code->OR(32, MJitStateCpsr(), Imm32(flag_mask)); + } else { + code->AND(32, MJitStateCpsr(), Imm32(~flag_mask)); + } + } else { + X64Reg to_store = reg_alloc.UseScratchRegister(arg.GetInst()); - X64Reg to_store = reg_alloc.UseScratchRegister(value->GetArg(0).get()); - - // TODO: Flag optimization - - code->SHL(32, R(to_store), Imm8(30)); - code->AND(32, MJitStateCpsr(), Imm32(~static_cast(1 << 30))); - code->OR(32, MJitStateCpsr(), R(to_store)); + code->SHL(32, R(to_store), Imm8(flag_bit)); + code->AND(32, MJitStateCpsr(), Imm32(~flag_mask)); + code->OR(32, MJitStateCpsr(), R(to_store)); + } } -void EmitX64::EmitGetCFlag(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.DefRegister(value); - - // TODO: Flag optimization - +void EmitX64::EmitGetCFlag(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.DefRegister(inst); code->MOV(32, R(result), MJitStateCpsr()); code->SHR(32, R(result), Imm8(29)); code->AND(32, R(result), Imm32(1)); } -void EmitX64::EmitSetCFlag(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitSetCFlag(IR::Block&, IR::Inst* inst) { + constexpr size_t flag_bit = 29; + constexpr u32 flag_mask = 1u << flag_bit; + IR::Value arg = inst->GetArg(0); + if (arg.IsImmediate()) { + if (arg.GetU1()) { + code->OR(32, MJitStateCpsr(), Imm32(flag_mask)); + } else { + code->AND(32, MJitStateCpsr(), Imm32(~flag_mask)); + } + } else { + X64Reg to_store = reg_alloc.UseScratchRegister(arg.GetInst()); - X64Reg to_store = reg_alloc.UseScratchRegister(value->GetArg(0).get()); - - // TODO: Flag optimization - - code->SHL(32, R(to_store), Imm8(29)); - code->AND(32, MJitStateCpsr(), Imm32(~static_cast(1 << 29))); - code->OR(32, MJitStateCpsr(), R(to_store)); + code->SHL(32, R(to_store), Imm8(flag_bit)); + code->AND(32, MJitStateCpsr(), Imm32(~flag_mask)); + code->OR(32, MJitStateCpsr(), R(to_store)); + } } -void EmitX64::EmitGetVFlag(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.DefRegister(value); - - // TODO: Flag optimization - +void EmitX64::EmitGetVFlag(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.DefRegister(inst); code->MOV(32, R(result), MJitStateCpsr()); code->SHR(32, R(result), Imm8(28)); code->AND(32, R(result), Imm32(1)); } -void EmitX64::EmitSetVFlag(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitSetVFlag(IR::Block&, IR::Inst* inst) { + constexpr size_t flag_bit = 28; + constexpr u32 flag_mask = 1u << flag_bit; + IR::Value arg = inst->GetArg(0); + if (arg.IsImmediate()) { + if (arg.GetU1()) { + code->OR(32, MJitStateCpsr(), Imm32(flag_mask)); + } else { + code->AND(32, MJitStateCpsr(), Imm32(~flag_mask)); + } + } else { + X64Reg to_store = reg_alloc.UseScratchRegister(arg.GetInst()); - X64Reg to_store = reg_alloc.UseScratchRegister(value->GetArg(0).get()); - - // TODO: Flag optimization - - code->SHL(32, R(to_store), Imm8(28)); - code->AND(32, MJitStateCpsr(), Imm32(~static_cast(1 << 28))); - code->OR(32, MJitStateCpsr(), R(to_store)); + code->SHL(32, R(to_store), Imm8(flag_bit)); + code->AND(32, MJitStateCpsr(), Imm32(~flag_mask)); + code->OR(32, MJitStateCpsr(), R(to_store)); + } } -void EmitX64::EmitBXWritePC(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg new_pc = reg_alloc.UseScratchRegister(value->GetArg(0).get()); - X64Reg tmp1 = reg_alloc.ScratchRegister(); - X64Reg tmp2 = reg_alloc.ScratchRegister(); +void EmitX64::EmitBXWritePC(IR::Block&, IR::Inst* inst) { + const u32 T_bit = 1 << 5; + auto arg = inst->GetArg(0); // Pseudocode: // if (new_pc & 1) { @@ -234,66 +220,74 @@ void EmitX64::EmitBXWritePC(IR::Value* value_) { // cpsr.T = false; // } - code->MOV(32, R(tmp1), MJitStateCpsr()); - code->MOV(32, R(tmp2), R(tmp1)); - code->AND(32, R(tmp2), Imm32(~(1 << 5))); // CPSR.T = 0 - code->OR(32, R(tmp1), Imm32(1 << 5)); // CPSR.T = 1 - code->TEST(8, R(new_pc), Imm8(1)); - code->CMOVcc(32, tmp1, R(tmp2), CC_E); // CPSR.T = pc & 1 - code->MOV(32, MJitStateCpsr(), R(tmp1)); - code->LEA(32, tmp2, MComplex(new_pc, new_pc, 1, 0)); - code->OR(32, R(tmp2), Imm32(0xFFFFFFFC)); // tmp2 = pc & 1 ? 0xFFFFFFFE : 0xFFFFFFFC - code->AND(32, R(new_pc), R(tmp2)); - code->MOV(32, MJitStateReg(Arm::Reg::PC), R(new_pc)); + if (arg.IsImmediate()) { + u32 new_pc = arg.GetU32(); + if (Common::Bit<0>(new_pc)) { + new_pc &= 0xFFFFFFFE; + code->MOV(32, MJitStateReg(Arm::Reg::PC), Imm32(new_pc)); + code->OR(32, MJitStateCpsr(), Imm32(T_bit)); + } else { + new_pc &= 0xFFFFFFFC; + code->MOV(32, MJitStateReg(Arm::Reg::PC), Imm32(new_pc)); + code->AND(32, MJitStateCpsr(), Imm32(~T_bit)); + } + } else { + X64Reg new_pc = reg_alloc.UseScratchRegister(arg.GetInst()); + X64Reg tmp1 = reg_alloc.ScratchRegister(); + X64Reg tmp2 = reg_alloc.ScratchRegister(); + + code->MOV(32, R(tmp1), MJitStateCpsr()); + code->MOV(32, R(tmp2), R(tmp1)); + code->AND(32, R(tmp2), Imm32(~T_bit)); // CPSR.T = 0 + code->OR(32, R(tmp1), Imm32(T_bit)); // CPSR.T = 1 + code->TEST(8, R(new_pc), Imm8(1)); + code->CMOVcc(32, tmp1, R(tmp2), CC_E); // CPSR.T = pc & 1 + code->MOV(32, MJitStateCpsr(), R(tmp1)); + code->LEA(32, tmp2, MComplex(new_pc, new_pc, 1, 0)); + code->OR(32, R(tmp2), Imm32(0xFFFFFFFC)); // tmp2 = pc & 1 ? 0xFFFFFFFE : 0xFFFFFFFC + code->AND(32, R(new_pc), R(tmp2)); + code->MOV(32, MJitStateReg(Arm::Reg::PC), R(new_pc)); + } } -void EmitX64::EmitCallSupervisor(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitCallSupervisor(IR::Block&, IR::Inst* inst) { + auto imm32 = inst->GetArg(0); - auto imm32 = value->GetArg(0).get(); reg_alloc.HostCall(nullptr, imm32); code->ABI_CallFunction(reinterpret_cast(cb.CallSVC)); } -void EmitX64::EmitGetCarryFromOp(IR::Value*) { +void EmitX64::EmitGetCarryFromOp(IR::Block&, IR::Inst*) { ASSERT_MSG(0, "should never happen"); } -void EmitX64::EmitGetOverflowFromOp(IR::Value*) { +void EmitX64::EmitGetOverflowFromOp(IR::Block&, IR::Inst*) { ASSERT_MSG(0, "should never happen"); } -void EmitX64::EmitLeastSignificantHalf(IR::Value* value_) { - auto value = reinterpret_cast(value_); - +void EmitX64::EmitLeastSignificantHalf(IR::Block&, IR::Inst* inst) { // TODO: Optimize - reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + reg_alloc.UseDefRegister(inst->GetArg(0), inst); } -void EmitX64::EmitLeastSignificantByte(IR::Value* value_) { - auto value = reinterpret_cast(value_); - +void EmitX64::EmitLeastSignificantByte(IR::Block&, IR::Inst* inst) { // TODO: Optimize - reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + reg_alloc.UseDefRegister(inst->GetArg(0), inst); } -void EmitX64::EmitMostSignificantBit(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); +void EmitX64::EmitMostSignificantBit(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); // TODO: Flag optimization code->SHR(32, R(result), Imm8(31)); } -void EmitX64::EmitIsZero(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); +void EmitX64::EmitIsZero(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); // TODO: Flag optimization @@ -302,427 +296,577 @@ void EmitX64::EmitIsZero(IR::Value* value_) { code->MOVZX(32, 8, result, R(result)); } -void EmitX64::EmitLogicalShiftLeft(IR::Value* value_) { - auto value = reinterpret_cast(value_); - auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp); +void EmitX64::EmitLogicalShiftLeft(IR::Block& block, IR::Inst* inst) { + auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp); // TODO: Consider using BMI2 instructions like SHLX when arm-in-host flags is implemented. if (!carry_inst) { - X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX}); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg zero = reg_alloc.ScratchRegister(); - reg_alloc.DecrementRemainingUses(value->GetArg(2).get()); + if (!inst->GetArg(2).IsImmediate()) { + // TODO: Remove redundant argument. + reg_alloc.DecrementRemainingUses(inst->GetArg(2).GetInst()); + } - // The 32-bit x64 SHL instruction masks the shift count by 0x1F before performing the shift. - // ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros. + auto shift_arg = inst->GetArg(1); - code->SHL(32, R(result), R(shift)); - code->XOR(32, R(zero), R(zero)); - code->CMP(8, R(shift), Imm8(32)); - code->CMOVcc(32, result, R(zero), CC_NB); + if (shift_arg.IsImmediate()) { + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + u8 shift = shift_arg.GetU8(); + + if (shift <= 31) { + code->SHL(32, R(result), Imm8(shift)); + } else { + code->XOR(32, R(result), R(result)); + } + } else { + X64Reg shift = reg_alloc.UseRegister(shift_arg.GetInst(), {HostLoc::RCX}); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg zero = reg_alloc.ScratchRegister(); + + // The 32-bit x64 SHL instruction masks the shift count by 0x1F before performing the shift. + // ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros. + + code->SHL(32, R(result), R(shift)); + code->XOR(32, R(zero), R(zero)); + code->CMP(8, R(shift), Imm8(32)); + code->CMOVcc(32, result, R(zero), CC_NB); + } } else { - inhibit_emission.insert(carry_inst); + EraseInstruction(block, carry_inst); + reg_alloc.DecrementRemainingUses(inst); - X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX}); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst); + auto shift_arg = inst->GetArg(1); - reg_alloc.DecrementRemainingUses(value); + if (shift_arg.IsImmediate()) { + u8 shift = shift_arg.GetU8(); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg carry = reg_alloc.UseDefRegister(inst->GetArg(2), carry_inst); - // TODO: Optimize this. + if (shift == 0) { + // There is nothing more to do. + } else if (shift < 32) { + code->BT(32, R(carry), Imm8(0)); + code->SHL(32, R(result), Imm8(shift)); + code->SETcc(CC_C, R(carry)); + } else if (shift > 32) { + code->XOR(32, R(result), R(result)); + code->XOR(32, R(carry), R(carry)); + } else { + code->MOV(32, R(carry), R(result)); + code->XOR(32, R(result), R(result)); + code->AND(32, R(carry), Imm32(1)); + } + } else { + X64Reg shift = reg_alloc.UseRegister(shift_arg.GetInst(), {HostLoc::RCX}); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg carry = reg_alloc.UseDefRegister(inst->GetArg(2), carry_inst); - code->CMP(8, R(shift), Imm8(32)); - auto Rs_gt32 = code->J_CC(CC_A); - auto Rs_eq32 = code->J_CC(CC_E); - // if (Rs & 0xFF < 32) { - code->BT(32, R(carry), Imm8(0)); // Set the carry flag for correct behaviour in the case when Rs & 0xFF == 0 - code->SHL(32, R(result), R(shift)); - code->SETcc(CC_C, R(carry)); - auto jmp_to_end_1 = code->J(); - // } else if (Rs & 0xFF > 32) { - code->SetJumpTarget(Rs_gt32); - code->XOR(32, R(result), R(result)); - code->XOR(32, R(carry), R(carry)); - auto jmp_to_end_2 = code->J(); - // } else if (Rs & 0xFF == 32) { - code->SetJumpTarget(Rs_eq32); - code->MOV(32, R(carry), R(result)); - code->AND(32, R(carry), Imm8(1)); - code->XOR(32, R(result), R(result)); - // } - code->SetJumpTarget(jmp_to_end_1); - code->SetJumpTarget(jmp_to_end_2); + // TODO: Optimize this. + + code->CMP(8, R(shift), Imm8(32)); + auto Rs_gt32 = code->J_CC(CC_A); + auto Rs_eq32 = code->J_CC(CC_E); + // if (Rs & 0xFF < 32) { + code->BT(32, R(carry), Imm8(0)); // Set the carry flag for correct behaviour in the case when Rs & 0xFF == 0 + code->SHL(32, R(result), R(shift)); + code->SETcc(CC_C, R(carry)); + auto jmp_to_end_1 = code->J(); + // } else if (Rs & 0xFF > 32) { + code->SetJumpTarget(Rs_gt32); + code->XOR(32, R(result), R(result)); + code->XOR(32, R(carry), R(carry)); + auto jmp_to_end_2 = code->J(); + // } else if (Rs & 0xFF == 32) { + code->SetJumpTarget(Rs_eq32); + code->MOV(32, R(carry), R(result)); + code->AND(32, R(carry), Imm8(1)); + code->XOR(32, R(result), R(result)); + // } + code->SetJumpTarget(jmp_to_end_1); + code->SetJumpTarget(jmp_to_end_2); + } } } -void EmitX64::EmitLogicalShiftRight(IR::Value* value_) { - auto value = reinterpret_cast(value_); - auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp); +void EmitX64::EmitLogicalShiftRight(IR::Block& block, IR::Inst* inst) { + auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp); if (!carry_inst) { - X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX}); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg zero = reg_alloc.ScratchRegister(); - reg_alloc.DecrementRemainingUses(value->GetArg(2).get()); + if (!inst->GetArg(2).IsImmediate()) { + // TODO: Remove redundant argument. + reg_alloc.DecrementRemainingUses(inst->GetArg(2).GetInst()); + } - // The 32-bit x64 SHR instruction masks the shift count by 0x1F before performing the shift. - // ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros. + auto shift_arg = inst->GetArg(1); - code->SHR(32, R(result), R(shift)); - code->XOR(32, R(zero), R(zero)); - code->CMP(8, R(shift), Imm8(32)); - code->CMOVcc(32, result, R(zero), CC_NB); + if (shift_arg.IsImmediate()) { + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + u8 shift = shift_arg.GetU8(); + + if (shift <= 31) { + code->SHR(32, R(result), Imm8(shift)); + } else { + code->XOR(32, R(result), R(result)); + } + } else { + X64Reg shift = reg_alloc.UseRegister(shift_arg.GetInst(), {HostLoc::RCX}); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg zero = reg_alloc.ScratchRegister(); + + // The 32-bit x64 SHR instruction masks the shift count by 0x1F before performing the shift. + // ARM differs from the behaviour: It does not mask the count, so shifts above 31 result in zeros. + + code->SHR(32, R(result), R(shift)); + code->XOR(32, R(zero), R(zero)); + code->CMP(8, R(shift), Imm8(32)); + code->CMOVcc(32, result, R(zero), CC_NB); + } } else { - inhibit_emission.insert(carry_inst); + EraseInstruction(block, carry_inst); + reg_alloc.DecrementRemainingUses(inst); - X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX}); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst); + auto shift_arg = inst->GetArg(1); - reg_alloc.DecrementRemainingUses(value); + if (shift_arg.IsImmediate()) { + u8 shift = shift_arg.GetU8(); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg carry = reg_alloc.UseDefRegister(inst->GetArg(2), carry_inst); - // TODO: Optimize this. + if (shift == 0) { + // There is nothing more to do. + } else if (shift < 32) { + code->SHR(32, R(result), Imm8(shift)); + code->SETcc(CC_C, R(carry)); + } else if (shift == 32) { + code->BT(32, R(result), Imm8(31)); + code->SETcc(CC_C, R(carry)); + code->MOV(32, R(result), Imm32(0)); + } else { + code->XOR(32, R(result), R(result)); + code->XOR(32, R(carry), R(carry)); + } + } else { + X64Reg shift = reg_alloc.UseRegister(shift_arg.GetInst(), {HostLoc::RCX}); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg carry = reg_alloc.UseDefRegister(inst->GetArg(2), carry_inst); - code->CMP(8, R(shift), Imm8(32)); - auto Rs_gt32 = code->J_CC(CC_A); - auto Rs_eq32 = code->J_CC(CC_E); - // if (Rs & 0xFF == 0) goto end; - code->TEST(8, R(shift), R(shift)); - auto Rs_zero = code->J_CC(CC_Z); - // if (Rs & 0xFF < 32) { - code->SHR(32, R(result), R(shift)); - code->SETcc(CC_C, R(carry)); - auto jmp_to_end_1 = code->J(); - // } else if (Rs & 0xFF > 32) { - code->SetJumpTarget(Rs_gt32); - code->MOV(32, R(result), Imm32(0)); - code->MOV(8, R(carry), Imm8(0)); - auto jmp_to_end_2 = code->J(); - // } else if (Rs & 0xFF == 32) { - code->SetJumpTarget(Rs_eq32); - code->BT(32, R(result), Imm8(31)); - code->SETcc(CC_C, R(carry)); - code->MOV(32, R(result), Imm32(0)); - // } - code->SetJumpTarget(jmp_to_end_1); - code->SetJumpTarget(jmp_to_end_2); - code->SetJumpTarget(Rs_zero); + // TODO: Optimize this. + + code->CMP(8, R(shift), Imm8(32)); + auto Rs_gt32 = code->J_CC(CC_A); + auto Rs_eq32 = code->J_CC(CC_E); + // if (Rs & 0xFF == 0) goto end; + code->TEST(8, R(shift), R(shift)); + auto Rs_zero = code->J_CC(CC_Z); + // if (Rs & 0xFF < 32) { + code->SHR(32, R(result), R(shift)); + code->SETcc(CC_C, R(carry)); + auto jmp_to_end_1 = code->J(); + // } else if (Rs & 0xFF > 32) { + code->SetJumpTarget(Rs_gt32); + code->XOR(32, R(result), R(result)); + code->XOR(32, R(carry), R(carry)); + auto jmp_to_end_2 = code->J(); + // } else if (Rs & 0xFF == 32) { + code->SetJumpTarget(Rs_eq32); + code->BT(32, R(result), Imm8(31)); + code->SETcc(CC_C, R(carry)); + code->MOV(32, R(result), Imm32(0)); + // } + code->SetJumpTarget(jmp_to_end_1); + code->SetJumpTarget(jmp_to_end_2); + code->SetJumpTarget(Rs_zero); + } } } -void EmitX64::EmitArithmeticShiftRight(IR::Value* value_) { - auto value = reinterpret_cast(value_); - auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp); +void EmitX64::EmitArithmeticShiftRight(IR::Block& block, IR::Inst* inst) { + auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp); if (!carry_inst) { - X64Reg shift = reg_alloc.UseScratchRegister(value->GetArg(1).get(), {HostLoc::RCX}); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg const31 = reg_alloc.ScratchRegister(); - reg_alloc.DecrementRemainingUses(value->GetArg(2).get()); + if (!inst->GetArg(2).IsImmediate()) { + // TODO: Remove redundant argument. + reg_alloc.DecrementRemainingUses(inst->GetArg(2).GetInst()); + } - // The 32-bit x64 SAR instruction masks the shift count by 0x1F before performing the shift. - // ARM differs from the behaviour: It does not mask the count. + auto shift_arg = inst->GetArg(1); - // We note that all shift values above 31 have the same behaviour as 31 does, so we saturate `shift` to 31. - code->MOV(32, R(const31), Imm32(31)); - code->MOVZX(32, 8, shift, R(shift)); - code->CMP(32, R(shift), Imm32(31)); - code->CMOVcc(32, shift, R(const31), CC_G); - code->SAR(32, R(result), R(shift)); + if (shift_arg.IsImmediate()) { + u32 shift = shift_arg.GetU8(); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + + code->SAR(32, R(result), Imm32(shift < 31 ? shift : 31)); + } else { + X64Reg shift = reg_alloc.UseScratchRegister(shift_arg.GetInst(), {HostLoc::RCX}); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg const31 = reg_alloc.ScratchRegister(); + + // The 32-bit x64 SAR instruction masks the shift count by 0x1F before performing the shift. + // ARM differs from the behaviour: It does not mask the count. + + // We note that all shift values above 31 have the same behaviour as 31 does, so we saturate `shift` to 31. + code->MOV(32, R(const31), Imm32(31)); + code->MOVZX(32, 8, shift, R(shift)); + code->CMP(32, R(shift), Imm32(31)); + code->CMOVcc(32, shift, R(const31), CC_G); + code->SAR(32, R(result), R(shift)); + } } else { - inhibit_emission.insert(carry_inst); + EraseInstruction(block, carry_inst); + reg_alloc.DecrementRemainingUses(inst); - X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX}); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst); + auto shift_arg = inst->GetArg(1); - reg_alloc.DecrementRemainingUses(value); + if (shift_arg.IsImmediate()) { + u8 shift = shift_arg.GetU8(); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg carry = reg_alloc.UseDefRegister(inst->GetArg(2), carry_inst); - // TODO: Optimize this. + if (shift == 0) { + // There is nothing more to do. + } else if (shift <= 31) { + code->SAR(32, R(result), Imm8(shift)); + code->SETcc(CC_C, R(carry)); + } else { + code->SAR(32, R(result), Imm8(31)); + code->BT(32, R(result), Imm8(31)); + code->SETcc(CC_C, R(carry)); + } + } else { + X64Reg shift = reg_alloc.UseRegister(shift_arg.GetInst(), {HostLoc::RCX}); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg carry = reg_alloc.UseDefRegister(inst->GetArg(2), carry_inst); - code->CMP(8, R(shift), Imm8(31)); - auto Rs_gt31 = code->J_CC(CC_A); - // if (Rs & 0xFF == 0) goto end; - code->TEST(8, R(shift), R(shift)); - auto Rs_zero = code->J_CC(CC_Z); - // if (Rs & 0xFF <= 31) { - code->SAR(32, R(result), R(CL)); - code->SETcc(CC_C, R(carry)); - auto jmp_to_end = code->J(); - // } else if (Rs & 0xFF > 31) { - code->SetJumpTarget(Rs_gt31); - code->SAR(32, R(result), Imm8(31)); // Verified. - code->BT(32, R(result), Imm8(31)); - code->SETcc(CC_C, R(carry)); - // } - code->SetJumpTarget(jmp_to_end); - code->SetJumpTarget(Rs_zero); + // TODO: Optimize this. + + code->CMP(8, R(shift), Imm8(31)); + auto Rs_gt31 = code->J_CC(CC_A); + // if (Rs & 0xFF == 0) goto end; + code->TEST(8, R(shift), R(shift)); + auto Rs_zero = code->J_CC(CC_Z); + // if (Rs & 0xFF <= 31) { + code->SAR(32, R(result), R(shift)); + code->SETcc(CC_C, R(carry)); + auto jmp_to_end = code->J(); + // } else if (Rs & 0xFF > 31) { + code->SetJumpTarget(Rs_gt31); + code->SAR(32, R(result), Imm8(31)); // Verified. + code->BT(32, R(result), Imm8(31)); + code->SETcc(CC_C, R(carry)); + // } + code->SetJumpTarget(jmp_to_end); + code->SetJumpTarget(Rs_zero); + } } } -void EmitX64::EmitRotateRight(IR::Value* value_) { - auto value = reinterpret_cast(value_); - auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp); +void EmitX64::EmitRotateRight(IR::Block& block, IR::Inst* inst) { + auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp); if (!carry_inst) { - X64Reg shift = reg_alloc.UseRegister(value->GetArg(1).get(), {HostLoc::RCX}); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - reg_alloc.DecrementRemainingUses(value->GetArg(2).get()); + if (!inst->GetArg(2).IsImmediate()) { + // TODO: Remove redundant argument. + reg_alloc.DecrementRemainingUses(inst->GetArg(2).GetInst()); + } - // x64 ROR instruction does (shift & 0x1F) for us. - code->ROR(32, R(result), R(shift)); + auto shift_arg = inst->GetArg(1); + + if (shift_arg.IsImmediate()) { + u8 shift = shift_arg.GetU8(); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + + code->ROR(32, R(result), Imm8(shift & 0x1F)); + } else { + X64Reg shift = reg_alloc.UseRegister(shift_arg.GetInst(), {HostLoc::RCX}); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + + // x64 ROR instruction does (shift & 0x1F) for us. + code->ROR(32, R(result), R(shift)); + } } else { - inhibit_emission.insert(carry_inst); + EraseInstruction(block, carry_inst); + reg_alloc.DecrementRemainingUses(inst); - X64Reg shift = reg_alloc.UseScratchRegister(value->GetArg(1).get(), {HostLoc::RCX}); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg carry = reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst); + auto shift_arg = inst->GetArg(1); - reg_alloc.DecrementRemainingUses(value); + if (shift_arg.IsImmediate()) { + u8 shift = shift_arg.GetU8(); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg carry = reg_alloc.UseDefRegister(inst->GetArg(2), carry_inst); - // TODO: Optimize + if (shift == 0) { + // There is nothing more to do. + } else if ((shift & 0x1F) == 0) { + code->BT(32, R(result), Imm8(31)); + code->SETcc(CC_C, R(carry)); + } else { + code->ROR(32, R(result), Imm8(shift)); + code->SETcc(CC_C, R(carry)); + } + } else { + X64Reg shift = reg_alloc.UseScratchRegister(shift_arg.GetInst(), {HostLoc::RCX}); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); + X64Reg carry = reg_alloc.UseDefRegister(inst->GetArg(2), carry_inst); - // if (Rs & 0xFF == 0) goto end; - code->TEST(8, R(shift), R(shift)); - auto Rs_zero = code->J_CC(CC_Z); + // TODO: Optimize - code->AND(32, R(shift), Imm8(0x1F)); - auto zero_1F = code->J_CC(CC_Z); - // if (Rs & 0x1F != 0) { - code->ROR(32, R(result), R(shift)); - code->SETcc(CC_C, R(carry)); - auto jmp_to_end = code->J(); - // } else { - code->SetJumpTarget(zero_1F); - code->BT(32, R(result), Imm8(31)); - code->SETcc(CC_C, R(carry)); - // } - code->SetJumpTarget(jmp_to_end); - code->SetJumpTarget(Rs_zero); + // if (Rs & 0xFF == 0) goto end; + code->TEST(8, R(shift), R(shift)); + auto Rs_zero = code->J_CC(CC_Z); + + code->AND(32, R(shift), Imm8(0x1F)); + auto zero_1F = code->J_CC(CC_Z); + // if (Rs & 0x1F != 0) { + code->ROR(32, R(result), R(shift)); + code->SETcc(CC_C, R(carry)); + auto jmp_to_end = code->J(); + // } else { + code->SetJumpTarget(zero_1F); + code->BT(32, R(result), Imm8(31)); + code->SETcc(CC_C, R(carry)); + // } + code->SetJumpTarget(jmp_to_end); + code->SetJumpTarget(Rs_zero); + } } } -void EmitX64::EmitAddWithCarry(IR::Value* value_) { - auto value = reinterpret_cast(value_); - auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp); - auto overflow_inst = FindUseWithOpcode(value, IR::Opcode::GetOverflowFromOp); +static X64Reg DoCarry(RegAlloc& reg_alloc, const IR::Value& carry_in, IR::Inst* carry_out) { + if (carry_in.IsImmediate()) { + return carry_out ? reg_alloc.DefRegister(carry_out) : INVALID_REG; + } else { + IR::Inst* in = carry_in.GetInst(); + return carry_out ? reg_alloc.UseDefRegister(in, carry_out) : reg_alloc.UseRegister(in); + } +} - X64Reg addend = reg_alloc.UseRegister(value->GetArg(1).get()); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg carry = carry_inst - ? reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst) - : reg_alloc.UseRegister(value->GetArg(2).get()); - X64Reg overflow = overflow_inst - ? reg_alloc.DefRegister(overflow_inst) - : X64Reg::INVALID_REG; +void EmitX64::EmitAddWithCarry(IR::Block& block, IR::Inst* inst) { + auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp); + auto overflow_inst = FindUseWithOpcode(inst, IR::Opcode::GetOverflowFromOp); + + IR::Value a = inst->GetArg(0); + IR::Value b = inst->GetArg(1); + IR::Value carry_in = inst->GetArg(2); + + X64Reg result = reg_alloc.UseDefRegister(a, inst); + X64Reg carry = DoCarry(reg_alloc, carry_in, carry_inst); + X64Reg overflow = overflow_inst ? reg_alloc.DefRegister(overflow_inst) : INVALID_REG; // TODO: Consider using LEA. - code->BT(32, R(carry), Imm8(0)); // Sets x64 CF appropriately. - code->ADC(32, R(result), R(addend)); + OpArg op_arg = b.IsImmediate() + ? Imm32(b.GetU32()) + : R(reg_alloc.UseRegister(b.GetInst())); + + if (carry_in.IsImmediate()) { + if (carry_in.GetU1()) { + code->STC(); + code->ADC(32, R(result), op_arg); + } else { + code->ADD(32, R(result), op_arg); + } + } else { + code->BT(32, R(carry), Imm8(0)); + code->ADC(32, R(result), op_arg); + } if (carry_inst) { - inhibit_emission.insert(carry_inst); - reg_alloc.DecrementRemainingUses(value); + EraseInstruction(block, carry_inst); + reg_alloc.DecrementRemainingUses(inst); code->SETcc(Gen::CC_C, R(carry)); } if (overflow_inst) { - inhibit_emission.insert(overflow_inst); - reg_alloc.DecrementRemainingUses(value); + EraseInstruction(block, overflow_inst); + reg_alloc.DecrementRemainingUses(inst); code->SETcc(Gen::CC_O, R(overflow)); } } -void EmitX64::EmitSubWithCarry(IR::Value* value_) { - auto value = reinterpret_cast(value_); - auto carry_inst = FindUseWithOpcode(value, IR::Opcode::GetCarryFromOp); - auto overflow_inst = FindUseWithOpcode(value, IR::Opcode::GetOverflowFromOp); +void EmitX64::EmitSubWithCarry(IR::Block& block, IR::Inst* inst) { + auto carry_inst = FindUseWithOpcode(inst, IR::Opcode::GetCarryFromOp); + auto overflow_inst = FindUseWithOpcode(inst, IR::Opcode::GetOverflowFromOp); - X64Reg addend = reg_alloc.UseRegister(value->GetArg(1).get()); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); - X64Reg carry = carry_inst - ? reg_alloc.UseDefRegister(value->GetArg(2).get(), carry_inst) - : reg_alloc.UseRegister(value->GetArg(2).get()); - X64Reg overflow = overflow_inst - ? reg_alloc.DefRegister(overflow_inst) - : X64Reg::INVALID_REG; + IR::Value a = inst->GetArg(0); + IR::Value b = inst->GetArg(1); + IR::Value carry_in = inst->GetArg(2); + + X64Reg result = reg_alloc.UseDefRegister(a, inst); + X64Reg carry = DoCarry(reg_alloc, carry_in, carry_inst); + X64Reg overflow = overflow_inst ? reg_alloc.DefRegister(overflow_inst) : INVALID_REG; // TODO: Consider using LEA. - // TODO: Optimize case when result isn't used but flags are (use a CMP instruction instead). + // TODO: Optimize CMP case. // Note that x64 CF is inverse of what the ARM carry flag is here. - code->BT(32, R(carry), Imm8(0)); - code->CMC(); - code->SBB(32, R(result), R(addend)); + OpArg op_arg = b.IsImmediate() + ? Imm32(b.GetU32()) + : R(reg_alloc.UseRegister(b.GetInst())); + + if (carry_in.IsImmediate()) { + if (carry_in.GetU1()) { + code->SUB(32, R(result), op_arg); + } else { + code->STC(); + code->SBB(32, R(result), op_arg); + } + } else { + code->BT(32, R(carry), Imm8(0)); + code->CMC(); + code->SBB(32, R(result), op_arg); + } if (carry_inst) { - inhibit_emission.insert(carry_inst); - reg_alloc.DecrementRemainingUses(value); + EraseInstruction(block, carry_inst); + reg_alloc.DecrementRemainingUses(inst); code->SETcc(Gen::CC_NC, R(carry)); } if (overflow_inst) { - inhibit_emission.insert(overflow_inst); - reg_alloc.DecrementRemainingUses(value); + EraseInstruction(block, overflow_inst); + reg_alloc.DecrementRemainingUses(inst); code->SETcc(Gen::CC_O, R(overflow)); } } -void EmitX64::EmitAnd(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitAnd(IR::Block&, IR::Inst* inst) { + IR::Value a = inst->GetArg(0); + IR::Value b = inst->GetArg(1); - X64Reg andend = reg_alloc.UseRegister(value->GetArg(1).get()); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + X64Reg result = reg_alloc.UseDefRegister(a, inst); + OpArg op_arg = b.IsImmediate() + ? Imm32(b.GetU32()) + : R(reg_alloc.UseRegister(b.GetInst())); - code->AND(32, R(result), R(andend)); + code->AND(32, R(result), op_arg); } -void EmitX64::EmitEor(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitEor(IR::Block&, IR::Inst* inst) { + IR::Value a = inst->GetArg(0); + IR::Value b = inst->GetArg(1); - X64Reg eorend = reg_alloc.UseRegister(value->GetArg(1).get()); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + X64Reg result = reg_alloc.UseDefRegister(a, inst); + OpArg op_arg = b.IsImmediate() + ? Imm32(b.GetU32()) + : R(reg_alloc.UseRegister(b.GetInst())); - code->XOR(32, R(result), R(eorend)); + code->XOR(32, R(result), op_arg); } -void EmitX64::EmitOr(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitOr(IR::Block&, IR::Inst* inst) { + IR::Value a = inst->GetArg(0); + IR::Value b = inst->GetArg(1); - X64Reg orend = reg_alloc.UseRegister(value->GetArg(1).get()); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + X64Reg result = reg_alloc.UseDefRegister(a, inst); + OpArg op_arg = b.IsImmediate() + ? Imm32(b.GetU32()) + : R(reg_alloc.UseRegister(b.GetInst())); - code->OR(32, R(result), R(orend)); + code->OR(32, R(result), op_arg); } -void EmitX64::EmitNot(IR::Value* value_) { - auto value = reinterpret_cast(value_); +void EmitX64::EmitNot(IR::Block&, IR::Inst* inst) { + IR::Value a = inst->GetArg(0); - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + if (a.IsImmediate()) { + X64Reg result = reg_alloc.DefRegister(inst); - code->NOT(32, R(result)); + code->MOV(32, R(result), Imm32(~a.GetU32())); + } else { + X64Reg result = reg_alloc.UseDefRegister(a.GetInst(), inst); + + code->NOT(32, R(result)); + } } -void EmitX64::EmitSignExtendHalfToWord(IR::Value* value_) { - auto value = reinterpret_cast(value_); - +void EmitX64::EmitSignExtendHalfToWord(IR::Block&, IR::Inst* inst) { // TODO: Remove unnecessary mov that may occur here - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); code->MOVSX(32, 16, result, R(result)); } -void EmitX64::EmitSignExtendByteToWord(IR::Value* value_) { - auto value = reinterpret_cast(value_); - +void EmitX64::EmitSignExtendByteToWord(IR::Block&, IR::Inst* inst) { // TODO: Remove unnecessary mov that may occur here - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); code->MOVSX(32, 8, result, R(result)); } -void EmitX64::EmitZeroExtendHalfToWord(IR::Value* value_) { - auto value = reinterpret_cast(value_); - +void EmitX64::EmitZeroExtendHalfToWord(IR::Block&, IR::Inst* inst) { // TODO: Remove unnecessary mov that may occur here - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); code->MOVZX(32, 16, result, R(result)); } -void EmitX64::EmitZeroExtendByteToWord(IR::Value* value_) { - auto value = reinterpret_cast(value_); - +void EmitX64::EmitZeroExtendByteToWord(IR::Block&, IR::Inst* inst) { // TODO: Remove unnecessary mov that may occur here - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); code->MOVZX(32, 8, result, R(result)); } -void EmitX64::EmitByteReverseWord(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); +void EmitX64::EmitByteReverseWord(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); code->BSWAP(32, result); } -void EmitX64::EmitByteReverseHalf(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); +void EmitX64::EmitByteReverseHalf(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); code->ROL(16, R(result), Imm8(8)); } -void EmitX64::EmitByteReverseDual(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - X64Reg result = reg_alloc.UseDefRegister(value->GetArg(0).get(), value); +void EmitX64::EmitByteReverseDual(IR::Block&, IR::Inst* inst) { + X64Reg result = reg_alloc.UseDefRegister(inst->GetArg(0), inst); code->BSWAP(64, result); } -void EmitX64::EmitReadMemory8(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - reg_alloc.HostCall(value, value->GetArg(0).get()); +void EmitX64::EmitReadMemory8(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(inst, inst->GetArg(0)); code->ABI_CallFunction(reinterpret_cast(cb.MemoryRead8)); } -void EmitX64::EmitReadMemory16(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - reg_alloc.HostCall(value, value->GetArg(0).get()); +void EmitX64::EmitReadMemory16(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(inst, inst->GetArg(0)); code->ABI_CallFunction(reinterpret_cast(cb.MemoryRead16)); } -void EmitX64::EmitReadMemory32(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - reg_alloc.HostCall(value, value->GetArg(0).get()); +void EmitX64::EmitReadMemory32(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(inst, inst->GetArg(0)); code->ABI_CallFunction(reinterpret_cast(cb.MemoryRead32)); } -void EmitX64::EmitReadMemory64(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - reg_alloc.HostCall(value, value->GetArg(0).get()); +void EmitX64::EmitReadMemory64(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(inst, inst->GetArg(0)); code->ABI_CallFunction(reinterpret_cast(cb.MemoryRead64)); } -void EmitX64::EmitWriteMemory8(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get()); +void EmitX64::EmitWriteMemory8(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(nullptr, inst->GetArg(0), inst->GetArg(1)); code->ABI_CallFunction(reinterpret_cast(cb.MemoryWrite8)); } -void EmitX64::EmitWriteMemory16(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get()); +void EmitX64::EmitWriteMemory16(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(nullptr, inst->GetArg(0), inst->GetArg(1)); code->ABI_CallFunction(reinterpret_cast(cb.MemoryWrite16)); } -void EmitX64::EmitWriteMemory32(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get()); +void EmitX64::EmitWriteMemory32(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(nullptr, inst->GetArg(0), inst->GetArg(1)); code->ABI_CallFunction(reinterpret_cast(cb.MemoryWrite32)); } -void EmitX64::EmitWriteMemory64(IR::Value* value_) { - auto value = reinterpret_cast(value_); - - reg_alloc.HostCall(nullptr, value->GetArg(0).get(), value->GetArg(1).get()); +void EmitX64::EmitWriteMemory64(IR::Block&, IR::Inst* inst) { + reg_alloc.HostCall(nullptr, inst->GetArg(0), inst->GetArg(1)); code->ABI_CallFunction(reinterpret_cast(cb.MemoryWrite64)); } diff --git a/src/backend_x64/emit_x64.h b/src/backend_x64/emit_x64.h index b0d2d5f9..7eeb96dd 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -23,7 +23,7 @@ public: EmitX64(Gen::XEmitter* code, Routines* routines, UserCallbacks cb, Jit* 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) { auto iter = basic_blocks.find(descriptor); @@ -34,53 +34,49 @@ public: private: // Microinstruction emitters - void EmitImmU1(IR::Value* value); - void EmitImmU8(IR::Value* value); - void EmitImmU32(IR::Value* value); - void EmitImmRegRef(IR::Value* value); - void EmitGetRegister(IR::Value* value); - void EmitSetRegister(IR::Value* value); - void EmitGetNFlag(IR::Value* value); - void EmitSetNFlag(IR::Value* value); - void EmitGetZFlag(IR::Value* value); - void EmitSetZFlag(IR::Value* value); - void EmitGetCFlag(IR::Value* value); - void EmitSetCFlag(IR::Value* value); - void EmitGetVFlag(IR::Value* value); - void EmitSetVFlag(IR::Value* value); - void EmitBXWritePC(IR::Value* value); - void EmitCallSupervisor(IR::Value* value); - void EmitGetCarryFromOp(IR::Value* value); - void EmitGetOverflowFromOp(IR::Value* value); - void EmitLeastSignificantHalf(IR::Value* value); - void EmitLeastSignificantByte(IR::Value* value); - void EmitMostSignificantBit(IR::Value* value); - void EmitIsZero(IR::Value* value); - void EmitLogicalShiftLeft(IR::Value* value); - void EmitLogicalShiftRight(IR::Value* value); - void EmitArithmeticShiftRight(IR::Value* value); - void EmitRotateRight(IR::Value* value); - void EmitAddWithCarry(IR::Value* value); - void EmitSubWithCarry(IR::Value* value); - void EmitAnd(IR::Value* value); - void EmitEor(IR::Value* value); - void EmitOr(IR::Value* value); - void EmitNot(IR::Value* value); - void EmitSignExtendHalfToWord(IR::Value* value); - void EmitSignExtendByteToWord(IR::Value* value); - void EmitZeroExtendHalfToWord(IR::Value* value); - void EmitZeroExtendByteToWord(IR::Value* value); - void EmitByteReverseWord(IR::Value* value); - void EmitByteReverseHalf(IR::Value* value); - void EmitByteReverseDual(IR::Value* value); - void EmitReadMemory8(IR::Value* value); - void EmitReadMemory16(IR::Value* value); - void EmitReadMemory32(IR::Value* value); - void EmitReadMemory64(IR::Value* value); - void EmitWriteMemory8(IR::Value* value); - void EmitWriteMemory16(IR::Value* value); - void EmitWriteMemory32(IR::Value* value); - void EmitWriteMemory64(IR::Value* value); + void EmitGetRegister(IR::Block& block, IR::Inst* inst); + void EmitSetRegister(IR::Block& block, IR::Inst* inst); + void EmitGetNFlag(IR::Block& block, IR::Inst* inst); + void EmitSetNFlag(IR::Block& block, IR::Inst* inst); + void EmitGetZFlag(IR::Block& block, IR::Inst* inst); + void EmitSetZFlag(IR::Block& block, IR::Inst* inst); + void EmitGetCFlag(IR::Block& block, IR::Inst* inst); + void EmitSetCFlag(IR::Block& block, IR::Inst* inst); + void EmitGetVFlag(IR::Block& block, IR::Inst* inst); + void EmitSetVFlag(IR::Block& block, IR::Inst* inst); + void EmitBXWritePC(IR::Block& block, IR::Inst* inst); + void EmitCallSupervisor(IR::Block& block, IR::Inst* inst); + void EmitGetCarryFromOp(IR::Block& block, IR::Inst* inst); + void EmitGetOverflowFromOp(IR::Block& block, IR::Inst* inst); + void EmitLeastSignificantHalf(IR::Block& block, IR::Inst* inst); + void EmitLeastSignificantByte(IR::Block& block, IR::Inst* inst); + void EmitMostSignificantBit(IR::Block& block, IR::Inst* inst); + void EmitIsZero(IR::Block& block, IR::Inst* inst); + void EmitLogicalShiftLeft(IR::Block& block, IR::Inst* inst); + void EmitLogicalShiftRight(IR::Block& block, IR::Inst* inst); + void EmitArithmeticShiftRight(IR::Block& block, IR::Inst* inst); + void EmitRotateRight(IR::Block& block, IR::Inst* inst); + void EmitAddWithCarry(IR::Block& block, IR::Inst* inst); + void EmitSubWithCarry(IR::Block& block, IR::Inst* inst); + void EmitAnd(IR::Block& block, IR::Inst* inst); + void EmitEor(IR::Block& block, IR::Inst* inst); + void EmitOr(IR::Block& block, IR::Inst* inst); + void EmitNot(IR::Block& block, IR::Inst* inst); + void EmitSignExtendHalfToWord(IR::Block& block, IR::Inst* inst); + void EmitSignExtendByteToWord(IR::Block& block, IR::Inst* inst); + void EmitZeroExtendHalfToWord(IR::Block& block, IR::Inst* inst); + void EmitZeroExtendByteToWord(IR::Block& block, IR::Inst* inst); + void EmitByteReverseWord(IR::Block& block, IR::Inst* inst); + void EmitByteReverseHalf(IR::Block& block, IR::Inst* inst); + void EmitByteReverseDual(IR::Block& block, IR::Inst* inst); + void EmitReadMemory8(IR::Block& block, IR::Inst* inst); + void EmitReadMemory16(IR::Block& block, IR::Inst* inst); + void EmitReadMemory32(IR::Block& block, IR::Inst* inst); + void EmitReadMemory64(IR::Block& block, IR::Inst* inst); + void EmitWriteMemory8(IR::Block& block, IR::Inst* inst); + void EmitWriteMemory16(IR::Block& block, IR::Inst* inst); + void EmitWriteMemory32(IR::Block& block, IR::Inst* inst); + void EmitWriteMemory64(IR::Block& block, IR::Inst* inst); // Helpers void EmitAddCycles(size_t cycles); diff --git a/src/backend_x64/reg_alloc.cpp b/src/backend_x64/reg_alloc.cpp index a5659099..971d237f 100644 --- a/src/backend_x64/reg_alloc.cpp +++ b/src/backend_x64/reg_alloc.cpp @@ -14,36 +14,22 @@ namespace Dynarmic { namespace BackendX64 { -// TODO: Just turn this into a function that indexes a std::array. -const static std::map hostloc_to_x64 = { - { HostLoc::RAX, Gen::RAX }, - { HostLoc::RBX, Gen::RBX }, - { 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::X64Reg HostLocToX64(HostLoc loc) { + DEBUG_ASSERT(HostLocIsRegister(loc)); + // HostLoc is ordered such that the numbers line up. + return static_cast(loc); +} static Gen::OpArg SpillToOpArg(HostLoc loc) { - ASSERT(HostLocIsSpill(loc)); + DEBUG_ASSERT(HostLocIsSpill(loc)); size_t i = static_cast(loc) - static_cast(HostLoc::FirstSpill); return Gen::MDisp(Gen::R15, static_cast(offsetof(JitState, Spill) + i * sizeof(u32))); } -Gen::X64Reg RegAlloc::DefRegister(IR::Value* def_value, std::initializer_list desired_locations) { - 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"); - ASSERT_MSG(ValueLocations(def_value).empty(), "def_value has already been defined"); +Gen::X64Reg RegAlloc::DefRegister(IR::Inst* def_inst, std::initializer_list 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"); HostLoc location = SelectARegister(desired_locations); @@ -52,43 +38,54 @@ Gen::X64Reg RegAlloc::DefRegister(IR::Value* def_value, std::initializer_listNumUses(); + hostloc_state[static_cast(location)] = HostLocState::Def; + hostloc_to_inst[static_cast(location)] = def_inst; - return hostloc_to_x64.at(location); + return HostLocToX64(location); } -Gen::X64Reg RegAlloc::UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list desired_locations) { - 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"); - 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"); +Gen::X64Reg RegAlloc::UseDefRegister(IR::Value use_value, IR::Inst* def_inst, std::initializer_list desired_locations) { + if (!use_value.IsImmediate()) { + return UseDefRegister(use_value.GetInst(), def_inst, desired_locations); + } - // TODO: Optimize the case when this is the last use_value use. - Gen::X64Reg use_reg = UseRegister(use_value); - Gen::X64Reg def_reg = DefRegister(def_value, desired_locations); + return LoadImmediateIntoRegister(use_value, DefRegister(def_inst, desired_locations)); +} + +Gen::X64Reg RegAlloc::UseDefRegister(IR::Inst* use_inst, IR::Inst* def_inst, std::initializer_list 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)); return def_reg; } -Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_list desired_locations) { - ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister)); - 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"); - ASSERT_MSG(remaining_uses[use_value] != 0, "use_value ran out of uses. (Use-d an IR::Value* too many times)"); +Gen::X64Reg RegAlloc::UseRegister(IR::Value use_value, std::initializer_list desired_locations) { + if (!use_value.IsImmediate()) { + return UseRegister(use_value.GetInst(), desired_locations); + } - 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 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); if (iter != desired_locations.end()) { - ASSERT(hostloc_state[current_location] == HostLocState::Idle || hostloc_state[current_location] == HostLocState::Use); + ASSERT(hostloc_state[static_cast(current_location)] == HostLocState::Idle || hostloc_state[static_cast(current_location)] == HostLocState::Use); // Update state - hostloc_state[current_location] = HostLocState::Use; - remaining_uses[use_value]--; + hostloc_state[static_cast(current_location)] = HostLocState::Use; + DecrementRemainingUses(use_inst); - return hostloc_to_x64.at(current_location); + return HostLocToX64(current_location); } HostLoc new_location = SelectARegister(desired_locations); @@ -98,33 +95,40 @@ Gen::X64Reg RegAlloc::UseRegister(IR::Value* use_value, std::initializer_listMOV(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; - std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]); - remaining_uses[use_value]--; + hostloc_state[static_cast(new_location)] = HostLocState::Use; + std::swap(hostloc_to_inst[static_cast(new_location)], hostloc_to_inst[static_cast(current_location)]); + DecrementRemainingUses(use_inst); } else if (HostLocIsRegister(current_location)) { - ASSERT(hostloc_state[current_location] == HostLocState::Idle); + ASSERT(hostloc_state[static_cast(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; - std::swap(hostloc_to_value[new_location], hostloc_to_value[current_location]); - remaining_uses[use_value]--; + hostloc_state[static_cast(new_location)] = HostLocState::Use; + std::swap(hostloc_to_inst[static_cast(new_location)], hostloc_to_inst[static_cast(current_location)]); + DecrementRemainingUses(use_inst); } else { 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 desired_locations) { - ASSERT(std::all_of(desired_locations.begin(), desired_locations.end(), HostLocIsRegister)); - 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"); - ASSERT_MSG(remaining_uses[use_value] != 0, "use_value ran out of uses. (Use-d an IR::Value* too many times)"); +Gen::X64Reg RegAlloc::UseScratchRegister(IR::Value use_value, std::initializer_list desired_locations) { + if (!use_value.IsImmediate()) { + return UseScratchRegister(use_value.GetInst(), desired_locations); + } - 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 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); if (HostLocIsSpill(current_location)) { @@ -132,34 +136,34 @@ Gen::X64Reg RegAlloc::UseScratchRegister(IR::Value* use_value, std::initializer_ 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; - remaining_uses[use_value]--; + hostloc_state[static_cast(new_location)] = HostLocState::Scratch; + DecrementRemainingUses(use_inst); } else if (HostLocIsRegister(current_location)) { - ASSERT(hostloc_state[current_location] == HostLocState::Idle); + ASSERT(hostloc_state[static_cast(current_location)] == HostLocState::Idle); if (IsRegisterOccupied(new_location)) { SpillRegister(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 { - 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; - remaining_uses[use_value]--; + hostloc_state[static_cast(new_location)] = HostLocState::Scratch; + DecrementRemainingUses(use_inst); } else { ASSERT_MSG(0, "Invalid current_location"); } - return hostloc_to_x64.at(new_location); + return HostLocToX64(new_location); } Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list 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); @@ -168,12 +172,32 @@ Gen::X64Reg RegAlloc::ScratchRegister(std::initializer_list desired_loc } // Update state - hostloc_state[location] = HostLocState::Scratch; + hostloc_state[static_cast(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; #ifdef _WIN32 constexpr std::array 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 OtherCallerSave = { HostLoc::R8, HostLoc::R9, HostLoc::R10, HostLoc::R11 }; #endif - const std::array args = {arg0_use, arg1_use, arg2_use, arg3_use}; + const std::array args = {&arg0_use, &arg1_use, &arg2_use, &arg3_use}; // 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++) { - if (args[i]) { - UseScratchRegister(args[i], {AbiArgs[i]}); + if (!args[i]->IsEmpty()) { + UseScratchRegister(*args[i], {AbiArgs[i]}); } else { ScratchRegister({AbiArgs[i]}); } @@ -231,36 +255,36 @@ HostLoc RegAlloc::SelectARegister(std::initializer_list desired_locatio return candidates.front(); } -std::vector RegAlloc::ValueLocations(IR::Value* value) const { +std::vector RegAlloc::ValueLocations(IR::Inst* value) const { std::vector locations; - for (const auto& iter : hostloc_to_value) - if (iter.second == value) - locations.emplace_back(iter.first); + for (size_t i = 0; i < HostLocCount; i++) + if (hostloc_to_inst[i] == value) + locations.emplace_back(static_cast(i)); return locations; } 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(loc)) != nullptr; } 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(loc)) != HostLocState::Idle; } void RegAlloc::SpillRegister(HostLoc loc) { 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(loc)] == HostLocState::Idle, "Allocated registers cannot be spilled"); 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"); 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_value[loc] = nullptr; + hostloc_to_inst[static_cast(new_loc)] = hostloc_to_inst[static_cast(loc)]; + hostloc_to_inst[static_cast(loc)] = nullptr; } HostLoc RegAlloc::FindFreeSpill() const { @@ -272,27 +296,25 @@ HostLoc RegAlloc::FindFreeSpill() const { } void RegAlloc::EndOfAllocScope() { - hostloc_state.clear(); + hostloc_state.fill(HostLocState::Idle); - for (auto& iter : hostloc_to_value) - if (iter.second && remaining_uses[iter.second] == 0) - iter.second = nullptr; + for (auto& iter : hostloc_to_inst) + if (iter && iter->use_count == 0) + iter = nullptr; } -void RegAlloc::DecrementRemainingUses(IR::Value* value) { - ASSERT_MSG(remaining_uses.find(value) != remaining_uses.end(), "value does not exist"); - ASSERT_MSG(remaining_uses[value] > 0, "value doesn't have any remaining uses"); - remaining_uses[value]--; +void RegAlloc::DecrementRemainingUses(IR::Inst* value) { + ASSERT_MSG(value->use_count > 0, "value doesn't have any remaining uses"); + value->use_count--; } 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() { - hostloc_to_value.clear(); - hostloc_state.clear(); - remaining_uses.clear(); + hostloc_to_inst.fill(nullptr); + hostloc_state.fill(HostLocState::Idle); } } // namespace BackendX64 diff --git a/src/backend_x64/reg_alloc.h b/src/backend_x64/reg_alloc.h index 8d9bed42..bf4f58eb 100644 --- a/src/backend_x64/reg_alloc.h +++ b/src/backend_x64/reg_alloc.h @@ -17,11 +17,14 @@ namespace Dynarmic { namespace BackendX64 { 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, FirstSpill, }; +constexpr size_t HostLocCount = static_cast(HostLoc::FirstSpill) + SpillCount; + enum class HostLocState { Idle, Def, Use, Scratch }; @@ -66,22 +69,26 @@ public: RegAlloc(Gen::XEmitter* code) : code(code) {} /// Late-def - Gen::X64Reg DefRegister(IR::Value* def_value, std::initializer_list desired_locations = hostloc_any_register); + Gen::X64Reg DefRegister(IR::Inst* def_inst, std::initializer_list desired_locations = hostloc_any_register); /// Early-use, Late-def - Gen::X64Reg UseDefRegister(IR::Value* use_value, IR::Value* def_value, std::initializer_list desired_locations = hostloc_any_register); + Gen::X64Reg UseDefRegister(IR::Value use_value, IR::Inst* def_inst, std::initializer_list desired_locations = hostloc_any_register); + Gen::X64Reg UseDefRegister(IR::Inst* use_inst, IR::Inst* def_inst, std::initializer_list desired_locations = hostloc_any_register); /// Early-use - Gen::X64Reg UseRegister(IR::Value* use_value, std::initializer_list desired_locations = hostloc_any_register); + Gen::X64Reg UseRegister(IR::Value use_value, std::initializer_list desired_locations = hostloc_any_register); + Gen::X64Reg UseRegister(IR::Inst* use_inst, std::initializer_list desired_locations = hostloc_any_register); /// Early-use, Destroyed - Gen::X64Reg UseScratchRegister(IR::Value* use_value, std::initializer_list desired_locations = hostloc_any_register); + Gen::X64Reg UseScratchRegister(IR::Value use_value, std::initializer_list desired_locations = hostloc_any_register); + Gen::X64Reg UseScratchRegister(IR::Inst* use_inst, std::initializer_list desired_locations = hostloc_any_register); /// Early-def, Late-use, single-use Gen::X64Reg ScratchRegister(std::initializer_list 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. - 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 - void DecrementRemainingUses(IR::Value* value); + void DecrementRemainingUses(IR::Inst* value); void EndOfAllocScope(); @@ -91,7 +98,7 @@ public: private: HostLoc SelectARegister(std::initializer_list desired_locations) const; - std::vector ValueLocations(IR::Value* value) const; + std::vector ValueLocations(IR::Inst* value) const; bool IsRegisterOccupied(HostLoc loc) const; bool IsRegisterAllocated(HostLoc loc) const; @@ -100,10 +107,9 @@ private: Gen::XEmitter* code = nullptr; - using mapping_map_t = std::map; - mapping_map_t hostloc_to_value; - std::map hostloc_state; - std::map remaining_uses; + using mapping_map_t = std::array; + mapping_map_t hostloc_to_inst; + std::array hostloc_state; }; } // namespace BackendX64 diff --git a/src/common/assert.h b/src/common/assert.h index 2bf77da6..b9c954b1 100644 --- a/src/common/assert.h +++ b/src/common/assert.h @@ -45,7 +45,7 @@ static void assert_noinline_call(const Fn& fn) { #define DEBUG_ASSERT_MSG(_a_, ...) ASSERT_MSG(_a_, __VA_ARGS__) #else // not debug #define DEBUG_ASSERT(_a_) -#define DEBUG_ASSERT_MSG(_a_, _desc_, ...) +#define DEBUG_ASSERT_MSG(_a_, ...) #endif #define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!") diff --git a/src/frontend/ir/ir.cpp b/src/frontend/ir/ir.cpp index 3bb69132..f3a6d10b 100644 --- a/src/frontend/ir/ir.cpp +++ b/src/frontend/ir/ir.cpp @@ -52,94 +52,69 @@ const char* GetNameOf(Opcode op) { // Value class member definitions -void Value::ReplaceUsesWith(ValuePtr replacement) { - while (!uses.empty()) { - auto use = uses.front(); - use.use_owner.lock()->ReplaceUseOfXWithY(use.value.lock(), replacement); - } -} - -std::vector Value::GetUses() const { - std::vector 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(); })); +Type Value::GetType() const { + return IsImmediate() ? type : inner.inst->GetType(); } // Inst class member definitions -Inst::Inst(Opcode op_) : Value(op_) { - args.resize(GetNumArgsOf(op)); +Value Inst::GetArg(size_t index) const { + DEBUG_ASSERT(index < GetNumArgsOf(op)); + DEBUG_ASSERT(!args[index].IsEmpty()); + + return args[index]; } -void Inst::SetArg(size_t index, ValuePtr value) { - auto this_ = shared_from_this(); +void Inst::SetArg(size_t index, Value value) { + DEBUG_ASSERT(index < GetNumArgsOf(op)); + DEBUG_ASSERT(value.GetType() == GetArgTypeOf(op, index)); - if (auto prev_value = args.at(index).lock()) { - prev_value->RemoveUse(this_); + if (!args[index].IsImmediate()) { + UndoUse(args[index]); + } + if (!value.IsImmediate()) { + Use(value); } - ASSERT(value->GetType() == GetArgTypeOf(op, index)); - 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(); + args[index] = value; } void Inst::Invalidate() { - AssertValid(); - ASSERT(!HasUses()); - - 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_); + for (auto& value : args) { + if (!value.IsImmediate()) { + UndoUse(value); } } +} - 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) { @@ -160,65 +135,48 @@ std::string DumpBlock(const IR::Block& block) { } ret += "\n"; - std::map value_to_index; + std::map inst_to_index; size_t index = 0; - const auto arg_to_string = [&value_to_index](IR::ValuePtr arg) -> std::string { - if (!arg) { + const auto arg_to_string = [&inst_to_index](const IR::Value& arg) -> std::string { + if (arg.IsEmpty()) { return ""; + } else if (!arg.IsImmediate()) { + return Common::StringFromFormat("%%%zu", inst_to_index.at(arg.GetInst())); } - switch (arg->GetOpcode()) { - case Opcode::ImmU1: { - auto inst = reinterpret_cast(arg.get()); - return Common::StringFromFormat("#%s", inst->value ? "1" : "0"); - } - case Opcode::ImmU8: { - auto inst = reinterpret_cast(arg.get()); - return Common::StringFromFormat("#%u", inst->value); - } - case Opcode::ImmU32: { - auto inst = reinterpret_cast(arg.get()); - return Common::StringFromFormat("#%#x", inst->value); - } - case Opcode::ImmRegRef: { - auto inst = reinterpret_cast(arg.get()); - return Arm::RegToString(inst->value); - } - default: { - return Common::StringFromFormat("%%%zu", value_to_index.at(arg.get())); - } + switch (arg.GetType()) { + case Type::U1: + return Common::StringFromFormat("#%s", arg.GetU1() ? "1" : "0"); + case Type::U8: + return Common::StringFromFormat("#%u", arg.GetU8()); + case Type::U32: + return Common::StringFromFormat("#%#x", arg.GetU32()); + case Type::RegRef: + return Arm::RegToString(arg.GetRegRef()); + default: + return ""; } }; - for (const auto& inst_ptr : block.instructions) { - const Opcode op = inst_ptr->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 - } + for (auto inst = block.instructions.begin(); inst != block.instructions.end(); ++inst) { + const Opcode op = inst->GetOpcode(); - ret += GetNameOf(op); - - const size_t arg_count = GetNumArgsOf(op); - const auto inst = reinterpret_cast(inst_ptr.get()); - 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; - } + if (GetTypeOf(op) != Type::Void) { + ret += Common::StringFromFormat("%%%-5zu = ", index); + } else { + ret += " "; // '%00000 = ' -> 1 + 5 + 3 = 9 spaces } + + 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; diff --git a/src/frontend/ir/ir.h b/src/frontend/ir/ir.h index 8e9f8d3f..50d10e2c 100644 --- a/src/frontend/ir/ir.h +++ b/src/frontend/ir/ir.h @@ -10,9 +10,12 @@ #include #include -#include +#include +#include #include +#include +#include "common/assert.h" #include "common/common_types.h" #include "frontend/arm_types.h" #include "frontend/ir/opcodes.h" @@ -47,22 +50,91 @@ const char* GetNameOf(Opcode op); // 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; -using ValuePtr = std::shared_ptr; -using ValueWeakPtr = std::weak_ptr; +struct Value; +class Inst; -class Value : public std::enable_shared_from_this { +struct Value final { public: - virtual ~Value() = default; + Value() : type(Type::Void) {} - bool HasUses() const { return !uses.empty(); } - bool HasOneUse() const { return uses.size() == 1; } - bool HasManyUses() const { return uses.size() > 1; } + explicit Value(Inst* value) : type(Type::Opaque) { + inner.inst = value; + } - /// Replace all uses of this Value with `replacement`. - void ReplaceUsesWith(ValuePtr replacement); + explicit Value(Arm::Reg value) : type(Type::RegRef) { + 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; +class Inst final : public boost::intrusive::list_base_hook { +public: + Inst(Opcode op) : op(op) {} + + bool HasUses() const { return use_count > 0; } /// Get the microop this microinstruction represents. Opcode GetOpcode() const { return op; } @@ -70,99 +142,22 @@ public: Type GetType() const { return GetTypeOf(op); } /// Get the number of arguments this instruction has. size_t NumArgs() const { return GetNumArgsOf(op); } - /// Get the number of uses this instruction has. - size_t NumUses() const { return uses.size(); } - std::vector GetUses() const; + Value GetArg(size_t index) const; + void SetArg(size_t index, Value value); - /// Prepare this Value for removal from the instruction stream. - virtual void Invalidate() {} - /// Assert that this Value is valid. - virtual void AssertValid(); + void Invalidate(); - intptr_t GetTag() const { return tag; } - void SetTag(intptr_t tag_) { tag = tag_; } - -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); + size_t use_count = 0; + Inst* carry_inst = nullptr; + Inst* overflow_inst = nullptr; private: + void Use(Value& value); + void UndoUse(Value& value); + Opcode op; - - struct Use { - /// The instruction which is being used. - ValueWeakPtr value; - /// The instruction which is using `value`. - ValueWeakPtr use_owner; - }; - std::list 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 args; + std::array args; }; namespace Term { @@ -261,7 +256,9 @@ public: boost::optional cond_failed = {}; /// List of instructions in this block. - std::list instructions; + boost::intrusive::list instructions; + /// Memory pool for instruction list + std::unique_ptr> instruction_alloc_pool = std::make_unique>(sizeof(Inst)); /// Terminal instruction of this block. Terminal terminal = Term::Invalid{}; diff --git a/src/frontend/ir/ir_emitter.cpp b/src/frontend/ir/ir_emitter.cpp index 8c8df66d..1b57fd89 100644 --- a/src/frontend/ir/ir_emitter.cpp +++ b/src/frontend/ir/ir_emitter.cpp @@ -24,138 +24,132 @@ u32 IREmitter::AlignPC(size_t alignment) { return static_cast(pc - pc % alignment); } -IR::ValuePtr IREmitter::Imm1(bool value) { - auto imm1 = std::make_shared(value); - AddToBlock(imm1); - return imm1; +IR::Value IREmitter::Imm1(bool imm1) { + return IR::Value(imm1); } -IR::ValuePtr IREmitter::Imm8(u8 i) { - auto imm8 = std::make_shared(i); - AddToBlock(imm8); - return imm8; +IR::Value IREmitter::Imm8(u8 imm8) { + return IR::Value(imm8); } -IR::ValuePtr IREmitter::Imm32(u32 i) { - auto imm32 = std::make_shared(i); - AddToBlock(imm32); - return imm32; +IR::Value IREmitter::Imm32(u32 imm32) { + return IR::Value(imm32); } -IR::ValuePtr IREmitter::GetRegister(Reg reg) { +IR::Value IREmitter::GetRegister(Reg reg) { if (reg == Reg::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); - 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. // The below implementation is for ARMv6k BranchWritePC(value); } -void IREmitter::BranchWritePC(IR::ValuePtr value) { +void IREmitter::BranchWritePC(const IR::Value& value) { if (!current_location.TFlag) { 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 { 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}); } -void IREmitter::LoadWritePC(IR::ValuePtr value) { +void IREmitter::LoadWritePC(const IR::Value& value) { // This behaviour is ARM version-dependent. // The below implementation is for ARMv6k BXWritePC(value); } -void IREmitter::CallSupervisor(IR::ValuePtr value) { +void IREmitter::CallSupervisor(const IR::Value& value) { Inst(IR::Opcode::CallSupervisor, {value}); } -IR::ValuePtr IREmitter::GetCFlag() { +IR::Value IREmitter::GetCFlag() { return Inst(IR::Opcode::GetCFlag, {}); } -void IREmitter::SetNFlag(IR::ValuePtr value) { +void IREmitter::SetNFlag(const IR::Value& value) { Inst(IR::Opcode::SetNFlag, {value}); } -void IREmitter::SetZFlag(IR::ValuePtr value) { +void IREmitter::SetZFlag(const IR::Value& value) { Inst(IR::Opcode::SetZFlag, {value}); } -void IREmitter::SetCFlag(IR::ValuePtr value) { +void IREmitter::SetCFlag(const IR::Value& value) { Inst(IR::Opcode::SetCFlag, {value}); } -void IREmitter::SetVFlag(IR::ValuePtr value) { +void IREmitter::SetVFlag(const IR::Value& 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}); } -IR::ValuePtr IREmitter::LeastSignificantByte(IR::ValuePtr value) { +IR::Value IREmitter::LeastSignificantByte(const IR::Value& 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}); } -IR::ValuePtr IREmitter::IsZero(IR::ValuePtr value) { +IR::Value IREmitter::IsZero(const IR::Value& 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 carry_out = Inst(IR::Opcode::GetCarryFromOp, {result}); 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 carry_out = Inst(IR::Opcode::GetCarryFromOp, {result}); 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 carry_out = Inst(IR::Opcode::GetCarryFromOp, {result}); 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 carry_out = Inst(IR::Opcode::GetCarryFromOp, {result}); 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 carry_out = Inst(IR::Opcode::GetCarryFromOp, {result}); auto overflow = Inst(IR::Opcode::GetOverflowFromOp, {result}); 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)}); } -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). auto result = Inst(IR::Opcode::SubWithCarry, {a, b, carry_in}); 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}; } -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)}); } -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}); } -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}); } -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}); } -IR::ValuePtr IREmitter::Not(IR::ValuePtr a) { +IR::Value IREmitter::Not(const IR::Value& 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}); } -IR::ValuePtr IREmitter::SignExtendByteToWord(IR::ValuePtr a) { +IR::Value IREmitter::SignExtendByteToWord(const IR::Value& 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}); } -IR::ValuePtr IREmitter::ZeroExtendByteToWord(IR::ValuePtr a) { +IR::Value IREmitter::ZeroExtendByteToWord(const IR::Value& 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}); } -IR::ValuePtr IREmitter::ByteReverseHalf(IR::ValuePtr a) { +IR::Value IREmitter::ByteReverseHalf(const IR::Value& 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}); } -IR::ValuePtr IREmitter::ReadMemory8(IR::ValuePtr vaddr) { +IR::Value IREmitter::ReadMemory8(const IR::Value& 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}); 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}); 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}); 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}); } -void IREmitter::WriteMemory16(IR::ValuePtr vaddr, IR::ValuePtr value) { +void IREmitter::WriteMemory16(const IR::Value& vaddr, const IR::Value& value) { 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) { - 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) { - 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) { @@ -260,28 +260,18 @@ void IREmitter::SetTerm(const IR::Terminal& terminal) { block.terminal = terminal; } -IR::ValuePtr IREmitter::Inst(IR::Opcode op, std::initializer_list args) { - auto inst = std::make_shared(op); - assert(args.size() == inst->NumArgs()); +IR::Value IREmitter::Inst(IR::Opcode op, std::initializer_list args) { + IR::Inst* inst = new(block.instruction_alloc_pool->malloc()) IR::Inst(op); + DEBUG_ASSERT(args.size() == inst->NumArgs()); 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); index++; }); - AddToBlock(inst); - return inst; -} - -IR::ValuePtr IREmitter::RegRef(Reg reg) { - auto regref = std::make_shared(reg); - AddToBlock(regref); - return regref; -} - -void IREmitter::AddToBlock(IR::ValuePtr value) { - block.instructions.emplace_back(value); + block.instructions.push_back(*inst); + return IR::Value(inst); } } // namespace Arm diff --git a/src/frontend/ir/ir_emitter.h b/src/frontend/ir/ir_emitter.h index 6efccb17..165cc058 100644 --- a/src/frontend/ir/ir_emitter.h +++ b/src/frontend/ir/ir_emitter.h @@ -21,79 +21,77 @@ public: LocationDescriptor current_location; struct ResultAndCarry { - IR::ValuePtr result; - IR::ValuePtr carry; + IR::Value result; + IR::Value carry; }; struct ResultAndCarryAndOverflow { - IR::ValuePtr result; - IR::ValuePtr carry; - IR::ValuePtr overflow; + IR::Value result; + IR::Value carry; + IR::Value overflow; }; void Unimplemented(); u32 PC(); u32 AlignPC(size_t alignment); - IR::ValuePtr Imm1(bool value); - IR::ValuePtr Imm8(u8 value); - IR::ValuePtr Imm32(u32 value); + IR::Value Imm1(bool value); + IR::Value Imm8(u8 value); + IR::Value Imm32(u32 value); - IR::ValuePtr GetRegister(Reg source_reg); - void SetRegister(const Reg dest_reg, IR::ValuePtr value); + IR::Value GetRegister(Reg source_reg); + void SetRegister(const Reg dest_reg, const IR::Value& value); - void ALUWritePC(IR::ValuePtr value); - void BranchWritePC(IR::ValuePtr value); - void BXWritePC(IR::ValuePtr value); - void LoadWritePC(IR::ValuePtr value); - void CallSupervisor(IR::ValuePtr value); + void ALUWritePC(const IR::Value& value); + void BranchWritePC(const IR::Value& value); + void BXWritePC(const IR::Value& value); + void LoadWritePC(const IR::Value& value); + void CallSupervisor(const IR::Value& value); - IR::ValuePtr GetCFlag(); - void SetNFlag(IR::ValuePtr value); - void SetZFlag(IR::ValuePtr value); - void SetCFlag(IR::ValuePtr value); - void SetVFlag(IR::ValuePtr value); + IR::Value GetCFlag(); + void SetNFlag(const IR::Value& value); + void SetZFlag(const IR::Value& value); + void SetCFlag(const IR::Value& value); + void SetVFlag(const IR::Value& value); - IR::ValuePtr LeastSignificantHalf(IR::ValuePtr value); - IR::ValuePtr LeastSignificantByte(IR::ValuePtr value); - IR::ValuePtr MostSignificantBit(IR::ValuePtr value); - IR::ValuePtr IsZero(IR::ValuePtr value); + IR::Value LeastSignificantHalf(const IR::Value& value); + IR::Value LeastSignificantByte(const IR::Value& value); + IR::Value MostSignificantBit(const IR::Value& value); + IR::Value IsZero(const IR::Value& value); - ResultAndCarry LogicalShiftLeft(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 RotateRight(IR::ValuePtr value_in, IR::ValuePtr shift_amount, IR::ValuePtr carry_in); - ResultAndCarryAndOverflow AddWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in); - IR::ValuePtr Add(IR::ValuePtr a, IR::ValuePtr b); - ResultAndCarryAndOverflow SubWithCarry(IR::ValuePtr a, IR::ValuePtr b, IR::ValuePtr carry_in); - IR::ValuePtr Sub(IR::ValuePtr a, IR::ValuePtr b); - IR::ValuePtr And(IR::ValuePtr a, IR::ValuePtr b); - IR::ValuePtr Eor(IR::ValuePtr a, IR::ValuePtr b); - IR::ValuePtr Or(IR::ValuePtr a, IR::ValuePtr b); - IR::ValuePtr Not(IR::ValuePtr a); - IR::ValuePtr SignExtendHalfToWord(IR::ValuePtr a); - IR::ValuePtr SignExtendByteToWord(IR::ValuePtr a); - IR::ValuePtr ZeroExtendHalfToWord(IR::ValuePtr a); - IR::ValuePtr ZeroExtendByteToWord(IR::ValuePtr a); - IR::ValuePtr ByteReverseWord(IR::ValuePtr a); - IR::ValuePtr ByteReverseHalf(IR::ValuePtr a); - IR::ValuePtr ByteReverseDual(IR::ValuePtr a); + ResultAndCarry LogicalShiftLeft(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in); + ResultAndCarry LogicalShiftRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in); + ResultAndCarry ArithmeticShiftRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in); + ResultAndCarry RotateRight(const IR::Value& value_in, const IR::Value& shift_amount, const IR::Value& carry_in); + ResultAndCarryAndOverflow AddWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in); + IR::Value Add(const IR::Value& a, const IR::Value& b); + ResultAndCarryAndOverflow SubWithCarry(const IR::Value& a, const IR::Value& b, const IR::Value& carry_in); + IR::Value Sub(const IR::Value& a, const IR::Value& b); + IR::Value And(const IR::Value& a, const IR::Value& b); + IR::Value Eor(const IR::Value& a, const IR::Value& b); + IR::Value Or(const IR::Value& a, const IR::Value& b); + IR::Value Not(const IR::Value& a); + IR::Value SignExtendHalfToWord(const IR::Value& a); + IR::Value SignExtendByteToWord(const IR::Value& a); + IR::Value ZeroExtendHalfToWord(const IR::Value& a); + IR::Value ZeroExtendByteToWord(const IR::Value& a); + IR::Value ByteReverseWord(const IR::Value& a); + IR::Value ByteReverseHalf(const IR::Value& a); + IR::Value ByteReverseDual(const IR::Value& a); - IR::ValuePtr ReadMemory8(IR::ValuePtr vaddr); - IR::ValuePtr ReadMemory16(IR::ValuePtr vaddr); - IR::ValuePtr ReadMemory32(IR::ValuePtr vaddr); - IR::ValuePtr ReadMemory64(IR::ValuePtr vaddr); - void WriteMemory8(IR::ValuePtr vaddr, IR::ValuePtr value); - void WriteMemory16(IR::ValuePtr vaddr, IR::ValuePtr value); - void WriteMemory32(IR::ValuePtr vaddr, IR::ValuePtr value); - void WriteMemory64(IR::ValuePtr vaddr, IR::ValuePtr value); + IR::Value ReadMemory8(const IR::Value& vaddr); + IR::Value ReadMemory16(const IR::Value& vaddr); + IR::Value ReadMemory32(const IR::Value& vaddr); + IR::Value ReadMemory64(const IR::Value& vaddr); + void WriteMemory8(const IR::Value& vaddr, const IR::Value& value); + void WriteMemory16(const IR::Value& vaddr, const IR::Value& value); + void WriteMemory32(const IR::Value& vaddr, const IR::Value& value); + void WriteMemory64(const IR::Value& vaddr, const IR::Value& value); void SetTerm(const IR::Terminal& terminal); private: - IR::ValuePtr Inst(IR::Opcode op, std::initializer_list args); - IR::ValuePtr RegRef(Reg reg); - void AddToBlock(IR::ValuePtr value); + IR::Value Inst(IR::Opcode op, std::initializer_list args); }; } // namespace Arm diff --git a/src/frontend/ir/opcodes.inc b/src/frontend/ir/opcodes.inc index ada4ce67..9f440384 100644 --- a/src/frontend/ir/opcodes.inc +++ b/src/frontend/ir/opcodes.inc @@ -1,11 +1,5 @@ // 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 OPCODE(GetRegister, T::U32, T::RegRef ) OPCODE(SetRegister, T::Void, T::RegRef, T::U32 ) diff --git a/src/frontend/translate/translate_arm.cpp b/src/frontend/translate/translate_arm.cpp index 36b709b9..3d158ac2 100644 --- a/src/frontend/translate/translate_arm.cpp +++ b/src/frontend/translate/translate_arm.cpp @@ -340,7 +340,7 @@ IR::Block TranslateArm(LocationDescriptor descriptor, MemoryRead32FuncType memor visitor.ir.block.cond_failed = { visitor.ir.current_location }; } - return visitor.ir.block; + return std::move(visitor.ir.block); } } // namespace Arm diff --git a/src/frontend/translate/translate_thumb.cpp b/src/frontend/translate/translate_thumb.cpp index 072065b7..babe816d 100644 --- a/src/frontend/translate/translate_thumb.cpp +++ b/src/frontend/translate/translate_thumb.cpp @@ -888,7 +888,7 @@ IR::Block TranslateThumb(LocationDescriptor descriptor, MemoryRead32FuncType mem visitor.ir.block.cycle_count++; } - return visitor.ir.block; + return std::move(visitor.ir.block); } } // namespace Arm diff --git a/src/ir_opt/dead_code_elimination_pass.cpp b/src/ir_opt/dead_code_elimination_pass.cpp index 1fee4b50..c3ffdb04 100644 --- a/src/ir_opt/dead_code_elimination_pass.cpp +++ b/src/ir_opt/dead_code_elimination_pass.cpp @@ -26,8 +26,8 @@ void DeadCodeElimination(IR::Block& block) { auto iter = block.instructions.end(); do { --iter; - if (!(*iter)->HasUses() && is_side_effect_free((*iter)->GetOpcode())) { - (*iter)->Invalidate(); + if (!iter->HasUses() && is_side_effect_free(iter->GetOpcode())) { + iter->Invalidate(); iter = block.instructions.erase(iter); } } while (iter != block.instructions.begin()); diff --git a/src/ir_opt/get_set_elimination_pass.cpp b/src/ir_opt/get_set_elimination_pass.cpp index 5b573e4a..0ca26337 100644 --- a/src/ir_opt/get_set_elimination_pass.cpp +++ b/src/ir_opt/get_set_elimination_pass.cpp @@ -12,6 +12,7 @@ namespace Dynarmic { namespace Optimization { void GetSetElimination(IR::Block& block) { +#if 0 using Iterator = decltype(block.instructions.begin()); struct RegisterInfo { IR::ValuePtr register_value = nullptr; @@ -102,6 +103,7 @@ void GetSetElimination(IR::Block& block) { break; } } +#endif } } // namespace Optimization diff --git a/src/ir_opt/verification_pass.cpp b/src/ir_opt/verification_pass.cpp index 85fb3799..8668a619 100644 --- a/src/ir_opt/verification_pass.cpp +++ b/src/ir_opt/verification_pass.cpp @@ -12,9 +12,11 @@ namespace Dynarmic { namespace Optimization { void VerificationPass(const IR::Block& block) { +#if 0 for (const auto& inst : block.instructions) { inst->AssertValid(); } +#endif } } // namespace Optimization