using Ryujinx.Cpu.LightningJit.CodeGen; using System; namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64 { static class InstEmitMultiply { public static void Mla(CodeGenContext context, uint rd, uint rn, uint rm, uint ra) { Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); context.Arm64Assembler.Madd(rdOperand, rnOperand, rmOperand, raOperand); } public static void Mls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra) { Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); context.Arm64Assembler.Msub(rdOperand, rnOperand, rmOperand, raOperand); } public static void Mul(CodeGenContext context, uint rd, uint rn, uint rm, bool s) { Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); if (s) { using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); context.Arm64Assembler.Mul(rdOperand, rnOperand, rmOperand); context.Arm64Assembler.Tst(rdOperand, rdOperand); InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); context.SetNzcvModified(); } else { context.Arm64Assembler.Mul(rdOperand, rnOperand, rmOperand); } } public static void Smlabb(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool nHigh, bool mHigh) { using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh); SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); context.Arm64Assembler.Sxtw(tempA64, raOperand); context.Arm64Assembler.Smaddl(tempN.Operand, tempN.Operand, tempM.Operand, tempA64); CheckResultOverflow(context, tempM64, tempN.Operand); context.Arm64Assembler.Mov(rdOperand, tempN.Operand); } public static void Smlad(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x) { EmitSmladSmlsd(context, rd, rn, rm, ra, x, add: true); } public static void Smlal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s) { EmitMultiplyAddLong(context, context.Arm64Assembler.Smaddl, rdLo, rdHi, rn, rm, s); } public static void Smlalbb(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool nHigh, bool mHigh) { Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh); SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); context.Arm64Assembler.Lsl(tempA64, rdHiOperand64, InstEmitCommon.Const(32)); context.Arm64Assembler.Orr(tempA64, tempA64, rdLoOperand); context.Arm64Assembler.Smaddl(rdLoOperand64, tempN.Operand, tempM.Operand, tempA64); if (rdLo != rdHi) { context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); } context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. } public static void Smlald(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x) { EmitSmlaldSmlsld(context, rdLo, rdHi, rn, rm, x, add: true); } public static void Smlawb(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool mHigh) { using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); context.Arm64Assembler.Sxtw(tempA64, raOperand); context.Arm64Assembler.Lsl(tempA64, tempA64, InstEmitCommon.Const(16)); context.Arm64Assembler.Smaddl(tempN.Operand, rnOperand, tempM.Operand, tempA64); context.Arm64Assembler.Asr(tempN64, tempN64, InstEmitCommon.Const(16)); CheckResultOverflow(context, tempM64, tempN.Operand); context.Arm64Assembler.Mov(rdOperand, tempN.Operand); } public static void Smlsd(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x) { EmitSmladSmlsd(context, rd, rn, rm, ra, x, add: false); } public static void Smlsld(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x) { EmitSmlaldSmlsld(context, rdLo, rdHi, rn, rm, x, add: false); } public static void Smmla(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r) { EmitSmmlaSmmls(context, rd, rn, rm, ra, r, add: true); } public static void Smmls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r) { EmitSmmlaSmmls(context, rd, rn, rm, ra, r, add: false); } public static void Smmul(CodeGenContext context, uint rd, uint rn, uint rm, bool r) { Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); context.Arm64Assembler.Smull(rdOperand64, rnOperand, rmOperand); if (r) { using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); context.Arm64Assembler.Mov(tempRegister.Operand, 0x80000000u); context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempRegister.Operand); } context.Arm64Assembler.Lsr(rdOperand64, rdOperand64, InstEmitCommon.Const(32)); } public static void Smuad(CodeGenContext context, uint rd, uint rn, uint rm, bool x) { EmitSmuadSmusd(context, rd, rn, rm, x, add: true); } public static void Smulbb(CodeGenContext context, uint rd, uint rn, uint rm, bool nHigh, bool mHigh) { Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); SelectSignedHalfword(context, tempN.Operand, rnOperand, nHigh); SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); context.Arm64Assembler.Smull(rdOperand64, tempN.Operand, tempM.Operand); context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend. } public static void Smull(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s) { EmitMultiplyLong(context, context.Arm64Assembler.Smull, rdLo, rdHi, rn, rm, s); } public static void Smulwb(CodeGenContext context, uint rd, uint rn, uint rm, bool mHigh) { using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); SelectSignedHalfword(context, tempM.Operand, rmOperand, mHigh); context.Arm64Assembler.Smull(tempN.Operand, rnOperand, tempM.Operand); context.Arm64Assembler.Asr(tempN64, tempN64, InstEmitCommon.Const(16)); CheckResultOverflow(context, tempM64, tempN.Operand); context.Arm64Assembler.Mov(rdOperand, tempN.Operand); } public static void Smusd(CodeGenContext context, uint rd, uint rn, uint rm, bool x) { EmitSmuadSmusd(context, rd, rn, rm, x, add: false); } public static void Umaal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm) { Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); if (rdLo == rdHi) { using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand tempRegister64 = new(OperandKind.Register, OperandType.I64, tempRegister.Operand.Value); context.Arm64Assembler.Umaddl(tempRegister64, rnOperand, rmOperand, rdLoOperand64); context.Arm64Assembler.Add(rdLoOperand64, tempRegister64, rdHiOperand64); } else { context.Arm64Assembler.Umaddl(rdLoOperand64, rnOperand, rmOperand, rdLoOperand64); context.Arm64Assembler.Add(rdLoOperand64, rdLoOperand64, rdHiOperand64); } if (rdLo != rdHi) { context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); } context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. } public static void Umlal(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s) { EmitMultiplyAddLong(context, context.Arm64Assembler.Umaddl, rdLo, rdHi, rn, rm, s); } public static void Umull(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool s) { EmitMultiplyLong(context, context.Arm64Assembler.Umull, rdLo, rdHi, rn, rm, s); } private static void EmitMultiplyLong(CodeGenContext context, Action action, uint rdLo, uint rdHi, uint rn, uint rm, bool s) { Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); if (s) { using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); action(rdLoOperand64, rnOperand, rmOperand); context.Arm64Assembler.Tst(rdLoOperand64, rdLoOperand64); InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); } else { action(rdLoOperand64, rnOperand, rmOperand); } if (rdLo != rdHi) { context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); } context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. } private static void EmitMultiplyAddLong(CodeGenContext context, Action action, uint rdLo, uint rdHi, uint rn, uint rm, bool s) { Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); using ScopedRegister raRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand raOperand64 = new(OperandKind.Register, OperandType.I64, raRegister.Operand.Value); context.Arm64Assembler.Lsl(raOperand64, rdHiOperand64, InstEmitCommon.Const(32)); context.Arm64Assembler.Orr(raOperand64, raOperand64, rdLoOperand); if (s) { using ScopedRegister flagsRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped(); InstEmitCommon.GetCurrentFlags(context, flagsRegister.Operand); action(rdLoOperand64, rnOperand, rmOperand, raOperand64); context.Arm64Assembler.Tst(rdLoOperand64, rdLoOperand64); InstEmitCommon.RestoreCvFlags(context, flagsRegister.Operand); context.SetNzcvModified(); } else { action(rdLoOperand64, rnOperand, rmOperand, raOperand64); } if (rdLo != rdHi) { context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); } context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. } private static void EmitSmladSmlsd(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool x, bool add) { Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); ScopedRegister swapTemp = default; if (x) { swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped(); context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16)); rmOperand = swapTemp.Operand; } context.Arm64Assembler.Sxth(tempN64, rnOperand); context.Arm64Assembler.Sxth(tempM64, rmOperand); context.Arm64Assembler.Sxtw(tempA64, raOperand); context.Arm64Assembler.Mul(rdOperand64, tempN64, tempM64); context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16)); context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16)); if (add) { context.Arm64Assembler.Smaddl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64); } else { context.Arm64Assembler.Smsubl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64); } context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempA64); CheckResultOverflow(context, tempM64, rdOperand64); context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend. if (x) { swapTemp.Dispose(); } } private static void EmitSmlaldSmlsld(CodeGenContext context, uint rdLo, uint rdHi, uint rn, uint rm, bool x, bool add) { Operand rdLoOperand = InstEmitCommon.GetOutputGpr(context, rdLo); Operand rdHiOperand = InstEmitCommon.GetOutputGpr(context, rdHi); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand rdLoOperand64 = new(OperandKind.Register, OperandType.I64, rdLoOperand.Value); Operand rdHiOperand64 = new(OperandKind.Register, OperandType.I64, rdHiOperand.Value); using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); ScopedRegister swapTemp = default; if (x) { swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped(); context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16)); rmOperand = swapTemp.Operand; } context.Arm64Assembler.Sxth(tempN64, rnOperand); context.Arm64Assembler.Sxth(tempM64, rmOperand); context.Arm64Assembler.Mul(rdLoOperand64, tempN64, tempM64); context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16)); context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16)); if (add) { context.Arm64Assembler.Smaddl(rdLoOperand64, tempN.Operand, tempM.Operand, rdLoOperand64); } else { context.Arm64Assembler.Smsubl(rdLoOperand64, tempN.Operand, tempM.Operand, rdLoOperand64); } context.Arm64Assembler.Lsl(tempA64, rdHiOperand64, InstEmitCommon.Const(32)); context.Arm64Assembler.Orr(tempA64, tempA64, rdLoOperand); context.Arm64Assembler.Add(rdLoOperand64, rdLoOperand64, tempA64); if (rdLo != rdHi) { context.Arm64Assembler.Lsr(rdHiOperand64, rdLoOperand64, InstEmitCommon.Const(32)); } context.Arm64Assembler.Mov(rdLoOperand, rdLoOperand); // Zero-extend. if (x) { swapTemp.Dispose(); } } private static void EmitSmmlaSmmls(CodeGenContext context, uint rd, uint rn, uint rm, uint ra, bool r, bool add) { Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand raOperand = InstEmitCommon.GetInputGpr(context, ra); Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); Operand raOperand64 = new(OperandKind.Register, OperandType.I64, raOperand.Value); using ScopedRegister tempA = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand tempA64 = new(OperandKind.Register, OperandType.I64, tempA.Operand.Value); context.Arm64Assembler.Lsl(tempA64, raOperand64, InstEmitCommon.Const(32)); if (add) { context.Arm64Assembler.Smaddl(rdOperand64, rnOperand, rmOperand, tempA64); } else { context.Arm64Assembler.Smsubl(rdOperand64, rnOperand, rmOperand, tempA64); } if (r) { context.Arm64Assembler.Mov(tempA.Operand, 0x80000000u); context.Arm64Assembler.Add(rdOperand64, rdOperand64, tempA64); } context.Arm64Assembler.Lsr(rdOperand64, rdOperand64, InstEmitCommon.Const(32)); } private static void EmitSmuadSmusd(CodeGenContext context, uint rd, uint rn, uint rm, bool x, bool add) { Operand rdOperand = InstEmitCommon.GetOutputGpr(context, rd); Operand rnOperand = InstEmitCommon.GetInputGpr(context, rn); Operand rmOperand = InstEmitCommon.GetInputGpr(context, rm); Operand rdOperand64 = new(OperandKind.Register, OperandType.I64, rdOperand.Value); using ScopedRegister tempN = context.RegisterAllocator.AllocateTempGprRegisterScoped(); using ScopedRegister tempM = context.RegisterAllocator.AllocateTempGprRegisterScoped(); Operand tempN64 = new(OperandKind.Register, OperandType.I64, tempN.Operand.Value); Operand tempM64 = new(OperandKind.Register, OperandType.I64, tempM.Operand.Value); ScopedRegister swapTemp = default; if (x) { swapTemp = context.RegisterAllocator.AllocateTempGprRegisterScoped(); context.Arm64Assembler.Ror(swapTemp.Operand, rmOperand, InstEmitCommon.Const(16)); rmOperand = swapTemp.Operand; } context.Arm64Assembler.Sxth(tempN64, rnOperand); context.Arm64Assembler.Sxth(tempM64, rmOperand); context.Arm64Assembler.Mul(rdOperand64, tempN64, tempM64); context.Arm64Assembler.Asr(tempN.Operand, rnOperand, InstEmitCommon.Const(16)); context.Arm64Assembler.Asr(tempM.Operand, rmOperand, InstEmitCommon.Const(16)); if (add) { context.Arm64Assembler.Smaddl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64); } else { context.Arm64Assembler.Smsubl(rdOperand64, tempN.Operand, tempM.Operand, rdOperand64); } context.Arm64Assembler.Mov(rdOperand, rdOperand); // Zero-extend. if (x) { swapTemp.Dispose(); } } private static void SelectSignedHalfword(CodeGenContext context, Operand dest, Operand source, bool high) { if (high) { context.Arm64Assembler.Asr(dest, source, InstEmitCommon.Const(16)); } else { context.Arm64Assembler.Sxth(dest, source); } } private static void CheckResultOverflow(CodeGenContext context, Operand temp64, Operand result) { context.Arm64Assembler.Sxtw(temp64, result); context.Arm64Assembler.Sub(temp64, temp64, result); int branchIndex = context.CodeWriter.InstructionPointer; context.Arm64Assembler.Cbz(temp64, 0); // Set Q flag if we had an overflow. InstEmitSaturate.SetQFlag(context); int delta = context.CodeWriter.InstructionPointer - branchIndex; context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5)); } } }