InstEmitMemoryExHelper.cs 6.5 KB

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