InstEmitMemoryEx.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. using ARMeilleure.Decoders;
  2. using ARMeilleure.IntermediateRepresentation;
  3. using ARMeilleure.Translation;
  4. using System;
  5. using System.Diagnostics;
  6. using static ARMeilleure.Instructions.InstEmitHelper;
  7. using static ARMeilleure.IntermediateRepresentation.OperandHelper;
  8. namespace ARMeilleure.Instructions
  9. {
  10. static partial class InstEmit
  11. {
  12. [Flags]
  13. private enum AccessType
  14. {
  15. None = 0,
  16. Ordered = 1,
  17. Exclusive = 2,
  18. OrderedEx = Ordered | Exclusive
  19. }
  20. public static void Clrex(ArmEmitterContext context)
  21. {
  22. context.Call(new _Void(NativeInterface.ClearExclusive));
  23. }
  24. public static void Dmb(ArmEmitterContext context) => EmitBarrier(context);
  25. public static void Dsb(ArmEmitterContext context) => EmitBarrier(context);
  26. public static void Ldar(ArmEmitterContext context) => EmitLdr(context, AccessType.Ordered);
  27. public static void Ldaxr(ArmEmitterContext context) => EmitLdr(context, AccessType.OrderedEx);
  28. public static void Ldxr(ArmEmitterContext context) => EmitLdr(context, AccessType.Exclusive);
  29. public static void Ldxp(ArmEmitterContext context) => EmitLdp(context, AccessType.Exclusive);
  30. public static void Ldaxp(ArmEmitterContext context) => EmitLdp(context, AccessType.OrderedEx);
  31. private static void EmitLdr(ArmEmitterContext context, AccessType accType)
  32. {
  33. EmitLoadEx(context, accType, pair: false);
  34. }
  35. private static void EmitLdp(ArmEmitterContext context, AccessType accType)
  36. {
  37. EmitLoadEx(context, accType, pair: true);
  38. }
  39. private static void EmitLoadEx(ArmEmitterContext context, AccessType accType, bool pair)
  40. {
  41. OpCodeMemEx op = (OpCodeMemEx)context.CurrOp;
  42. bool ordered = (accType & AccessType.Ordered) != 0;
  43. bool exclusive = (accType & AccessType.Exclusive) != 0;
  44. if (ordered)
  45. {
  46. EmitBarrier(context);
  47. }
  48. Operand address = context.Copy(GetIntOrSP(context, op.Rn));
  49. if (pair)
  50. {
  51. // Exclusive loads should be atomic. For pairwise loads, we need to
  52. // read all the data at once. For a 32-bits pairwise load, we do a
  53. // simple 64-bits load, for a 128-bits load, we need to call a special
  54. // method to read 128-bits atomically.
  55. if (op.Size == 2)
  56. {
  57. Operand value = EmitLoad(context, address, exclusive, 3);
  58. Operand valueLow = context.ConvertI64ToI32(value);
  59. valueLow = context.ZeroExtend32(OperandType.I64, valueLow);
  60. Operand valueHigh = context.ShiftRightUI(value, Const(32));
  61. SetIntOrZR(context, op.Rt, valueLow);
  62. SetIntOrZR(context, op.Rt2, valueHigh);
  63. }
  64. else if (op.Size == 3)
  65. {
  66. Operand value = EmitLoad(context, address, exclusive, 4);
  67. Operand valueLow = context.VectorExtract(OperandType.I64, value, 0);
  68. Operand valueHigh = context.VectorExtract(OperandType.I64, value, 1);
  69. SetIntOrZR(context, op.Rt, valueLow);
  70. SetIntOrZR(context, op.Rt2, valueHigh);
  71. }
  72. else
  73. {
  74. throw new InvalidOperationException($"Invalid load size of {1 << op.Size} bytes.");
  75. }
  76. }
  77. else
  78. {
  79. // 8, 16, 32 or 64-bits (non-pairwise) load.
  80. Operand value = EmitLoad(context, address, exclusive, op.Size);
  81. SetIntOrZR(context, op.Rt, value);
  82. }
  83. }
  84. private static Operand EmitLoad(
  85. ArmEmitterContext context,
  86. Operand address,
  87. bool exclusive,
  88. int size)
  89. {
  90. Delegate fallbackMethodDlg = null;
  91. if (exclusive)
  92. {
  93. switch (size)
  94. {
  95. case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByteExclusive); break;
  96. case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16Exclusive); break;
  97. case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32Exclusive); break;
  98. case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64Exclusive); break;
  99. case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128Exclusive); break;
  100. }
  101. }
  102. else
  103. {
  104. switch (size)
  105. {
  106. case 0: fallbackMethodDlg = new _U8_U64 (NativeInterface.ReadByte); break;
  107. case 1: fallbackMethodDlg = new _U16_U64 (NativeInterface.ReadUInt16); break;
  108. case 2: fallbackMethodDlg = new _U32_U64 (NativeInterface.ReadUInt32); break;
  109. case 3: fallbackMethodDlg = new _U64_U64 (NativeInterface.ReadUInt64); break;
  110. case 4: fallbackMethodDlg = new _V128_U64(NativeInterface.ReadVector128); break;
  111. }
  112. }
  113. return context.Call(fallbackMethodDlg, address);
  114. }
  115. public static void Pfrm(ArmEmitterContext context)
  116. {
  117. // Memory Prefetch, execute as no-op.
  118. }
  119. public static void Stlr(ArmEmitterContext context) => EmitStr(context, AccessType.Ordered);
  120. public static void Stlxr(ArmEmitterContext context) => EmitStr(context, AccessType.OrderedEx);
  121. public static void Stxr(ArmEmitterContext context) => EmitStr(context, AccessType.Exclusive);
  122. public static void Stxp(ArmEmitterContext context) => EmitStp(context, AccessType.Exclusive);
  123. public static void Stlxp(ArmEmitterContext context) => EmitStp(context, AccessType.OrderedEx);
  124. private static void EmitStr(ArmEmitterContext context, AccessType accType)
  125. {
  126. EmitStoreEx(context, accType, pair: false);
  127. }
  128. private static void EmitStp(ArmEmitterContext context, AccessType accType)
  129. {
  130. EmitStoreEx(context, accType, pair: true);
  131. }
  132. private static void EmitStoreEx(ArmEmitterContext context, AccessType accType, bool pair)
  133. {
  134. OpCodeMemEx op = (OpCodeMemEx)context.CurrOp;
  135. bool ordered = (accType & AccessType.Ordered) != 0;
  136. bool exclusive = (accType & AccessType.Exclusive) != 0;
  137. if (ordered)
  138. {
  139. EmitBarrier(context);
  140. }
  141. Operand address = context.Copy(GetIntOrSP(context, op.Rn));
  142. Operand t = GetIntOrZR(context, op.Rt);
  143. Operand s = null;
  144. if (pair)
  145. {
  146. Debug.Assert(op.Size == 2 || op.Size == 3, "Invalid size for pairwise store.");
  147. Operand t2 = GetIntOrZR(context, op.Rt2);
  148. Operand value;
  149. if (op.Size == 2)
  150. {
  151. value = context.BitwiseOr(t, context.ShiftLeft(t2, Const(32)));
  152. }
  153. else /* if (op.Size == 3) */
  154. {
  155. value = context.VectorInsert(context.VectorZero(), t, 0);
  156. value = context.VectorInsert(value, t2, 1);
  157. }
  158. s = EmitStore(context, address, value, exclusive, op.Size + 1);
  159. }
  160. else
  161. {
  162. s = EmitStore(context, address, t, exclusive, op.Size);
  163. }
  164. if (s != null)
  165. {
  166. // This is only needed for exclusive stores. The function returns 0
  167. // when the store is successful, and 1 otherwise.
  168. SetIntOrZR(context, op.Rs, s);
  169. }
  170. }
  171. private static Operand EmitStore(
  172. ArmEmitterContext context,
  173. Operand address,
  174. Operand value,
  175. bool exclusive,
  176. int size)
  177. {
  178. if (size < 3)
  179. {
  180. value = context.ConvertI64ToI32(value);
  181. }
  182. Delegate fallbackMethodDlg = null;
  183. if (exclusive)
  184. {
  185. switch (size)
  186. {
  187. case 0: fallbackMethodDlg = new _S32_U64_U8 (NativeInterface.WriteByteExclusive); break;
  188. case 1: fallbackMethodDlg = new _S32_U64_U16 (NativeInterface.WriteUInt16Exclusive); break;
  189. case 2: fallbackMethodDlg = new _S32_U64_U32 (NativeInterface.WriteUInt32Exclusive); break;
  190. case 3: fallbackMethodDlg = new _S32_U64_U64 (NativeInterface.WriteUInt64Exclusive); break;
  191. case 4: fallbackMethodDlg = new _S32_U64_V128(NativeInterface.WriteVector128Exclusive); break;
  192. }
  193. return context.Call(fallbackMethodDlg, address, value);
  194. }
  195. else
  196. {
  197. switch (size)
  198. {
  199. case 0: fallbackMethodDlg = new _Void_U64_U8 (NativeInterface.WriteByte); break;
  200. case 1: fallbackMethodDlg = new _Void_U64_U16 (NativeInterface.WriteUInt16); break;
  201. case 2: fallbackMethodDlg = new _Void_U64_U32 (NativeInterface.WriteUInt32); break;
  202. case 3: fallbackMethodDlg = new _Void_U64_U64 (NativeInterface.WriteUInt64); break;
  203. case 4: fallbackMethodDlg = new _Void_U64_V128(NativeInterface.WriteVector128); break;
  204. }
  205. context.Call(fallbackMethodDlg, address, value);
  206. return null;
  207. }
  208. }
  209. private static void EmitBarrier(ArmEmitterContext context)
  210. {
  211. // Note: This barrier is most likely not necessary, and probably
  212. // doesn't make any difference since we need to do a ton of stuff
  213. // (software MMU emulation) to read or write anything anyway.
  214. }
  215. }
  216. }