aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure/Instructions/InstEmitSimdMemory32.cs
diff options
context:
space:
mode:
Diffstat (limited to 'ARMeilleure/Instructions/InstEmitSimdMemory32.cs')
-rw-r--r--ARMeilleure/Instructions/InstEmitSimdMemory32.cs352
1 files changed, 352 insertions, 0 deletions
diff --git a/ARMeilleure/Instructions/InstEmitSimdMemory32.cs b/ARMeilleure/Instructions/InstEmitSimdMemory32.cs
new file mode 100644
index 00000000..fb9931d8
--- /dev/null
+++ b/ARMeilleure/Instructions/InstEmitSimdMemory32.cs
@@ -0,0 +1,352 @@
+using ARMeilleure.Decoders;
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.State;
+using ARMeilleure.Translation;
+
+using static ARMeilleure.Instructions.InstEmitHelper;
+using static ARMeilleure.Instructions.InstEmitMemoryHelper;
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+namespace ARMeilleure.Instructions
+{
+ static partial class InstEmit32
+ {
+ public static void Vld1(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 1, true);
+ }
+
+ public static void Vld2(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 2, true);
+ }
+
+ public static void Vld3(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 3, true);
+ }
+
+ public static void Vld4(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 4, true);
+ }
+
+ public static void Vst1(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 1, false);
+ }
+
+ public static void Vst2(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 2, false);
+ }
+
+ public static void Vst3(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 3, false);
+ }
+
+ public static void Vst4(ArmEmitterContext context)
+ {
+ EmitVStoreOrLoadN(context, 4, false);
+ }
+
+ public static void EmitVStoreOrLoadN(ArmEmitterContext context, int count, bool load)
+ {
+ if (context.CurrOp is OpCode32SimdMemSingle)
+ {
+ OpCode32SimdMemSingle op = (OpCode32SimdMemSingle)context.CurrOp;
+
+ int eBytes = 1 << op.Size;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+
+ // TODO: Check alignment.
+ int offset = 0;
+ int d = op.Vd;
+
+ for (int i = 0; i < count; i++)
+ {
+ // Write an element from a double simd register.
+ Operand address = context.Add(n, Const(offset));
+ if (eBytes == 8)
+ {
+ if (load)
+ {
+ EmitDVectorLoad(context, address, d);
+ }
+ else
+ {
+ EmitDVectorStore(context, address, d);
+ }
+ }
+ else
+ {
+ int index = ((d & 1) << (3 - op.Size)) + op.Index;
+ if (load)
+ {
+ if (op.Replicate)
+ {
+ var regs = (count > 1) ? 1 : op.Increment;
+ for (int reg = 0; reg < regs; reg++)
+ {
+ int dreg = reg + d;
+ int rIndex = ((dreg & 1) << (3 - op.Size));
+ int limit = rIndex + (1 << (3 - op.Size));
+
+ while (rIndex < limit)
+ {
+ EmitLoadSimd(context, address, GetVecA32(dreg >> 1), dreg >> 1, rIndex++, op.Size);
+ }
+ }
+ }
+ else
+ {
+ EmitLoadSimd(context, address, GetVecA32(d >> 1), d >> 1, index, op.Size);
+ }
+ }
+ else
+ {
+ EmitStoreSimd(context, address, d >> 1, index, op.Size);
+ }
+ }
+ offset += eBytes;
+ d += op.Increment;
+ }
+
+ if (op.WBack)
+ {
+ if (op.RegisterIndex)
+ {
+ Operand m = GetIntA32(context, op.Rm);
+ SetIntA32(context, op.Rn, context.Add(n, m));
+ }
+ else
+ {
+ SetIntA32(context, op.Rn, context.Add(n, Const(count * eBytes)));
+ }
+ }
+ }
+ else
+ {
+ OpCode32SimdMemPair op = (OpCode32SimdMemPair)context.CurrOp;
+
+ int eBytes = 1 << op.Size;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+ int offset = 0;
+ int d = op.Vd;
+
+ for (int reg = 0; reg < op.Regs; reg++)
+ {
+ for (int elem = 0; elem < op.Elems; elem++)
+ {
+ int elemD = d + reg;
+ for (int i = 0; i < count; i++)
+ {
+ // Write an element from a double simd register
+ // add ebytes for each element.
+ Operand address = context.Add(n, Const(offset));
+ int index = ((elemD & 1) << (3 - op.Size)) + elem;
+ if (eBytes == 8)
+ {
+ if (load)
+ {
+ EmitDVectorLoad(context, address, elemD);
+ }
+ else
+ {
+ EmitDVectorStore(context, address, elemD);
+ }
+ }
+ else
+ {
+
+ if (load)
+ {
+ EmitLoadSimd(context, address, GetVecA32(elemD >> 1), elemD >> 1, index, op.Size);
+ }
+ else
+ {
+ EmitStoreSimd(context, address, elemD >> 1, index, op.Size);
+ }
+ }
+
+ offset += eBytes;
+ elemD += op.Increment;
+ }
+ }
+ }
+
+ if (op.WBack)
+ {
+ if (op.RegisterIndex)
+ {
+ Operand m = GetIntA32(context, op.Rm);
+ SetIntA32(context, op.Rn, context.Add(n, m));
+ }
+ else
+ {
+ SetIntA32(context, op.Rn, context.Add(n, Const(count * 8 * op.Regs)));
+ }
+ }
+ }
+ }
+
+ public static void Vldm(ArmEmitterContext context)
+ {
+ OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+
+ Operand baseAddress = context.Add(n, Const(op.Offset));
+
+ bool writeBack = op.PostOffset != 0;
+
+ if (writeBack)
+ {
+ SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
+ }
+
+ int range = op.RegisterRange;
+
+ int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
+ int offset = 0;
+ int byteSize = 4;
+
+ for (int num = 0; num < range; num++, sReg++)
+ {
+ Operand address = context.Add(baseAddress, Const(offset));
+ Operand vec = GetVecA32(sReg >> 2);
+
+ EmitLoadSimd(context, address, vec, sReg >> 2, sReg & 3, WordSizeLog2);
+ offset += byteSize;
+ }
+ }
+
+ public static void Vstm(ArmEmitterContext context)
+ {
+ OpCode32SimdMemMult op = (OpCode32SimdMemMult)context.CurrOp;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+
+ Operand baseAddress = context.Add(n, Const(op.Offset));
+
+ bool writeBack = op.PostOffset != 0;
+
+ if (writeBack)
+ {
+ SetIntA32(context, op.Rn, context.Add(n, Const(op.PostOffset)));
+ }
+
+ int offset = 0;
+
+ int range = op.RegisterRange;
+ int sReg = (op.DoubleWidth) ? (op.Vd << 1) : op.Vd;
+ int byteSize = 4;
+
+ for (int num = 0; num < range; num++, sReg++)
+ {
+ Operand address = context.Add(baseAddress, Const(offset));
+
+ EmitStoreSimd(context, address, sReg >> 2, sReg & 3, WordSizeLog2);
+
+ offset += byteSize;
+ }
+ }
+
+ public static void Vldr(ArmEmitterContext context)
+ {
+ EmitVLoadOrStore(context, AccessType.Load);
+ }
+
+ public static void Vstr(ArmEmitterContext context)
+ {
+ EmitVLoadOrStore(context, AccessType.Store);
+ }
+
+ private static void EmitDVectorStore(ArmEmitterContext context, Operand address, int vecD)
+ {
+ int vecQ = vecD >> 1;
+ int vecSElem = (vecD & 1) << 1;
+ Operand lblBigEndian = Label();
+ Operand lblEnd = Label();
+
+ context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
+
+ EmitStoreSimd(context, address, vecQ, vecSElem, WordSizeLog2);
+ EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem | 1, WordSizeLog2);
+
+ context.Branch(lblEnd);
+
+ context.MarkLabel(lblBigEndian);
+
+ EmitStoreSimd(context, address, vecQ, vecSElem | 1, WordSizeLog2);
+ EmitStoreSimd(context, context.Add(address, Const(4)), vecQ, vecSElem, WordSizeLog2);
+
+ context.MarkLabel(lblEnd);
+ }
+
+ private static void EmitDVectorLoad(ArmEmitterContext context, Operand address, int vecD)
+ {
+ int vecQ = vecD >> 1;
+ int vecSElem = (vecD & 1) << 1;
+ Operand vec = GetVecA32(vecQ);
+
+ Operand lblBigEndian = Label();
+ Operand lblEnd = Label();
+
+ context.BranchIfTrue(lblBigEndian, GetFlag(PState.EFlag));
+
+ EmitLoadSimd(context, address, vec, vecQ, vecSElem, WordSizeLog2);
+ EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem | 1, WordSizeLog2);
+
+ context.Branch(lblEnd);
+
+ context.MarkLabel(lblBigEndian);
+
+ EmitLoadSimd(context, address, vec, vecQ, vecSElem | 1, WordSizeLog2);
+ EmitLoadSimd(context, context.Add(address, Const(4)), vec, vecQ, vecSElem, WordSizeLog2);
+
+ context.MarkLabel(lblEnd);
+ }
+
+ private static void EmitVLoadOrStore(ArmEmitterContext context, AccessType accType)
+ {
+ OpCode32SimdMemImm op = (OpCode32SimdMemImm)context.CurrOp;
+
+ Operand n = context.Copy(GetIntA32(context, op.Rn));
+ Operand m = GetMemM(context, setCarry: false);
+
+ Operand address = op.Add
+ ? context.Add(n, m)
+ : context.Subtract(n, m);
+
+ int size = op.Size;
+
+ if ((accType & AccessType.Load) != 0)
+ {
+ if (size == DWordSizeLog2)
+ {
+ EmitDVectorLoad(context, address, op.Vd);
+ }
+ else
+ {
+ Operand vec = GetVecA32(op.Vd >> 2);
+ EmitLoadSimd(context, address, vec, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
+ }
+ }
+ else
+ {
+ if (size == DWordSizeLog2)
+ {
+ EmitDVectorStore(context, address, op.Vd);
+ }
+ else
+ {
+ EmitStoreSimd(context, address, op.Vd >> 2, (op.Vd & 3) << (2 - size), size);
+ }
+ }
+ }
+ }
+}