/ src / ARMeilleure / Instructions / InstEmitMemoryExHelper.cs
InstEmitMemoryExHelper.cs
  1  using ARMeilleure.IntermediateRepresentation;
  2  using ARMeilleure.State;
  3  using ARMeilleure.Translation;
  4  using static ARMeilleure.Instructions.InstEmitHelper;
  5  using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  6  
  7  namespace ARMeilleure.Instructions
  8  {
  9      static class InstEmitMemoryExHelper
 10      {
 11          private const int ErgSizeLog2 = 4;
 12  
 13          public static Operand EmitLoadExclusive(ArmEmitterContext context, Operand address, bool exclusive, int size)
 14          {
 15              if (exclusive)
 16              {
 17                  Operand value;
 18  
 19                  if (size == 4)
 20                  {
 21                      // Only 128-bit CAS is guaranteed to have a atomic load.
 22                      Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, default, write: false, 4);
 23  
 24                      Operand zero = context.VectorZero();
 25  
 26                      value = context.CompareAndSwap(physAddr, zero, zero);
 27                  }
 28                  else
 29                  {
 30                      value = InstEmitMemoryHelper.EmitReadIntAligned(context, address, size);
 31                  }
 32  
 33                  Operand arg0 = context.LoadArgument(OperandType.I64, 0);
 34  
 35                  Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
 36                  Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset()));
 37  
 38                  context.Store(exAddrPtr, context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask())));
 39  
 40                  // Make sure the unused higher bits of the value are cleared.
 41                  if (size < 3)
 42                  {
 43                      context.Store(exValuePtr, Const(0UL));
 44                  }
 45                  if (size < 4)
 46                  {
 47                      context.Store(context.Add(exValuePtr, Const(exValuePtr.Type, 8L)), Const(0UL));
 48                  }
 49  
 50                  // Store the new exclusive value.
 51                  context.Store(exValuePtr, value);
 52  
 53                  return value;
 54              }
 55              else
 56              {
 57                  return InstEmitMemoryHelper.EmitReadIntAligned(context, address, size);
 58              }
 59          }
 60  
 61          public static void EmitStoreExclusive(
 62              ArmEmitterContext context,
 63              Operand address,
 64              Operand value,
 65              bool exclusive,
 66              int size,
 67              int rs,
 68              bool a32)
 69          {
 70              if (size < 3)
 71              {
 72                  value = context.ConvertI64ToI32(value);
 73              }
 74  
 75              if (exclusive)
 76              {
 77                  // We overwrite one of the register (Rs),
 78                  // keep a copy of the values to ensure we are working with the correct values.
 79                  address = context.Copy(address);
 80                  value = context.Copy(value);
 81  
 82                  void SetRs(Operand value)
 83                  {
 84                      if (a32)
 85                      {
 86                          SetIntA32(context, rs, value);
 87                      }
 88                      else
 89                      {
 90                          SetIntOrZR(context, rs, value);
 91                      }
 92                  }
 93  
 94                  Operand arg0 = context.LoadArgument(OperandType.I64, 0);
 95  
 96                  Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
 97                  Operand exAddr = context.Load(address.Type, exAddrPtr);
 98  
 99                  // STEP 1: Check if we have exclusive access to this memory region. If not, fail and skip store.
100                  Operand maskedAddress = context.BitwiseAnd(address, Const(address.Type, GetExclusiveAddressMask()));
101  
102                  Operand exFailed = context.ICompareNotEqual(exAddr, maskedAddress);
103  
104                  Operand lblExit = Label();
105  
106                  SetRs(Const(1));
107  
108                  context.BranchIfTrue(lblExit, exFailed);
109  
110                  // STEP 2: We have exclusive access and the address is valid, attempt the store using CAS.
111                  Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, default, write: true, size);
112  
113                  Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset()));
114                  Operand exValue = size switch
115                  {
116                      0 => context.Load8(exValuePtr),
117                      1 => context.Load16(exValuePtr),
118                      2 => context.Load(OperandType.I32, exValuePtr),
119                      3 => context.Load(OperandType.I64, exValuePtr),
120                      _ => context.Load(OperandType.V128, exValuePtr),
121                  };
122  
123                  Operand currValue = size switch
124                  {
125                      0 => context.CompareAndSwap8(physAddr, exValue, value),
126                      1 => context.CompareAndSwap16(physAddr, exValue, value),
127                      _ => context.CompareAndSwap(physAddr, exValue, value),
128                  };
129  
130                  // STEP 3: Check if we succeeded by comparing expected and in-memory values.
131                  Operand storeFailed;
132  
133                  if (size == 4)
134                  {
135                      Operand currValueLow = context.VectorExtract(OperandType.I64, currValue, 0);
136                      Operand currValueHigh = context.VectorExtract(OperandType.I64, currValue, 1);
137  
138                      Operand exValueLow = context.VectorExtract(OperandType.I64, exValue, 0);
139                      Operand exValueHigh = context.VectorExtract(OperandType.I64, exValue, 1);
140  
141                      storeFailed = context.BitwiseOr(
142                          context.ICompareNotEqual(currValueLow, exValueLow),
143                          context.ICompareNotEqual(currValueHigh, exValueHigh));
144                  }
145                  else
146                  {
147                      storeFailed = context.ICompareNotEqual(currValue, exValue);
148                  }
149  
150                  SetRs(storeFailed);
151  
152                  context.MarkLabel(lblExit);
153              }
154              else
155              {
156                  InstEmitMemoryHelper.EmitWriteIntAligned(context, address, value, size);
157              }
158          }
159  
160          public static void EmitClearExclusive(ArmEmitterContext context)
161          {
162              Operand arg0 = context.LoadArgument(OperandType.I64, 0);
163  
164              Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
165  
166              // We store ULONG max to force any exclusive address checks to fail,
167              // since this value is not aligned to the ERG mask.
168              context.Store(exAddrPtr, Const(ulong.MaxValue));
169          }
170  
171          private static long GetExclusiveAddressMask() => ~((4L << ErgSizeLog2) - 1);
172      }
173  }