diff --git a/src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp b/src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp index f5624a8d..feccbe7e 100644 --- a/src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp @@ -188,7 +188,6 @@ void EmitIR(oaknut::CodeGenerator& code, Emit template<> void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { - // TODO: Use host flags const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); auto args = ctx.reg_alloc.GetArgumentInfo(inst); @@ -198,12 +197,11 @@ void EmitIR(oaknut::CodeGenerator& code, EmitCon if (!carry_inst) { if (shift_arg.IsImmediate()) { + const u8 shift = shift_arg.GetImmediateU8(); auto Wresult = ctx.reg_alloc.WriteW(inst); auto Woperand = ctx.reg_alloc.ReadW(operand_arg); RegAlloc::Realize(Wresult, Woperand); - const u8 shift = shift_arg.GetImmediateU8(); - if (shift <= 31) { code.LSL(Wresult, Woperand, shift); } else { @@ -227,7 +225,6 @@ void EmitIR(oaknut::CodeGenerator& code, EmitCon ctx.reg_alloc.DefineAsExisting(carry_inst, carry_arg); } else if (shift_arg.IsImmediate()) { // TODO: Use RMIF - const u8 shift = shift_arg.GetImmediateU8(); if (shift < 32) { @@ -315,10 +312,93 @@ void EmitIR(oaknut::CodeGenerator& code, EmitCo template<> void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { - (void)code; - (void)ctx; - (void)inst; - ASSERT_FALSE("Unimplemented"); + const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto& operand_arg = args[0]; + auto& shift_arg = args[1]; + auto& carry_arg = args[2]; + + if (!carry_inst) { + if (shift_arg.IsImmediate()) { + const u8 shift = shift_arg.GetImmediateU8(); + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + RegAlloc::Realize(Wresult, Woperand); + + code.ASR(Wresult, Woperand, shift <= 31 ? shift : 31); + } else { + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + auto Wshift = ctx.reg_alloc.ReadW(shift_arg); + RegAlloc::Realize(Wresult, Woperand, Wshift); + ctx.reg_alloc.SpillFlags(); + + code.AND(Wscratch0, Wshift, 0xff); + code.MOV(Wscratch1, 31); + code.CMP(Wscratch0, 31); + code.CSEL(Wscratch0, Wscratch0, Wscratch1, LS); + code.ASR(Wresult, Woperand, Wscratch0); + } + } else { + if (shift_arg.IsImmediate() && shift_arg.GetImmediateU8() == 0) { + ctx.reg_alloc.DefineAsExisting(inst, operand_arg); + ctx.reg_alloc.DefineAsExisting(carry_inst, carry_arg); + } else if (shift_arg.IsImmediate()) { + // TODO: Use RMIF + + const u8 shift = shift_arg.GetImmediateU8(); + + if (shift <= 31) { + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + RegAlloc::Realize(Wresult, Wcarry_out, Woperand); + + code.UBFX(Wcarry_out, Woperand, shift - 1, 1); + code.LSL(Wcarry_out, Wcarry_out, 29); + code.ASR(Wresult, Woperand, shift); + } else { + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + RegAlloc::Realize(Wresult, Wcarry_out, Woperand); + + code.ASR(Wresult, Woperand, 31); + code.AND(Wcarry_out, Wresult, 1 << 29); + } + } else { + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + auto Wshift = ctx.reg_alloc.ReadW(shift_arg); + auto Wcarry_in = ctx.reg_alloc.ReadW(carry_arg); + RegAlloc::Realize(Wresult, Wcarry_out, Woperand, Wshift, Wcarry_in); + ctx.reg_alloc.SpillFlags(); + + // TODO: Use RMIF + + oaknut::Label zero, end; + + code.ANDS(Wscratch0, Wshift, 0xff); + code.B(EQ, zero); + + code.MOV(Wscratch1, 31); + code.CMP(Wscratch0, 31); + code.CSEL(Wscratch0, Wscratch0, Wscratch1, LO); + code.SUB(Wscratch1, Wscratch0, 1); + code.ASR(Wresult, Woperand, Wscratch0); + code.LSR(Wcarry_out, Woperand, Wscratch1); + code.UBFIZ(Wcarry_out, Wcarry_out, 29, 1); + code.B(end); + + code.l(zero); + code.MOV(*Wresult, Woperand); + code.MOV(*Wcarry_out, Wcarry_in); + + code.l(end); + } + } } template<>