InstEmitMemoryEx.cs 12 KB

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