InstEmitMemoryEx.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. using ChocolArm64.Decoders;
  2. using ChocolArm64.IntermediateRepresentation;
  3. using ChocolArm64.Memory;
  4. using ChocolArm64.State;
  5. using ChocolArm64.Translation;
  6. using System;
  7. using System.Reflection.Emit;
  8. using System.Threading;
  9. using static ChocolArm64.Instructions.InstEmitMemoryHelper;
  10. namespace ChocolArm64.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. public static void Clrex(ILEmitterCtx context)
  23. {
  24. context.EmitLdarg(TranslatedSub.StateArgIdx);
  25. context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.ClearExclusiveAddress));
  26. }
  27. public static void Dmb(ILEmitterCtx context) => EmitBarrier(context);
  28. public static void Dsb(ILEmitterCtx context) => EmitBarrier(context);
  29. public static void Ldar(ILEmitterCtx context) => EmitLdr(context, AccessType.Ordered);
  30. public static void Ldaxr(ILEmitterCtx context) => EmitLdr(context, AccessType.OrderedEx);
  31. public static void Ldxr(ILEmitterCtx context) => EmitLdr(context, AccessType.Exclusive);
  32. public static void Ldxp(ILEmitterCtx context) => EmitLdp(context, AccessType.Exclusive);
  33. public static void Ldaxp(ILEmitterCtx context) => EmitLdp(context, AccessType.OrderedEx);
  34. private static void EmitLdr(ILEmitterCtx context, AccessType accType)
  35. {
  36. EmitLoad(context, accType, pair: false);
  37. }
  38. private static void EmitLdp(ILEmitterCtx context, AccessType accType)
  39. {
  40. EmitLoad(context, accType, pair: true);
  41. }
  42. private static void EmitLoad(ILEmitterCtx context, AccessType accType, bool pair)
  43. {
  44. OpCodeMemEx64 op = (OpCodeMemEx64)context.CurrOp;
  45. bool ordered = (accType & AccessType.Ordered) != 0;
  46. bool exclusive = (accType & AccessType.Exclusive) != 0;
  47. if (ordered)
  48. {
  49. EmitBarrier(context);
  50. }
  51. context.EmitLdint(op.Rn);
  52. context.EmitSttmp();
  53. if (exclusive)
  54. {
  55. context.EmitLdarg(TranslatedSub.StateArgIdx);
  56. context.EmitLdtmp();
  57. context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.SetExclusiveAddress));
  58. }
  59. void WriteExclusiveValue(string propName)
  60. {
  61. context.Emit(OpCodes.Dup);
  62. if (op.Size < 3)
  63. {
  64. context.Emit(OpCodes.Conv_U8);
  65. }
  66. context.EmitSttmp2();
  67. context.EmitLdarg(TranslatedSub.StateArgIdx);
  68. context.EmitLdtmp2();
  69. context.EmitCallPrivatePropSet(typeof(CpuThreadState), propName);
  70. }
  71. if (pair)
  72. {
  73. // Exclusive loads should be atomic. For pairwise loads, we need to
  74. // read all the data at once. For a 32-bits pairwise load, we do a
  75. // simple 64-bits load, for a 128-bits load, we need to call a special
  76. // method to read 128-bits atomically.
  77. if (op.Size == 2)
  78. {
  79. context.EmitLdtmp();
  80. EmitReadZxCall(context, 3);
  81. context.Emit(OpCodes.Dup);
  82. // Mask low half.
  83. context.Emit(OpCodes.Conv_U4);
  84. if (exclusive)
  85. {
  86. WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
  87. }
  88. context.EmitStintzr(op.Rt);
  89. // Shift high half.
  90. context.EmitLsr(32);
  91. context.Emit(OpCodes.Conv_U4);
  92. if (exclusive)
  93. {
  94. WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueHigh));
  95. }
  96. context.EmitStintzr(op.Rt2);
  97. }
  98. else if (op.Size == 3)
  99. {
  100. context.EmitLdarg(TranslatedSub.MemoryArgIdx);
  101. context.EmitLdtmp();
  102. context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicReadInt128));
  103. context.Emit(OpCodes.Dup);
  104. // Load low part of the vector.
  105. context.EmitLdc_I4(0);
  106. context.EmitLdc_I4(3);
  107. VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractIntZx));
  108. if (exclusive)
  109. {
  110. WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
  111. }
  112. context.EmitStintzr(op.Rt);
  113. // Load high part of the vector.
  114. context.EmitLdc_I4(1);
  115. context.EmitLdc_I4(3);
  116. VectorHelper.EmitCall(context, nameof(VectorHelper.VectorExtractIntZx));
  117. if (exclusive)
  118. {
  119. WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueHigh));
  120. }
  121. context.EmitStintzr(op.Rt2);
  122. }
  123. else
  124. {
  125. throw new InvalidOperationException($"Invalid load size of {1 << op.Size} bytes.");
  126. }
  127. }
  128. else
  129. {
  130. // 8, 16, 32 or 64-bits (non-pairwise) load.
  131. context.EmitLdtmp();
  132. EmitReadZxCall(context, op.Size);
  133. if (exclusive)
  134. {
  135. WriteExclusiveValue(nameof(CpuThreadState.ExclusiveValueLow));
  136. }
  137. context.EmitStintzr(op.Rt);
  138. }
  139. }
  140. public static void Pfrm(ILEmitterCtx context)
  141. {
  142. // Memory Prefetch, execute as no-op.
  143. }
  144. public static void Stlr(ILEmitterCtx context) => EmitStr(context, AccessType.Ordered);
  145. public static void Stlxr(ILEmitterCtx context) => EmitStr(context, AccessType.OrderedEx);
  146. public static void Stxr(ILEmitterCtx context) => EmitStr(context, AccessType.Exclusive);
  147. public static void Stxp(ILEmitterCtx context) => EmitStp(context, AccessType.Exclusive);
  148. public static void Stlxp(ILEmitterCtx context) => EmitStp(context, AccessType.OrderedEx);
  149. private static void EmitStr(ILEmitterCtx context, AccessType accType)
  150. {
  151. EmitStore(context, accType, pair: false);
  152. }
  153. private static void EmitStp(ILEmitterCtx context, AccessType accType)
  154. {
  155. EmitStore(context, accType, pair: true);
  156. }
  157. private static void EmitStore(ILEmitterCtx context, AccessType accType, bool pair)
  158. {
  159. OpCodeMemEx64 op = (OpCodeMemEx64)context.CurrOp;
  160. bool ordered = (accType & AccessType.Ordered) != 0;
  161. bool exclusive = (accType & AccessType.Exclusive) != 0;
  162. if (ordered)
  163. {
  164. EmitBarrier(context);
  165. }
  166. if (exclusive)
  167. {
  168. ILLabel lblEx = new ILLabel();
  169. ILLabel lblEnd = new ILLabel();
  170. context.EmitLdarg(TranslatedSub.StateArgIdx);
  171. context.EmitLdint(op.Rn);
  172. context.EmitPrivateCall(typeof(CpuThreadState), nameof(CpuThreadState.CheckExclusiveAddress));
  173. context.Emit(OpCodes.Brtrue_S, lblEx);
  174. // Address check failed, set error right away and do not store anything.
  175. context.EmitLdc_I4(1);
  176. context.EmitStintzr(op.Rs);
  177. context.Emit(OpCodes.Br, lblEnd);
  178. // Address check passed.
  179. context.MarkLabel(lblEx);
  180. context.EmitLdarg(TranslatedSub.MemoryArgIdx);
  181. context.EmitLdint(op.Rn);
  182. context.EmitLdarg(TranslatedSub.StateArgIdx);
  183. context.EmitCallPrivatePropGet(typeof(CpuThreadState), nameof(CpuThreadState.ExclusiveValueLow));
  184. void EmitCast()
  185. {
  186. // The input should be always int64.
  187. switch (op.Size)
  188. {
  189. case 0: context.Emit(OpCodes.Conv_U1); break;
  190. case 1: context.Emit(OpCodes.Conv_U2); break;
  191. case 2: context.Emit(OpCodes.Conv_U4); break;
  192. }
  193. }
  194. EmitCast();
  195. if (pair)
  196. {
  197. context.EmitLdarg(TranslatedSub.StateArgIdx);
  198. context.EmitCallPrivatePropGet(typeof(CpuThreadState), nameof(CpuThreadState.ExclusiveValueHigh));
  199. EmitCast();
  200. context.EmitLdintzr(op.Rt);
  201. EmitCast();
  202. context.EmitLdintzr(op.Rt2);
  203. EmitCast();
  204. switch (op.Size)
  205. {
  206. case 2: context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchange2xInt32)); break;
  207. case 3: context.EmitPrivateCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt128)); break;
  208. default: throw new InvalidOperationException($"Invalid store size of {1 << op.Size} bytes.");
  209. }
  210. }
  211. else
  212. {
  213. context.EmitLdintzr(op.Rt);
  214. EmitCast();
  215. switch (op.Size)
  216. {
  217. case 0: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeByte)); break;
  218. case 1: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt16)); break;
  219. case 2: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt32)); break;
  220. case 3: context.EmitCall(typeof(MemoryManager), nameof(MemoryManager.AtomicCompareExchangeInt64)); break;
  221. default: throw new InvalidOperationException($"Invalid store size of {1 << op.Size} bytes.");
  222. }
  223. }
  224. // The value returned is a bool, true if the values compared
  225. // were equal and the new value was written, false otherwise.
  226. // We need to invert this result, as on ARM 1 indicates failure,
  227. // and 0 success on those instructions.
  228. context.EmitLdc_I4(1);
  229. context.Emit(OpCodes.Xor);
  230. context.Emit(OpCodes.Dup);
  231. context.Emit(OpCodes.Conv_U8);
  232. context.EmitStintzr(op.Rs);
  233. // Only clear the exclusive monitor if the store was successful (Rs = false).
  234. context.Emit(OpCodes.Brtrue_S, lblEnd);
  235. Clrex(context);
  236. context.MarkLabel(lblEnd);
  237. }
  238. else
  239. {
  240. void EmitWriteCall(int rt, long offset)
  241. {
  242. context.EmitLdint(op.Rn);
  243. if (offset != 0)
  244. {
  245. context.EmitLdc_I8(offset);
  246. context.Emit(OpCodes.Add);
  247. }
  248. context.EmitLdintzr(rt);
  249. InstEmitMemoryHelper.EmitWriteCall(context, op.Size);
  250. }
  251. EmitWriteCall(op.Rt, 0);
  252. if (pair)
  253. {
  254. EmitWriteCall(op.Rt2, 1 << op.Size);
  255. }
  256. }
  257. }
  258. private static void EmitBarrier(ILEmitterCtx context)
  259. {
  260. // Note: This barrier is most likely not necessary, and probably
  261. // doesn't make any difference since we need to do a ton of stuff
  262. // (software MMU emulation) to read or write anything anyway.
  263. context.EmitCall(typeof(Thread), nameof(Thread.MemoryBarrier));
  264. }
  265. }
  266. }