diff options
Diffstat (limited to 'src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs')
| -rw-r--r-- | src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs | 1105 |
1 files changed, 1105 insertions, 0 deletions
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs new file mode 100644 index 00000000..c0762819 --- /dev/null +++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitAlu.cs @@ -0,0 +1,1105 @@ +using Ryujinx.Cpu.LightningJit.CodeGen; +using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; +using System; +using System.Diagnostics; + +namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 +{ + static class InstEmitAlu + { + private const uint Imm12Limit = 0x1000; + + public static void AdcI(CodeGenContext context, uint rd, uint rn, uint imm, bool s) + { + EmitI(context, s ? context.Arm64Assembler.Adcs : context.Arm64Assembler.Adc, rd, rn, imm, s); + } + + public static void AdcR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitR(context, s ? context.Arm64Assembler.Adcs : context.Arm64Assembler.Adc, rd, rn, rm, sType, imm5, s); + } + + public static void AdcRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitRr(context, s ? context.Arm64Assembler.Adcs : context.Arm64Assembler.Adc, rd, rn, rm, sType, rs, s); + } + + public static void AddI(CodeGenContext context, uint rd, uint rn, uint imm, bool s) + { + EmitArithmeticI(context, s ? context.Arm64Assembler.Adds : context.Arm64Assembler.Add, rd, rn, imm, s); + } + + public static void AddR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitArithmeticR(context, s ? context.Arm64Assembler.Adds : context.Arm64Assembler.Add, rd, rn, rm, sType, imm5, s); + } + + public static void AddRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitRr(context, s ? context.Arm64Assembler.Adds : context.Arm64Assembler.Add, rd, rn, rm, sType, rs, s); + } + + public static void Adr(CodeGenContext context, uint rd, uint imm, bool add) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + + uint pc = context.Pc & ~3u; + + if (add) + { + pc += imm; + } + else + { + pc -= imm; + } + + context.Arm64Assembler.Mov(rdOperand, pc); + } + + public static void AndI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s) + { + EmitLogicalI(context, s ? context.Arm64Assembler.Ands : context.Arm64Assembler.And, rd, rn, imm, immRotated, s); + } + + public static void AndR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitLogicalR(context, s ? context.Arm64Assembler.Ands : context.Arm64Assembler.And, rd, rn, rm, sType, imm5, s); + } + + public static void AndRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitLogicalRr(context, s ? context.Arm64Assembler.Ands : context.Arm64Assembler.And, rd, rn, rm, sType, rs, s); + } + + public static void BicI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s) + { + if (!s && CodeGenCommon.TryEncodeBitMask(OperandType.I32, ~imm, out _, out _, out _)) + { + AndI(context, rd, rn, ~imm, immRotated, s); + } + else + { + EmitLogicalI(context, s ? context.Arm64Assembler.Bics : context.Arm64Assembler.Bic, rd, rn, imm, immRotated, s, immForm: false); + } + } + + public static void BicR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitLogicalR(context, s ? context.Arm64Assembler.Bics : context.Arm64Assembler.Bic, rd, rn, rm, sType, imm5, s); + } + + public static void BicRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitLogicalRr(context, s ? context.Arm64Assembler.Bics : context.Arm64Assembler.Bic, rd, rn, rm, sType, rs, s); + } + + public static void CmnI(CodeGenContext context, uint rn, uint imm) + { + EmitCompareI(context, context.Arm64Assembler.Cmn, rn, imm); + } + + public static void CmnR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5) + { + EmitCompareR(context, context.Arm64Assembler.Cmn, rn, rm, sType, imm5); + } + + public static void CmnRr(CodeGenContext context, uint rn, uint rm, uint sType, uint rs) + { + EmitCompareRr(context, context.Arm64Assembler.Cmn, rn, rm, sType, rs); + } + + public static void CmpI(CodeGenContext context, uint rn, uint imm) + { + EmitCompareI(context, context.Arm64Assembler.Cmp, rn, imm); + } + + public static void CmpR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5) + { + EmitCompareR(context, context.Arm64Assembler.Cmp, rn, rm, sType, imm5); + } + + public static void CmpRr(CodeGenContext context, uint rn, uint rm, uint sType, uint rs) + { + EmitCompareRr(context, context.Arm64Assembler.Cmp, rn, rm, sType, rs); + } + + public static void EorI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s) + { + EmitLogicalI(context, s ? context.Arm64Assembler.Eors : context.Arm64Assembler.Eor, rd, rn, imm, immRotated, s); + } + + public static void EorR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitLogicalR(context, s ? context.Arm64Assembler.Eors : context.Arm64Assembler.Eor, rd, rn, rm, sType, imm5, s); + } + + public static void EorRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitLogicalRr(context, s ? context.Arm64Assembler.Eors : context.Arm64Assembler.Eor, rd, rn, rm, sType, rs, s); + } + + public static void OrnI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s) + { + if (!s && CodeGenCommon.TryEncodeBitMask(OperandType.I32, ~imm, out _, out _, out _)) + { + OrrI(context, rd, rn, ~imm, immRotated, s); + } + else + { + EmitLogicalI(context, s ? context.Arm64Assembler.Orns : context.Arm64Assembler.Orn, rd, rn, imm, immRotated, s, immForm: false); + } + } + + public static void OrnR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitLogicalR(context, s ? context.Arm64Assembler.Orns : context.Arm64Assembler.Orn, rd, rn, rm, sType, imm5, s); + } + + public static void OrrI(CodeGenContext context, uint rd, uint rn, uint imm, bool immRotated, bool s) + { + EmitLogicalI(context, s ? context.Arm64Assembler.Orrs : context.Arm64Assembler.Orr, rd, rn, imm, immRotated, s); + } + + public static void OrrR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitLogicalR(context, s ? context.Arm64Assembler.Orrs : context.Arm64Assembler.Orr, rd, rn, rm, sType, imm5, s); + } + + public static void OrrRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitLogicalRr(context, s ? context.Arm64Assembler.Orrs : context.Arm64Assembler.Orr, rd, rn, rm, sType, rs, s); + } + + public static void RsbI(CodeGenContext context, uint rd, uint rn, uint imm, bool s) + { + if (imm == 0) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + if (s) + { + context.Arm64Assembler.Negs(rdOperand, rnOperand); + context.SetNzcvModified(); + } + else + { + context.Arm64Assembler.Neg(rdOperand, rnOperand); + } + } + else + { + EmitI(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, imm, s, reverse: true); + } + } + + public static void RsbR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitR(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, rm, sType, imm5, s, reverse: true); + } + + public static void RsbRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitRr(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, rm, sType, rs, s, reverse: true); + } + + public static void RscI(CodeGenContext context, uint rd, uint rn, uint imm, bool s) + { + EmitI(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, imm, s, reverse: true); + } + + public static void RscR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitR(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, rm, sType, imm5, s, reverse: true); + } + + public static void RscRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitRr(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, rm, sType, rs, s, reverse: true); + } + + public static void SbcI(CodeGenContext context, uint rd, uint rn, uint imm, bool s) + { + EmitI(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, imm, s); + } + + public static void SbcR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitR(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, rm, sType, imm5, s); + } + + public static void SbcRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitRr(context, s ? context.Arm64Assembler.Sbcs : context.Arm64Assembler.Sbc, rd, rn, rm, sType, rs, s); + } + + public static void SubI(CodeGenContext context, uint rd, uint rn, uint imm, bool s) + { + EmitArithmeticI(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, imm, s); + } + + public static void SubR(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + EmitArithmeticR(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, rm, sType, imm5, s); + } + + public static void SubRr(CodeGenContext context, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + EmitRr(context, s ? context.Arm64Assembler.Subs : context.Arm64Assembler.Sub, rd, rn, rm, sType, rs, s); + } + + public static void TeqI(CodeGenContext context, uint rn, uint imm, bool immRotated) + { + EmitLogicalI(context, (rnOperand, rmOperand) => EmitTeq(context, rnOperand, rmOperand), rn, imm, immRotated); + } + + public static void TeqR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5) + { + EmitLogicalR(context, (rnOperand, rmOperand) => EmitTeq(context, rnOperand, rmOperand), rn, rm, sType, imm5); + } + + public static void TeqRr(CodeGenContext context, uint rn, uint rm, uint sType, uint rs) + { + EmitLogicalRr(context, (rnOperand, rmOperand) => EmitTeq(context, rnOperand, rmOperand), rn, rm, sType, rs); + } + + public static void TstI(CodeGenContext context, uint rn, uint imm, bool immRotated) + { + EmitLogicalI(context, context.Arm64Assembler.Tst, rn, imm, immRotated); + } + + public static void TstR(CodeGenContext context, uint rn, uint rm, uint sType, uint imm5) + { + EmitLogicalR(context, context.Arm64Assembler.Tst, rn, rm, sType, imm5); + } + + public static void TstRr(CodeGenContext context, uint rn, uint rm, uint sType, uint rs) + { + EmitLogicalRr(context, context.Arm64Assembler.Tst, rn, rm, sType, rs); + } + + private static void EmitArithmeticI(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint imm, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + if (imm < Imm12Limit) + { + Operand rmOperand = new(OperandKind.Constant, OperandType.I32, imm); + + action(rdOperand, rnOperand, rmOperand); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mov(tempRegister.Operand, imm); + + action(rdOperand, rnOperand, tempRegister.Operand); + } + + if (s) + { + context.SetNzcvModified(); + } + } + + private static void EmitArithmeticR( + CodeGenContext context, + Action<Operand, Operand, Operand, ArmShiftType, int> action, + uint rd, + uint rn, + uint rm, + uint sType, + uint imm5, + bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (CanShiftArithmetic(sType, imm5)) + { + action(rdOperand, rnOperand, rmOperand, (ArmShiftType)sType, (int)imm5); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType); + + action(rdOperand, rnOperand, rmOperand, ArmShiftType.Lsl, 0); + } + + if (s) + { + context.SetNzcvModified(); + } + } + + private static void EmitCompareI(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint imm) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + if (imm < Imm12Limit) + { + Operand rmOperand = new(OperandKind.Constant, OperandType.I32, imm); + + action(rnOperand, rmOperand); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mov(tempRegister.Operand, imm); + + action(rnOperand, tempRegister.Operand); + } + + context.SetNzcvModified(); + } + + private static void EmitCompareR( + CodeGenContext context, + Action<Operand, Operand, ArmShiftType, int> action, + uint rn, + uint rm, + uint sType, + uint imm5) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (CanShiftArithmetic(sType, imm5)) + { + action(rnOperand, rmOperand, (ArmShiftType)sType, (int)imm5); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType); + + action(rnOperand, rmOperand, ArmShiftType.Lsl, 0); + } + + context.SetNzcvModified(); + } + + private static void EmitCompareRr(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint rm, uint sType, uint rs) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + rmOperand = GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType); + + action(rnOperand, rmOperand); + + context.SetNzcvModified(); + } + + private static void EmitI(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint imm, bool s, bool reverse = false) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mov(tempRegister.Operand, imm); + + if (reverse) + { + action(rdOperand, tempRegister.Operand, rnOperand); + } + else + { + action(rdOperand, rnOperand, tempRegister.Operand); + } + + if (s) + { + context.SetNzcvModified(); + } + } + + private static void EmitR(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s, bool reverse = false) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType); + + if (reverse) + { + action(rdOperand, rmOperand, rnOperand); + } + else + { + action(rdOperand, rnOperand, rmOperand); + } + + if (s) + { + context.SetNzcvModified(); + } + } + + private static void EmitRr(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint rm, uint sType, uint rs, bool s, bool reverse = false) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + rmOperand = GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType); + + if (reverse) + { + action(rdOperand, rmOperand, rnOperand); + } + else + { + action(rdOperand, rnOperand, rmOperand); + } + + if (s) + { + context.SetNzcvModified(); + } + } + + private static void EmitLogicalI(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint imm, bool immRotated) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + if (immRotated) + { + if ((imm & (1u << 31)) != 0) + { + context.Arm64Assembler.Orr(flagsRegister.Operand, flagsRegister.Operand, InstEmitCommon.Const(2)); + } + else + { + context.Arm64Assembler.Bfc(flagsRegister.Operand, 1, 1); + } + } + + if (CodeGenCommon.TryEncodeBitMask(OperandType.I32, imm, out _, out _, out _)) + { + action(rnOperand, InstEmitCommon.Const((int)imm)); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mov(tempRegister.Operand, imm); + + action(rnOperand, tempRegister.Operand); + } + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + + private static void EmitLogicalI( + CodeGenContext context, + Action<Operand, Operand, Operand> action, + uint rd, + uint rn, + uint imm, + bool immRotated, + bool s, + bool immForm = true) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + + ScopedRegister flagsRegister = default; + + if (s) + { + flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + if (immRotated) + { + if ((imm & (1u << 31)) != 0) + { + context.Arm64Assembler.Orr(flagsRegister.Operand, flagsRegister.Operand, InstEmitCommon.Const(2)); + } + else + { + context.Arm64Assembler.Bfc(flagsRegister.Operand, 1, 1); + } + } + } + + if (imm == 0 || (immForm && CodeGenCommon.TryEncodeBitMask(OperandType.I32, imm, out _, out _, out _))) + { + action(rdOperand, rnOperand, InstEmitCommon.Const((int)imm)); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Mov(tempRegister.Operand, imm); + + action(rdOperand, rnOperand, tempRegister.Operand); + } + + if (s) + { + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + flagsRegister.Dispose(); + + context.SetNzcvModified(); + } + } + + private static void EmitLogicalR(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint rm, uint sType, uint imm5) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, flagsRegister.Operand); + + action(rnOperand, rmOperand); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + + private static void EmitLogicalR(CodeGenContext context, Action<Operand, Operand, Operand, ArmShiftType, int> action, uint rd, uint rn, uint rm, uint sType, uint imm5, bool s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + + if (CanShift(sType, imm5) && !s) + { + action(rdOperand, rnOperand, rmOperand, (ArmShiftType)sType, (int)imm5); + } + else + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + ScopedRegister flagsRegister = default; + + if (s) + { + flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, flagsRegister.Operand); + } + else + { + rmOperand = GetMShiftedByImmediate(context, tempRegister.Operand, rmOperand, imm5, sType, null); + } + + action(rdOperand, rnOperand, rmOperand, ArmShiftType.Lsl, 0); + + if (s) + { + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + flagsRegister.Dispose(); + + context.SetNzcvModified(); + } + } + } + + private static void EmitLogicalRr(CodeGenContext context, Action<Operand, Operand> action, uint rn, uint rm, uint sType, uint rs) + { + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + rmOperand = GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType, flagsRegister.Operand); + + action(rnOperand, rmOperand); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + + private static void EmitLogicalRr(CodeGenContext context, Action<Operand, Operand, Operand> action, uint rd, uint rn, uint rm, uint sType, uint rs, bool s) + { + if (s) + { + Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); + Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); + Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); + Operand rsOperand = InstEmitCommon.GetInputGpr(context, rs); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); + + rmOperand = GetMShiftedByReg(context, tempRegister.Operand, rmOperand, rsOperand, sType, flagsRegister.Operand); + + action(rdOperand, rnOperand, rmOperand); + + InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); + + context.SetNzcvModified(); + } + else + { + EmitRr(context, action, rd, rn, rm, sType, rs, s); + } + } + + public static bool CanShiftArithmetic(uint sType, uint imm5) + { + // We can't encode ROR or RRX. + + return sType != 3 && (sType == 0 || imm5 != 0); + } + + public static bool CanShift(uint sType, uint imm5) + { + // We can encode all shift types directly, except RRX. + + return imm5 != 0 || sType == 0; + } + + public static Operand GetMShiftedByImmediate(CodeGenContext context, Operand dest, Operand m, uint imm, uint sType, Operand? carryOut = null) + { + int shift = (int)imm; + + if (shift == 0) + { + switch ((ArmShiftType)sType) + { + case ArmShiftType.Lsr: + shift = 32; + break; + case ArmShiftType.Asr: + shift = 32; + break; + case ArmShiftType.Ror: + shift = 1; + break; + } + } + + if (shift != 0) + { + switch ((ArmShiftType)sType) + { + case ArmShiftType.Lsl: + m = GetLslC(context, dest, m, carryOut, shift); + break; + case ArmShiftType.Lsr: + m = GetLsrC(context, dest, m, carryOut, shift); + break; + case ArmShiftType.Asr: + m = GetAsrC(context, dest, m, carryOut, shift); + break; + case ArmShiftType.Ror: + if (imm != 0) + { + m = GetRorC(context, dest, m, carryOut, shift); + } + else + { + m = GetRrxC(context, dest, m, carryOut); + } + break; + } + } + + return m; + } + + public static Operand GetMShiftedByReg(CodeGenContext context, Operand dest, Operand m, Operand s, uint sType, Operand? carryOut = null) + { + Operand shiftResult = m; + + switch ((ArmShiftType)sType) + { + case ArmShiftType.Lsl: + shiftResult = EmitLslC(context, dest, m, carryOut, s); + break; + case ArmShiftType.Lsr: + shiftResult = EmitLsrC(context, dest, m, carryOut, s); + break; + case ArmShiftType.Asr: + shiftResult = EmitAsrC(context, dest, m, carryOut, s); + break; + case ArmShiftType.Ror: + shiftResult = EmitRorC(context, dest, m, carryOut, s); + break; + } + + return shiftResult; + } + + private static void EmitIfHelper(CodeGenContext context, Operand boolValue, Action action, bool expected = true) + { + Debug.Assert(boolValue.Type == OperandType.I32); + + int branchInstructionPointer = context.CodeWriter.InstructionPointer; + + if (expected) + { + context.Arm64Assembler.Cbnz(boolValue, 0); + } + else + { + context.Arm64Assembler.Cbz(boolValue, 0); + } + + action(); + + int offset = context.CodeWriter.InstructionPointer - branchInstructionPointer; + Debug.Assert(offset >= 0); + Debug.Assert((offset << 13) >> 13 == offset); + uint branchInst = context.CodeWriter.ReadInstructionAt(branchInstructionPointer); + context.CodeWriter.WriteInstructionAt(branchInstructionPointer, branchInst | (uint)(offset << 5)); + } + + private static Operand EmitLslC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, Operand shift) + { + Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand mask = tempRegister.Operand; + context.Arm64Assembler.Uxtb(mask, shift); + context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(32)); + context.Arm64Assembler.Asr(mask, mask, InstEmitCommon.Const(31)); + + Operand dest64 = new(OperandKind.Register, OperandType.I64, dest.Value); + + if (carryOut.HasValue) + { + context.Arm64Assembler.Lslv(dest64, m, shift); + } + else + { + context.Arm64Assembler.Lslv(dest, m, shift); + } + + // If shift >= 32, force the result to 0. + context.Arm64Assembler.And(dest, dest, mask); + + if (carryOut.HasValue) + { + EmitIfHelper(context, shift, () => + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Uxtb(mask, shift); + context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(33)); + context.Arm64Assembler.Lsr(mask, mask, InstEmitCommon.Const(31)); + context.Arm64Assembler.Lsr(tempRegister.Operand, dest64, InstEmitCommon.Const(32)); + context.Arm64Assembler.And(tempRegister.Operand, tempRegister.Operand, mask); + + UpdateCarryFlag(context, tempRegister.Operand, carryOut.Value); + }, false); + } + + return dest; + } + + private static Operand GetLslC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, int shift) + { + Debug.Assert(m.Type == OperandType.I32); + + if ((uint)shift > 32) + { + return GetShiftByMoreThan32(context, carryOut); + } + else if (shift == 32) + { + if (carryOut.HasValue) + { + SetCarryMLsb(context, m, carryOut.Value); + } + + return InstEmitCommon.Const(0); + } + else + { + if (carryOut.HasValue) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Lsr(tempRegister.Operand, m, InstEmitCommon.Const(32 - shift)); + + UpdateCarryFlag(context, tempRegister.Operand, carryOut.Value); + } + + context.Arm64Assembler.Lsl(dest, m, InstEmitCommon.Const(shift)); + + return dest; + } + } + + private static Operand EmitLsrC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, Operand shift) + { + Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand mask = tempRegister.Operand; + context.Arm64Assembler.Uxtb(mask, shift); + context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(32)); + context.Arm64Assembler.Asr(mask, mask, InstEmitCommon.Const(31)); + + context.Arm64Assembler.Lsrv(dest, m, shift); + + // If shift >= 32, force the result to 0. + context.Arm64Assembler.And(dest, dest, mask); + + if (carryOut.HasValue) + { + EmitIfHelper(context, shift, () => + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Uxtb(mask, shift); + context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(33)); + context.Arm64Assembler.Lsr(mask, mask, InstEmitCommon.Const(31)); + context.Arm64Assembler.Sub(tempRegister.Operand, shift, InstEmitCommon.Const(1)); + context.Arm64Assembler.Lsrv(tempRegister.Operand, m, tempRegister.Operand); + context.Arm64Assembler.And(tempRegister.Operand, tempRegister.Operand, mask); + + UpdateCarryFlag(context, tempRegister.Operand, carryOut.Value); + }, false); + } + + return dest; + } + + public static Operand GetLsrC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, int shift) + { + Debug.Assert(m.Type == OperandType.I32); + + if ((uint)shift > 32) + { + return GetShiftByMoreThan32(context, carryOut); + } + else if (shift == 32) + { + if (carryOut.HasValue) + { + SetCarryMMsb(context, m, carryOut.Value); + } + + return InstEmitCommon.Const(0); + } + else + { + if (carryOut.HasValue) + { + SetCarryMShrOut(context, m, shift, carryOut.Value); + } + + context.Arm64Assembler.Lsr(dest, m, InstEmitCommon.Const(shift)); + + return dest; + } + } + + private static Operand GetShiftByMoreThan32(CodeGenContext context, Operand? carryOut) + { + if (carryOut.HasValue) + { + // Clear carry flag. + + context.Arm64Assembler.Bfc(carryOut.Value, 1, 1); + } + + return InstEmitCommon.Const(0); + } + + private static Operand EmitAsrC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, Operand shift) + { + Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + Operand mask = tempRegister.Operand; + context.Arm64Assembler.Uxtb(mask, shift); + context.Arm64Assembler.Sub(mask, mask, InstEmitCommon.Const(31)); + context.Arm64Assembler.Orn(mask, shift, mask, ArmShiftType.Asr, 31); + + context.Arm64Assembler.Asrv(dest, m, mask); + + if (carryOut.HasValue) + { + EmitIfHelper(context, shift, () => + { + // If shift >= 32, carry should be equal to the MSB of Rm. + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Sub(tempRegister.Operand, mask, InstEmitCommon.Const(1)); + context.Arm64Assembler.Orr(tempRegister.Operand, tempRegister.Operand, mask, ArmShiftType.Asr, 31); + context.Arm64Assembler.Lsrv(tempRegister.Operand, m, tempRegister.Operand); + + UpdateCarryFlag(context, tempRegister.Operand, carryOut.Value); + }, false); + } + + return dest; + } + + private static Operand GetAsrC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, int shift) + { + Debug.Assert(m.Type == OperandType.I32); + + if ((uint)shift >= 32) + { + context.Arm64Assembler.Asr(dest, m, InstEmitCommon.Const(31)); + + if (carryOut.HasValue) + { + SetCarryMLsb(context, dest, carryOut.Value); + } + + return dest; + } + else + { + if (carryOut.HasValue) + { + SetCarryMShrOut(context, m, shift, carryOut.Value); + } + + context.Arm64Assembler.Asr(dest, m, InstEmitCommon.Const(shift)); + + return dest; + } + } + + private static Operand EmitRorC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, Operand shift) + { + Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32); + + context.Arm64Assembler.Rorv(dest, m, shift); + + if (carryOut.HasValue) + { + EmitIfHelper(context, shift, () => + { + SetCarryMMsb(context, m, carryOut.Value); + }, false); + } + + return dest; + } + + private static Operand GetRorC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut, int shift) + { + Debug.Assert(m.Type == OperandType.I32); + + shift &= 0x1f; + + context.Arm64Assembler.Ror(dest, m, InstEmitCommon.Const(shift)); + + if (carryOut.HasValue) + { + SetCarryMMsb(context, dest, carryOut.Value); + } + + return dest; + } + + private static Operand GetRrxC(CodeGenContext context, Operand dest, Operand m, Operand? carryOut) + { + Debug.Assert(m.Type == OperandType.I32); + + // Rotate right by 1 with carry. + + if (carryOut.HasValue) + { + SetCarryMLsb(context, m, carryOut.Value); + } + + context.Arm64Assembler.Mov(dest, m); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.MrsNzcv(tempRegister.Operand); + context.Arm64Assembler.Bfxil(dest, tempRegister.Operand, 29, 1); + context.Arm64Assembler.Ror(dest, dest, InstEmitCommon.Const(1)); + + return dest; + } + + private static void SetCarryMLsb(CodeGenContext context, Operand m, Operand carryOut) + { + Debug.Assert(m.Type == OperandType.I32); + + UpdateCarryFlag(context, m, carryOut); + } + + private static void SetCarryMMsb(CodeGenContext context, Operand m, Operand carryOut) + { + Debug.Assert(m.Type == OperandType.I32); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Lsr(tempRegister.Operand, m, InstEmitCommon.Const(31)); + + UpdateCarryFlag(context, tempRegister.Operand, carryOut); + } + + private static void SetCarryMShrOut(CodeGenContext context, Operand m, int shift, Operand carryOut) + { + Debug.Assert(m.Type == OperandType.I32); + + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Lsr(tempRegister.Operand, m, InstEmitCommon.Const(shift - 1)); + + UpdateCarryFlag(context, tempRegister.Operand, carryOut); + } + + private static void UpdateCarryFlag(CodeGenContext context, Operand value, Operand carryOut) + { + context.Arm64Assembler.Bfi(carryOut, value, 1, 1); + } + + private static void EmitTeq(CodeGenContext context, Operand rnOperand, Operand rmOperand) + { + using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); + + context.Arm64Assembler.Eors(tempRegister.Operand, rnOperand, rmOperand); + } + } +} |
