InstEmitMemoryExHelper.cs 7.5 KB

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