/ src / ARMeilleure / Instructions / InstEmitMemoryEx.cs
InstEmitMemoryEx.cs
  1  using ARMeilleure.Decoders;
  2  using ARMeilleure.IntermediateRepresentation;
  3  using ARMeilleure.Translation;
  4  using System;
  5  using System.Diagnostics;
  6  using static ARMeilleure.Instructions.InstEmitHelper;
  7  using static ARMeilleure.Instructions.InstEmitMemoryExHelper;
  8  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  9  
 10  namespace ARMeilleure.Instructions
 11  {
 12      static partial class InstEmit
 13      {
 14          [Flags]
 15          private enum AccessType
 16          {
 17              None = 0,
 18              Ordered = 1,
 19              Exclusive = 2,
 20              OrderedEx = Ordered | Exclusive,
 21          }
 22  
 23          public static void Clrex(ArmEmitterContext context)
 24          {
 25              EmitClearExclusive(context);
 26          }
 27  
 28          public static void Csdb(ArmEmitterContext context)
 29          {
 30              // Execute as no-op.
 31          }
 32  
 33          public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
 34          public static void Dsb(ArmEmitterContext context) => EmitBarrier(context);
 35  
 36          public static void Ldar(ArmEmitterContext context) => EmitLdr(context, AccessType.Ordered);
 37          public static void Ldaxr(ArmEmitterContext context) => EmitLdr(context, AccessType.OrderedEx);
 38          public static void Ldxr(ArmEmitterContext context) => EmitLdr(context, AccessType.Exclusive);
 39          public static void Ldxp(ArmEmitterContext context) => EmitLdp(context, AccessType.Exclusive);
 40          public static void Ldaxp(ArmEmitterContext context) => EmitLdp(context, AccessType.OrderedEx);
 41  
 42          private static void EmitLdr(ArmEmitterContext context, AccessType accType)
 43          {
 44              EmitLoadEx(context, accType, pair: false);
 45          }
 46  
 47          private static void EmitLdp(ArmEmitterContext context, AccessType accType)
 48          {
 49              EmitLoadEx(context, accType, pair: true);
 50          }
 51  
 52          private static void EmitLoadEx(ArmEmitterContext context, AccessType accType, bool pair)
 53          {
 54              OpCodeMemEx op = (OpCodeMemEx)context.CurrOp;
 55  
 56              bool ordered = (accType & AccessType.Ordered) != 0;
 57              bool exclusive = (accType & AccessType.Exclusive) != 0;
 58  
 59              if (ordered)
 60              {
 61                  EmitBarrier(context);
 62              }
 63  
 64              Operand address = context.Copy(GetIntOrSP(context, op.Rn));
 65  
 66              if (pair)
 67              {
 68                  // Exclusive loads should be atomic. For pairwise loads, we need to
 69                  // read all the data at once. For a 32-bits pairwise load, we do a
 70                  // simple 64-bits load, for a 128-bits load, we need to call a special
 71                  // method to read 128-bits atomically.
 72                  if (op.Size == 2)
 73                  {
 74                      Operand value = EmitLoadExclusive(context, address, exclusive, 3);
 75  
 76                      Operand valueLow = context.ConvertI64ToI32(value);
 77  
 78                      valueLow = context.ZeroExtend32(OperandType.I64, valueLow);
 79  
 80                      Operand valueHigh = context.ShiftRightUI(value, Const(32));
 81  
 82                      SetIntOrZR(context, op.Rt, valueLow);
 83                      SetIntOrZR(context, op.Rt2, valueHigh);
 84                  }
 85                  else if (op.Size == 3)
 86                  {
 87                      Operand value = EmitLoadExclusive(context, address, exclusive, 4);
 88  
 89                      Operand valueLow = context.VectorExtract(OperandType.I64, value, 0);
 90                      Operand valueHigh = context.VectorExtract(OperandType.I64, value, 1);
 91  
 92                      SetIntOrZR(context, op.Rt, valueLow);
 93                      SetIntOrZR(context, op.Rt2, valueHigh);
 94                  }
 95                  else
 96                  {
 97                      throw new InvalidOperationException($"Invalid load size of {1 << op.Size} bytes.");
 98                  }
 99              }
100              else
101              {
102                  // 8, 16, 32 or 64-bits (non-pairwise) load.
103                  Operand value = EmitLoadExclusive(context, address, exclusive, op.Size);
104  
105                  SetIntOrZR(context, op.Rt, value);
106              }
107          }
108  
109          public static void Prfm(ArmEmitterContext context)
110          {
111              // Memory Prefetch, execute as no-op.
112          }
113  
114          public static void Stlr(ArmEmitterContext context) => EmitStr(context, AccessType.Ordered);
115          public static void Stlxr(ArmEmitterContext context) => EmitStr(context, AccessType.OrderedEx);
116          public static void Stxr(ArmEmitterContext context) => EmitStr(context, AccessType.Exclusive);
117          public static void Stxp(ArmEmitterContext context) => EmitStp(context, AccessType.Exclusive);
118          public static void Stlxp(ArmEmitterContext context) => EmitStp(context, AccessType.OrderedEx);
119  
120          private static void EmitStr(ArmEmitterContext context, AccessType accType)
121          {
122              EmitStoreEx(context, accType, pair: false);
123          }
124  
125          private static void EmitStp(ArmEmitterContext context, AccessType accType)
126          {
127              EmitStoreEx(context, accType, pair: true);
128          }
129  
130          private static void EmitStoreEx(ArmEmitterContext context, AccessType accType, bool pair)
131          {
132              OpCodeMemEx op = (OpCodeMemEx)context.CurrOp;
133  
134              bool ordered = (accType & AccessType.Ordered) != 0;
135              bool exclusive = (accType & AccessType.Exclusive) != 0;
136  
137              Operand address = context.Copy(GetIntOrSP(context, op.Rn));
138  
139              Operand t = GetIntOrZR(context, op.Rt);
140  
141              if (pair)
142              {
143                  Debug.Assert(op.Size == 2 || op.Size == 3, "Invalid size for pairwise store.");
144  
145                  Operand t2 = GetIntOrZR(context, op.Rt2);
146  
147                  Operand value;
148  
149                  if (op.Size == 2)
150                  {
151                      value = context.BitwiseOr(t, context.ShiftLeft(t2, Const(32)));
152                  }
153                  else /* if (op.Size == 3) */
154                  {
155                      value = context.VectorInsert(context.VectorZero(), t, 0);
156                      value = context.VectorInsert(value, t2, 1);
157                  }
158  
159                  EmitStoreExclusive(context, address, value, exclusive, op.Size + 1, op.Rs, a32: false);
160              }
161              else
162              {
163                  EmitStoreExclusive(context, address, t, exclusive, op.Size, op.Rs, a32: false);
164              }
165  
166              if (ordered)
167              {
168                  EmitBarrier(context);
169              }
170          }
171  
172          private static void EmitBarrier(ArmEmitterContext context)
173          {
174              context.MemoryBarrier();
175          }
176      }
177  }