aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs')
-rw-r--r--src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs1513
1 files changed, 1513 insertions, 0 deletions
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs
new file mode 100644
index 00000000..dce6556e
--- /dev/null
+++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitNeonCommon.cs
@@ -0,0 +1,1513 @@
+
+using Ryujinx.Cpu.LightningJit.CodeGen;
+using System;
+using System.Diagnostics;
+
+namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
+{
+ static class InstEmitNeonCommon
+ {
+ public static ScopedRegister MoveScalarToSide(CodeGenContext context, uint srcReg, bool isFP32, bool forceAllocation = false)
+ {
+ int shift = isFP32 ? 2 : 1;
+ uint mask = isFP32 ? 3u : 1u;
+ uint elt = srcReg & mask;
+
+ if (elt == 0 && !forceAllocation)
+ {
+ return new ScopedRegister(context.RegisterAllocator, context.RegisterAllocator.RemapFpRegister((int)(srcReg >> shift), isFP32), false);
+ }
+
+ Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> shift));
+ ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(isFP32);
+
+ uint imm5 = GetImm5ForElementIndex(elt, isFP32);
+
+ context.Arm64Assembler.DupEltScalarFromElement(tempRegister.Operand, source, imm5);
+
+ return tempRegister;
+ }
+
+ public static ScopedRegister Move16BitScalarToSide(CodeGenContext context, uint srcReg, bool top = false)
+ {
+ uint elt = srcReg & 3;
+
+ Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> 2));
+ ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempFpRegisterScoped(true);
+
+ uint imm5 = GetImm5ForElementIndex((elt << 1) | (top ? 1u : 0u), 1);
+
+ context.Arm64Assembler.DupEltScalarFromElement(tempRegister.Operand, source, imm5);
+
+ return tempRegister;
+ }
+
+ public static void MoveScalarToSide(CodeGenContext context, Operand dest, uint srcReg, bool isFP32)
+ {
+ int shift = isFP32 ? 2 : 1;
+ uint mask = isFP32 ? 3u : 1u;
+ uint elt = srcReg & mask;
+
+ Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> shift));
+
+ uint imm5 = GetImm5ForElementIndex(elt, isFP32);
+
+ context.Arm64Assembler.DupEltScalarFromElement(dest, source, imm5);
+ }
+
+ public static ScopedRegister MoveScalarToSideIntoGpr(CodeGenContext context, uint srcReg, bool isFP32)
+ {
+ int shift = isFP32 ? 2 : 1;
+ uint mask = isFP32 ? 3u : 1u;
+ uint elt = srcReg & mask;
+
+ Operand source = context.RegisterAllocator.RemapSimdRegister((int)(srcReg >> shift));
+ ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
+
+ context.Arm64Assembler.Umov(tempRegister.Operand, source, (int)elt, isFP32 ? 2 : 3);
+
+ return tempRegister;
+ }
+
+ public static void InsertResult(CodeGenContext context, Operand source, uint dstReg, bool isFP32)
+ {
+ int shift = isFP32 ? 2 : 1;
+ uint mask = isFP32 ? 3u : 1u;
+ uint elt = dstReg & mask;
+
+ uint imm5 = GetImm5ForElementIndex(elt, isFP32);
+
+ Operand dest = context.RegisterAllocator.RemapSimdRegister((int)(dstReg >> shift));
+
+ context.Arm64Assembler.InsElt(dest, source, 0, imm5);
+ }
+
+ public static void Insert16BitResult(CodeGenContext context, Operand source, uint dstReg, bool top = false)
+ {
+ uint elt = dstReg & 3u;
+
+ uint imm5 = GetImm5ForElementIndex((elt << 1) | (top ? 1u : 0u), 1);
+
+ Operand dest = context.RegisterAllocator.RemapSimdRegister((int)(dstReg >> 2));
+
+ context.Arm64Assembler.InsElt(dest, source, 0, imm5);
+ }
+
+ public static void InsertResultFromGpr(CodeGenContext context, Operand source, uint dstReg, bool isFP32)
+ {
+ int shift = isFP32 ? 2 : 1;
+ uint mask = isFP32 ? 3u : 1u;
+ uint elt = dstReg & mask;
+
+ uint imm5 = GetImm5ForElementIndex(elt, isFP32);
+
+ Operand dest = context.RegisterAllocator.RemapSimdRegister((int)(dstReg >> shift));
+
+ context.Arm64Assembler.InsGen(dest, source, imm5);
+ }
+
+ public static uint GetImm5ForElementIndex(uint elt, bool isFP32)
+ {
+ return isFP32 ? (4u | (elt << 3)) : (8u | (elt << 4));
+ }
+
+ public static uint GetImm5ForElementIndex(uint elt, uint size)
+ {
+ return (1u << (int)size) | (elt << ((int)size + 1));
+ }
+
+ public static void EmitScalarUnaryF(CodeGenContext context, uint rd, uint rm, uint size, Action<Operand, Operand, uint> action)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ action(tempRegister.Operand, rmReg.Operand, size ^ 2u);
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitScalarUnaryF(CodeGenContext context, uint rd, uint rm, uint size, Action<Operand, Operand, uint> action, Action<Operand, Operand> actionHalf)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ if (size == 1)
+ {
+ actionHalf(tempRegister.Operand, rmReg.Operand);
+ }
+ else
+ {
+ action(tempRegister.Operand, rmReg.Operand, size & 1);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitScalarUnaryToGprTempF(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint size,
+ uint sf,
+ Action<Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs);
+
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempGprRegisterScoped();
+
+ action(tempRegister.Operand, rmReg.Operand, size ^ 2u, sf);
+
+ InsertResultFromGpr(context, tempRegister.Operand, rd, sf == 0);
+ }
+
+ public static void EmitScalarUnaryFromGprTempF(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint size,
+ uint sf,
+ Action<Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ using ScopedRegister rmReg = MoveScalarToSideIntoGpr(context, rm, sf == 0);
+
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ action(tempRegister.Operand, rmReg.Operand, size ^ 2u, sf);
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitScalarUnaryFixedF(CodeGenContext context, uint rd, uint rm, uint fbits, uint size, bool is16Bit, Action<Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ (uint immb, uint immh) = GetImmbImmh(fbits, size);
+
+ using ScopedRegister rmReg = is16Bit ? Move16BitScalarToSide(context, rm) : MoveScalarToSide(context, rm, singleRegs);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ action(tempRegister.Operand, rmReg.Operand, immb, immh);
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitScalarBinaryF(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint> action)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, singleRegs);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg, rmReg);
+
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size ^ 2u);
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitScalarBinaryShift(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint shift,
+ uint size,
+ bool isShl,
+ Action<Operand, Operand, uint, uint> action)
+ {
+ bool singleRegs = size != 3;
+
+ (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl);
+
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ action(tempRegister.Operand, rmReg.Operand, immb, immh);
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitScalarTernaryRdF(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint> action)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, singleRegs);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, singleRegs);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs);
+
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size ^ 2u);
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitScalarTernaryRdF(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ Action<Operand, Operand, Operand, Operand, uint> action)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ using ScopedRegister rdReg = MoveScalarToSide(context, rd, singleRegs);
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, singleRegs);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rdReg, rnReg, rmReg);
+
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, rdReg.Operand, size ^ 2u);
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitScalarTernaryMulNegRdF(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ bool negD,
+ bool negProduct)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+
+ bool singleRegs = size != 3;
+
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, singleRegs);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, singleRegs);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, singleRegs);
+
+ using ScopedRegister productRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ uint ftype = size ^ 2u;
+
+ context.Arm64Assembler.FmulFloat(productRegister.Operand, rnReg.Operand, rmReg.Operand, ftype);
+
+ if (negD)
+ {
+ context.Arm64Assembler.FnegFloat(tempRegister.Operand, tempRegister.Operand, ftype);
+ }
+
+ if (negProduct)
+ {
+ context.Arm64Assembler.FnegFloat(productRegister.Operand, productRegister.Operand, ftype);
+ }
+
+ context.Arm64Assembler.FaddFloat(tempRegister.Operand, tempRegister.Operand, productRegister.Operand, ftype);
+
+ InsertResult(context, tempRegister.Operand, rd, singleRegs);
+ }
+
+ public static void EmitVectorUnary(CodeGenContext context, uint rd, uint rm, Action<Operand, Operand> action)
+ {
+ Debug.Assert(((rd | rm) & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rmOperand);
+ }
+
+ public static void EmitVectorUnary(CodeGenContext context, uint rd, uint rm, uint q, Action<Operand, Operand, uint> action)
+ {
+ if (q == 0)
+ {
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ action(tempRegister.Operand, rmReg.Operand, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rmOperand, q);
+ }
+ }
+
+ public static void EmitVectorUnary(CodeGenContext context, uint rd, uint rm, uint size, uint q, Action<Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert(size < 3);
+
+ if (q == 0)
+ {
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ action(tempRegister.Operand, rmReg.Operand, size, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rmOperand, size, q);
+ }
+ }
+
+ public static void EmitVectorUnaryLong(CodeGenContext context, uint rd, uint rm, uint size, Action<Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert((rd & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rm & 1;
+
+ action(rdOperand, rmOperand, size, q);
+ }
+
+ public static void EmitVectorUnaryNarrow(CodeGenContext context, uint rd, uint rm, uint size, Action<Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert((rm & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rd & 1;
+
+ if (q == 0)
+ {
+ // Writing to the lower half would clear the higher bits, we don't want that, so use a temp register and move the element.
+
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ action(tempRegister.Operand, rmOperand, size, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ action(rdOperand, rmOperand, size, q);
+ }
+ }
+
+ public static void EmitVectorBinary(CodeGenContext context, uint rd, uint rn, uint rm, Action<Operand, Operand, Operand> action)
+ {
+ Debug.Assert(((rd | rn | rm) & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rnOperand, rmOperand);
+ }
+
+ public static void EmitVectorBinary(CodeGenContext context, uint rd, uint rn, uint rm, uint q, Action<Operand, Operand, Operand, uint> action)
+ {
+ if (q == 0)
+ {
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg, rmReg);
+
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rnOperand, rmOperand, q);
+ }
+ }
+
+ public static void EmitVectorBinary(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ uint q,
+ Action<Operand, Operand, Operand, uint, uint> action,
+ Action<Operand, Operand, Operand, uint> actionScalar)
+ {
+ Debug.Assert(size <= 3);
+
+ if (q == 0)
+ {
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg, rmReg);
+
+ if (size == 3)
+ {
+ actionScalar(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size);
+ }
+ else
+ {
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rnOperand, rmOperand, size, q);
+ }
+ }
+
+ public static void EmitVectorBinaryRd(CodeGenContext context, uint rd, uint rm, uint size, uint q, Action<Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert(size < 3);
+
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ action(tempRegister.Operand, rmReg.Operand, size, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+
+ public static void EmitVectorBinaryShift(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint shift,
+ uint size,
+ uint q,
+ bool isShl,
+ Action<Operand, Operand, uint, uint, uint> action,
+ Action<Operand, Operand, uint, uint> actionScalar)
+ {
+ (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl);
+
+ if (q == 0)
+ {
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ if (size == 3)
+ {
+ actionScalar(tempRegister.Operand, rmReg.Operand, immb, immh);
+ }
+ else
+ {
+ action(tempRegister.Operand, rmReg.Operand, immb, immh, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rmOperand, immb, immh, q);
+ }
+ }
+
+ public static void EmitVectorBinaryLong(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert((rd & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+
+ if ((rn & 1) == (rm & 1))
+ {
+ // Both inputs are on the same side of the vector, so we can use the variant that selects the half.
+
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rn & 1;
+
+ action(rdOperand, rnOperand, rmOperand, size, q);
+ }
+ else
+ {
+ // Inputs are on different sides of the vector, we have to move them.
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ action(rdOperand, rnReg.Operand, rmReg.Operand, size, 0);
+ }
+ }
+
+ public static void EmitVectorBinaryLongShift(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint shift,
+ uint size,
+ bool isShl,
+ Action<Operand, Operand, uint, uint, uint> action)
+ {
+ (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl);
+
+ Debug.Assert((rd & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+
+ uint q = rn & 1;
+
+ action(rdOperand, rnOperand, immb, immh, q);
+ }
+
+ public static void EmitVectorBinaryLongByScalar(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action)
+ {
+ Debug.Assert((rd & 1) == 0);
+
+ (uint h, uint l, uint m) = GetIndexForReg(ref rm, size);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rn & 1;
+
+ action(rdOperand, rnOperand, h, rmOperand, m, l, size, q);
+ }
+
+ public static void EmitVectorBinaryNarrow(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ Action<Operand, Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert((rn & 1) == 0);
+ Debug.Assert((rm & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rd & 1;
+
+ if (q == 0)
+ {
+ // Writing to the lower half would clear the higher bits, we don't want that, so use a temp register and move the element.
+
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ action(tempRegister.Operand, rnOperand, rmOperand, size, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ action(rdOperand, rnOperand, rmOperand, size, q);
+ }
+ }
+
+ public static void EmitVectorBinaryNarrowShift(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint shift,
+ uint size,
+ bool isShl,
+ Action<Operand, Operand, uint, uint, uint> action)
+ {
+ (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl);
+
+ Debug.Assert((rm & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rd & 1;
+
+ if (q == 0)
+ {
+ // Writing to the lower half would clear the higher bits, we don't want that, so use a temp register and move the element.
+
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ action(tempRegister.Operand, rmOperand, immb, immh, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ action(rdOperand, rmOperand, immb, immh, q);
+ }
+ }
+
+ public static void EmitVectorBinaryWide(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint, uint> action)
+ {
+ Debug.Assert(((rd | rn) & 1) == 0);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rm & 1;
+
+ action(rdOperand, rnOperand, rmOperand, size, q);
+ }
+
+ public static void EmitVectorBinaryByScalar(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ uint q,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action)
+ {
+ EmitVectorByScalarCore(context, rd, rn, rm, size, q, action, isTernary: false);
+ }
+
+ public static void EmitVectorTernaryRd(CodeGenContext context, uint rd, uint rn, uint rm, uint q, Action<Operand, Operand, Operand, uint> action)
+ {
+ if (q == 0)
+ {
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rnOperand, rmOperand, q);
+ }
+ }
+
+ public static void EmitVectorTernaryRd(CodeGenContext context, uint rd, uint rn, uint rm, uint size, uint q, Action<Operand, Operand, Operand, uint, uint> action)
+ {
+ if (q == 0)
+ {
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, size, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rnOperand, rmOperand, size, q);
+ }
+ }
+
+ public static void EmitVectorTernaryRdLong(CodeGenContext context, uint rd, uint rn, uint rm, uint size, Action<Operand, Operand, Operand, uint, uint> action)
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+
+ if ((rn & 1) == (rm & 1))
+ {
+ // Both inputs are on the same side of the vector, so we can use the variant that selects the half.
+
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rn & 1;
+
+ action(rdOperand, rnOperand, rmOperand, size, q);
+ }
+ else
+ {
+ // Inputs are on different sides of the vector, we have to move them.
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ action(rdOperand, rnReg.Operand, rmReg.Operand, size, 0);
+ }
+ }
+
+ public static void EmitVectorTernaryRdLongByScalar(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action)
+ {
+ (uint h, uint l, uint m) = GetIndexForReg(ref rm, size);
+
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ uint q = rn & 1;
+
+ action(rdOperand, rnOperand, h, rmOperand, m, l, size, q);
+ }
+
+ public static void EmitVectorTernaryRdShift(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint shift,
+ uint size,
+ uint q,
+ bool isShl,
+ Action<Operand, Operand, uint, uint, uint> action,
+ Action<Operand, Operand, uint, uint> actionScalar)
+ {
+ (uint immb, uint immh) = GetImmbImmhForShift(shift, size, isShl);
+
+ if (q == 0)
+ {
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ if (size == 3)
+ {
+ actionScalar(tempRegister.Operand, rmReg.Operand, immb, immh);
+ }
+ else
+ {
+ action(tempRegister.Operand, rmReg.Operand, immb, immh, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rmOperand, immb, immh, q);
+ }
+ }
+
+ public static void EmitVectorTernaryRdByScalar(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ uint q,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action)
+ {
+ EmitVectorByScalarCore(context, rd, rn, rm, size, q, action, isTernary: true);
+ }
+
+ private static void EmitVectorByScalarCore(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ uint q,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action,
+ bool isTernary)
+ {
+ (uint h, uint l, uint m) = GetIndexForReg(ref rm, size);
+
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ if (q == 0)
+ {
+ if (isTernary)
+ {
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+
+ action(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, size, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg);
+
+ action(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, size, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+
+ action(rdOperand, rnOperand, h, rmOperand, m, l, size, q);
+ }
+ }
+
+ public static void EmitVectorUnaryF(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint sz,
+ uint q,
+ Action<Operand, Operand, uint, uint> action,
+ Action<Operand, Operand, uint> actionHalf)
+ {
+ Debug.Assert(sz == 0 || sz == 1);
+
+ if (q == 0)
+ {
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ if (sz == 1)
+ {
+ actionHalf(tempRegister.Operand, rmReg.Operand, q);
+ }
+ else
+ {
+ action(tempRegister.Operand, rmReg.Operand, 0, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ if (sz == 1)
+ {
+ actionHalf(rdOperand, rmOperand, q);
+ }
+ else
+ {
+ action(rdOperand, rmOperand, 0, q);
+ }
+ }
+ }
+
+ public static void EmitVectorUnaryAnyF(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint size,
+ uint q,
+ Action<Operand, Operand, uint, uint> action,
+ Action<Operand, Operand, uint> actionHalf)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+ Debug.Assert(size != 3 || q == 1);
+
+ if (q == 0)
+ {
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ if (size == 1)
+ {
+ actionHalf(tempRegister.Operand, rmReg.Operand, q);
+ }
+ else
+ {
+ action(tempRegister.Operand, rmReg.Operand, size ^ 2u, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ if (size == 1)
+ {
+ actionHalf(rdOperand, rmOperand, q);
+ }
+ else
+ {
+ action(rdOperand, rmOperand, size ^ 2u, q);
+ }
+ }
+ }
+
+ public static void EmitVectorUnaryFixedAnyF(
+ CodeGenContext context,
+ uint rd,
+ uint rm,
+ uint fbits,
+ uint size,
+ uint q,
+ Action<Operand, Operand, uint, uint, uint> action)
+ {
+ Debug.Assert(size == 1 || size == 2 || size == 3);
+ Debug.Assert(size != 3 || q == 1);
+
+ (uint immb, uint immh) = GetImmbImmh(fbits, size);
+
+ if (q == 0)
+ {
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rmReg);
+
+ action(tempRegister.Operand, rmReg.Operand, immb, immh, q);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ action(rdOperand, rmOperand, immb, immh, q);
+ }
+ }
+
+ public static void EmitVectorBinaryF(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint sz,
+ uint q,
+ Action<Operand, Operand, Operand, uint, uint> action,
+ Action<Operand, Operand, Operand, uint> actionHalf)
+ {
+ Debug.Assert(sz == 0 || sz == 1);
+
+ if (q == 0)
+ {
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg, rmReg);
+
+ if (sz == 1)
+ {
+ actionHalf(tempRegister.Operand, rnReg.Operand, rmReg.Operand, q);
+ }
+ else
+ {
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, 0, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ if (sz == 1)
+ {
+ actionHalf(rdOperand, rnOperand, rmOperand, q);
+ }
+ else
+ {
+ action(rdOperand, rnOperand, rmOperand, 0, q);
+ }
+ }
+ }
+
+ public static void EmitVectorBinaryByScalarAnyF(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ uint q,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint> actionHalf)
+ {
+ EmitVectorByScalarAnyFCore(context, rd, rn, rm, size, q, action, actionHalf, isTernary: false);
+ }
+
+ public static void EmitVectorTernaryRdF(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint sz,
+ uint q,
+ Action<Operand, Operand, Operand, uint, uint> action,
+ Action<Operand, Operand, Operand, uint> actionHalf)
+ {
+ Debug.Assert(sz == 0 || sz == 1);
+
+ if (q == 0)
+ {
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ if (sz == 1)
+ {
+ actionHalf(tempRegister.Operand, rnReg.Operand, rmReg.Operand, q);
+ }
+ else
+ {
+ action(tempRegister.Operand, rnReg.Operand, rmReg.Operand, 0, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ if (sz == 1)
+ {
+ actionHalf(rdOperand, rnOperand, rmOperand, q);
+ }
+ else
+ {
+ action(rdOperand, rnOperand, rmOperand, 0, q);
+ }
+ }
+ }
+
+ public static void EmitVectorTernaryMulNegRdF(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint sz,
+ uint q,
+ bool negProduct)
+ {
+ Debug.Assert(sz == 0 || sz == 1);
+
+ if (q == 0)
+ {
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+ using ScopedRegister rmReg = MoveScalarToSide(context, rm, false);
+
+ EmitMulNegVector(context, tempRegister.Operand, rnReg.Operand, rmReg.Operand, sz, q, negProduct);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ EmitMulNegVector(context, rdOperand, rnOperand, rmOperand, sz, q, negProduct);
+ }
+ }
+
+ private static void EmitMulNegVector(
+ CodeGenContext context,
+ Operand rd,
+ Operand rn,
+ Operand rm,
+ uint sz,
+ uint q,
+ bool negProduct)
+ {
+ using ScopedRegister productRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ if (sz == 1)
+ {
+ context.Arm64Assembler.FmulVecHalf(productRegister.Operand, rn, rm, q);
+
+ if (negProduct)
+ {
+ context.Arm64Assembler.FnegHalf(productRegister.Operand, productRegister.Operand, q);
+ }
+
+ context.Arm64Assembler.FaddHalf(rd, rd, productRegister.Operand, q);
+ }
+ else
+ {
+ context.Arm64Assembler.FmulVecSingleAndDouble(productRegister.Operand, rn, rm, 0, q);
+
+ if (negProduct)
+ {
+ context.Arm64Assembler.FnegSingleAndDouble(productRegister.Operand, productRegister.Operand, 0, q);
+ }
+
+ context.Arm64Assembler.FaddSingleAndDouble(rd, rd, productRegister.Operand, 0, q);
+ }
+ }
+
+ public static void EmitVectorTernaryRdByScalarAnyF(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ uint q,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint> actionHalf)
+ {
+ EmitVectorByScalarAnyFCore(context, rd, rn, rm, size, q, action, actionHalf, isTernary: true);
+ }
+
+ private static void EmitVectorByScalarAnyFCore(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ uint q,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint, uint> action,
+ Action<Operand, Operand, uint, Operand, uint, uint, uint> actionHalf,
+ bool isTernary)
+ {
+ (uint h, uint l, uint m) = GetIndexForReg(ref rm, size);
+
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ if (q == 0)
+ {
+ if (isTernary)
+ {
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+
+ if (size == 1)
+ {
+ actionHalf(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, q);
+ }
+ else
+ {
+ action(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, 0, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+
+ using ScopedRegister tempRegister = PickSimdRegister(context.RegisterAllocator, rnReg);
+
+ if (size == 1)
+ {
+ actionHalf(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, q);
+ }
+ else
+ {
+ action(tempRegister.Operand, rnReg.Operand, h, rmOperand, m, l, 0, q);
+ }
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+
+ if (size == 1)
+ {
+ actionHalf(rdOperand, rnOperand, h, rmOperand, m, l, q);
+ }
+ else
+ {
+ action(rdOperand, rnOperand, h, rmOperand, m, l, 0, q);
+ }
+ }
+ }
+
+ public static void EmitVectorTernaryMulNegRdByScalarAnyF(
+ CodeGenContext context,
+ uint rd,
+ uint rn,
+ uint rm,
+ uint size,
+ uint q,
+ bool negProduct)
+ {
+ (uint h, uint l, uint m) = GetIndexForReg(ref rm, size);
+
+ Operand rmOperand = context.RegisterAllocator.RemapSimdRegister((int)(rm >> 1));
+
+ if (q == 0)
+ {
+ using ScopedRegister tempRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ MoveScalarToSide(context, tempRegister.Operand, rd, false);
+
+ using ScopedRegister rnReg = MoveScalarToSide(context, rn, false);
+
+ EmitMulNegVectorByScalar(context, tempRegister.Operand, rnReg.Operand, rmOperand, h, l, m, size, q, negProduct);
+
+ InsertResult(context, tempRegister.Operand, rd, false);
+ }
+ else
+ {
+ Operand rdOperand = context.RegisterAllocator.RemapSimdRegister((int)(rd >> 1));
+ Operand rnOperand = context.RegisterAllocator.RemapSimdRegister((int)(rn >> 1));
+
+ EmitMulNegVectorByScalar(context, rdOperand, rnOperand, rmOperand, h, l, m, size, q, negProduct);
+ }
+ }
+
+ private static void EmitMulNegVectorByScalar(
+ CodeGenContext context,
+ Operand rd,
+ Operand rn,
+ Operand rm,
+ uint h,
+ uint l,
+ uint m,
+ uint sz,
+ uint q,
+ bool negProduct)
+ {
+ using ScopedRegister productRegister = context.RegisterAllocator.AllocateTempSimdRegisterScoped();
+
+ if (sz == 1)
+ {
+ context.Arm64Assembler.FmulElt2regElementHalf(productRegister.Operand, rn, h, rm, m, l, q);
+
+ if (negProduct)
+ {
+ context.Arm64Assembler.FnegHalf(productRegister.Operand, productRegister.Operand, q);
+ }
+
+ context.Arm64Assembler.FaddHalf(rd, rd, productRegister.Operand, q);
+ }
+ else
+ {
+ context.Arm64Assembler.FmulElt2regElementSingleAndDouble(productRegister.Operand, rn, h, rm, m, l, 0, q);
+
+ if (negProduct)
+ {
+ context.Arm64Assembler.FnegSingleAndDouble(productRegister.Operand, productRegister.Operand, 0, q);
+ }
+
+ context.Arm64Assembler.FaddSingleAndDouble(rd, rd, productRegister.Operand, 0, q);
+ }
+ }
+
+ private static (uint, uint, uint) GetIndexForReg(ref uint reg, uint size)
+ {
+ int shift = (int)(size + 2);
+ uint index = reg >> shift;
+ reg &= (1u << shift) - 1;
+ index |= (reg & 1) << (5 - shift);
+
+ uint h, l, m;
+
+ if (size == 1)
+ {
+ Debug.Assert((index >> 3) == 0);
+
+ m = index & 1;
+ l = (index >> 1) & 1;
+ h = index >> 2;
+ }
+ else
+ {
+ Debug.Assert(size == 2);
+ Debug.Assert((index >> 2) == 0);
+
+ m = 0;
+ l = index & 1;
+ h = (index >> 1) & 1;
+ }
+
+ return (h, l, m);
+ }
+
+ private static (uint, uint) GetImmbImmh(uint value, uint size)
+ {
+ Debug.Assert(value > 0 && value <= (8u << (int)size));
+
+ uint imm = (8u << (int)size) | ((8u << (int)size) - value);
+
+ Debug.Assert((imm >> 7) == 0);
+
+ uint immb = imm & 7;
+ uint immh = imm >> 3;
+
+ return (immb, immh);
+ }
+
+ public static (uint, uint) GetImmbImmhForShift(uint value, uint size, bool isShl)
+ {
+ if (isShl)
+ {
+ Debug.Assert(value >= 0 && value < (8u << (int)size));
+
+ uint imm = (8u << (int)size) | (value & (0x3fu >> (int)(3 - size)));
+
+ Debug.Assert((imm >> 7) == 0);
+
+ uint immb = imm & 7;
+ uint immh = imm >> 3;
+
+ return (immb, immh);
+ }
+ else
+ {
+ return GetImmbImmh(value, size);
+ }
+ }
+
+ public static uint GetSizeFromImm6(uint imm6)
+ {
+ if ((imm6 & 0b100000) != 0)
+ {
+ return 2;
+ }
+ else if ((imm6 & 0b10000) != 0)
+ {
+ return 1;
+ }
+ else
+ {
+ Debug.Assert((imm6 & 0b1000) != 0);
+
+ return 0;
+ }
+ }
+
+ public static uint GetSizeFromImm7(uint imm7)
+ {
+ if ((imm7 & 0b1000000) != 0)
+ {
+ return 3;
+ }
+ else if ((imm7 & 0b100000) != 0)
+ {
+ return 2;
+ }
+ else if ((imm7 & 0b10000) != 0)
+ {
+ return 1;
+ }
+ else
+ {
+ Debug.Assert((imm7 & 0b1000) != 0);
+
+ return 0;
+ }
+ }
+
+ public static ScopedRegister PickSimdRegister(RegisterAllocator registerAllocator, ScopedRegister option1)
+ {
+ if (option1.IsAllocated)
+ {
+ return option1;
+ }
+
+ return registerAllocator.AllocateTempSimdRegisterScoped();
+ }
+
+ public static ScopedRegister PickSimdRegister(RegisterAllocator registerAllocator, ScopedRegister option1, ScopedRegister option2)
+ {
+ if (option1.IsAllocated)
+ {
+ return option1;
+ }
+ else if (option2.IsAllocated)
+ {
+ return option2;
+ }
+
+ return registerAllocator.AllocateTempSimdRegisterScoped();
+ }
+
+ public static ScopedRegister PickSimdRegister(RegisterAllocator registerAllocator, ScopedRegister option1, ScopedRegister option2, ScopedRegister option3)
+ {
+ if (option1.IsAllocated)
+ {
+ return option1;
+ }
+ else if (option2.IsAllocated)
+ {
+ return option2;
+ }
+ else if (option3.IsAllocated)
+ {
+ return option3;
+ }
+
+ return registerAllocator.AllocateTempSimdRegisterScoped();
+ }
+ }
+}