InstEmitMemoryExHelper.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. context.Store(exValuePtr, value);
  38. return value;
  39. }
  40. else
  41. {
  42. return InstEmitMemoryHelper.EmitReadIntAligned(context, address, size);
  43. }
  44. }
  45. public static void EmitStoreExclusive(
  46. ArmEmitterContext context,
  47. Operand address,
  48. Operand value,
  49. bool exclusive,
  50. int size,
  51. int rs,
  52. bool a32)
  53. {
  54. if (size < 3)
  55. {
  56. value = context.ConvertI64ToI32(value);
  57. }
  58. if (exclusive)
  59. {
  60. void SetRs(Operand value)
  61. {
  62. if (a32)
  63. {
  64. SetIntA32(context, rs, value);
  65. }
  66. else
  67. {
  68. SetIntOrZR(context, rs, value);
  69. }
  70. }
  71. Operand arg0 = context.LoadArgument(OperandType.I64, 0);
  72. Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
  73. Operand exAddr = context.Load(address.Type, exAddrPtr);
  74. // STEP 1: Check if we have exclusive access to this memory region. If not, fail and skip store.
  75. Operand maskedAddress = context.BitwiseAnd(address, Const(GetExclusiveAddressMask()));
  76. Operand exFailed = context.ICompareNotEqual(exAddr, maskedAddress);
  77. Operand lblExit = Label();
  78. SetRs(exFailed);
  79. context.BranchIfTrue(lblExit, exFailed);
  80. // STEP 2: We have exclusive access, make sure that the address is valid.
  81. Operand isUnalignedAddr = InstEmitMemoryHelper.EmitAddressCheck(context, address, size);
  82. Operand lblFastPath = Label();
  83. context.BranchIfFalse(lblFastPath, isUnalignedAddr);
  84. // The call is not expected to return (it should throw).
  85. context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
  86. // STEP 3: We have exclusive access and the address is valid, attempt the store using CAS.
  87. context.MarkLabel(lblFastPath);
  88. Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: true);
  89. Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset()));
  90. Operand exValue = size switch
  91. {
  92. 0 => context.Load8(exValuePtr),
  93. 1 => context.Load16(exValuePtr),
  94. 2 => context.Load(OperandType.I32, exValuePtr),
  95. 3 => context.Load(OperandType.I64, exValuePtr),
  96. _ => context.Load(OperandType.V128, exValuePtr)
  97. };
  98. Operand currValue = size switch
  99. {
  100. 0 => context.CompareAndSwap8(physAddr, exValue, value),
  101. 1 => context.CompareAndSwap16(physAddr, exValue, value),
  102. _ => context.CompareAndSwap(physAddr, exValue, value)
  103. };
  104. // STEP 4: Check if we succeeded by comparing expected and in-memory values.
  105. Operand storeFailed;
  106. if (size == 4)
  107. {
  108. Operand currValueLow = context.VectorExtract(OperandType.I64, currValue, 0);
  109. Operand currValueHigh = context.VectorExtract(OperandType.I64, currValue, 1);
  110. Operand exValueLow = context.VectorExtract(OperandType.I64, exValue, 0);
  111. Operand exValueHigh = context.VectorExtract(OperandType.I64, exValue, 1);
  112. storeFailed = context.BitwiseOr(
  113. context.ICompareNotEqual(currValueLow, exValueLow),
  114. context.ICompareNotEqual(currValueHigh, exValueHigh));
  115. }
  116. else
  117. {
  118. storeFailed = context.ICompareNotEqual(currValue, exValue);
  119. }
  120. SetRs(storeFailed);
  121. context.MarkLabel(lblExit);
  122. }
  123. else
  124. {
  125. InstEmitMemoryHelper.EmitWriteIntAligned(context, address, value, size);
  126. }
  127. }
  128. public static void EmitClearExclusive(ArmEmitterContext context)
  129. {
  130. Operand arg0 = context.LoadArgument(OperandType.I64, 0);
  131. Operand exAddrPtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveAddressOffset()));
  132. // We store ULONG max to force any exclusive address checks to fail,
  133. // since this value is not aligned to the ERG mask.
  134. context.Store(exAddrPtr, Const(ulong.MaxValue));
  135. }
  136. private static long GetExclusiveAddressMask() => ~((4L << ErgSizeLog2) - 1);
  137. }
  138. }