InstEmitAlu.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. using ChocolArm64.Decoders;
  2. using ChocolArm64.IntermediateRepresentation;
  3. using ChocolArm64.State;
  4. using ChocolArm64.Translation;
  5. using System;
  6. using System.Reflection;
  7. using System.Reflection.Emit;
  8. using System.Runtime.Intrinsics.X86;
  9. using static ChocolArm64.Instructions.InstEmitAluHelper;
  10. namespace ChocolArm64.Instructions
  11. {
  12. static partial class InstEmit
  13. {
  14. public static void Adc(ILEmitterCtx context) => EmitAdc(context, false);
  15. public static void Adcs(ILEmitterCtx context) => EmitAdc(context, true);
  16. private static void EmitAdc(ILEmitterCtx context, bool setFlags)
  17. {
  18. EmitAluLoadOpers(context);
  19. context.Emit(OpCodes.Add);
  20. context.EmitLdflg((int)PState.CBit);
  21. Type[] mthdTypes = new Type[] { typeof(bool) };
  22. MethodInfo mthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), mthdTypes);
  23. context.EmitCall(mthdInfo);
  24. if (context.CurrOp.RegisterSize != RegisterSize.Int32)
  25. {
  26. context.Emit(OpCodes.Conv_U8);
  27. }
  28. context.Emit(OpCodes.Add);
  29. if (setFlags)
  30. {
  31. context.EmitZnFlagCheck();
  32. EmitAdcsCCheck(context);
  33. EmitAddsVCheck(context);
  34. }
  35. EmitAluStore(context);
  36. }
  37. public static void Add(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Add);
  38. public static void Adds(ILEmitterCtx context)
  39. {
  40. context.TryOptMarkCondWithoutCmp();
  41. EmitAluLoadOpers(context);
  42. context.Emit(OpCodes.Add);
  43. context.EmitZnFlagCheck();
  44. EmitAddsCCheck(context);
  45. EmitAddsVCheck(context);
  46. EmitAluStoreS(context);
  47. }
  48. public static void And(ILEmitterCtx context) => EmitAluOp(context, OpCodes.And);
  49. public static void Ands(ILEmitterCtx context)
  50. {
  51. EmitAluLoadOpers(context);
  52. context.Emit(OpCodes.And);
  53. EmitZeroCvFlags(context);
  54. context.EmitZnFlagCheck();
  55. EmitAluStoreS(context);
  56. }
  57. public static void Asrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr);
  58. public static void Bic(ILEmitterCtx context) => EmitBic(context, false);
  59. public static void Bics(ILEmitterCtx context) => EmitBic(context, true);
  60. private static void EmitBic(ILEmitterCtx context, bool setFlags)
  61. {
  62. EmitAluLoadOpers(context);
  63. context.Emit(OpCodes.Not);
  64. context.Emit(OpCodes.And);
  65. if (setFlags)
  66. {
  67. EmitZeroCvFlags(context);
  68. context.EmitZnFlagCheck();
  69. }
  70. EmitAluStore(context, setFlags);
  71. }
  72. public static void Cls(ILEmitterCtx context)
  73. {
  74. OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp;
  75. context.EmitLdintzr(op.Rn);
  76. context.EmitLdc_I4(op.RegisterSize == RegisterSize.Int32 ? 32 : 64);
  77. SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingSigns));
  78. context.EmitStintzr(op.Rd);
  79. }
  80. public static void Clz(ILEmitterCtx context)
  81. {
  82. OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp;
  83. context.EmitLdintzr(op.Rn);
  84. if (Lzcnt.IsSupported)
  85. {
  86. Type tValue = op.RegisterSize == RegisterSize.Int32 ? typeof(uint) : typeof(ulong);
  87. context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { tValue }));
  88. }
  89. else
  90. {
  91. context.EmitLdc_I4(op.RegisterSize == RegisterSize.Int32 ? 32 : 64);
  92. SoftFallback.EmitCall(context, nameof(SoftFallback.CountLeadingZeros));
  93. }
  94. context.EmitStintzr(op.Rd);
  95. }
  96. public static void Eon(ILEmitterCtx context)
  97. {
  98. EmitAluLoadOpers(context);
  99. context.Emit(OpCodes.Not);
  100. context.Emit(OpCodes.Xor);
  101. EmitAluStore(context);
  102. }
  103. public static void Eor(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Xor);
  104. public static void Extr(ILEmitterCtx context)
  105. {
  106. // TODO: Ensure that the Shift is valid for the Is64Bits.
  107. OpCodeAluRs64 op = (OpCodeAluRs64)context.CurrOp;
  108. context.EmitLdintzr(op.Rm);
  109. if (op.Shift > 0)
  110. {
  111. context.EmitLdc_I4(op.Shift);
  112. context.Emit(OpCodes.Shr_Un);
  113. context.EmitLdintzr(op.Rn);
  114. context.EmitLdc_I4(op.GetBitsCount() - op.Shift);
  115. context.Emit(OpCodes.Shl);
  116. context.Emit(OpCodes.Or);
  117. }
  118. EmitAluStore(context);
  119. }
  120. public static void Lslv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shl);
  121. public static void Lsrv(ILEmitterCtx context) => EmitAluOpShift(context, OpCodes.Shr_Un);
  122. public static void Sbc(ILEmitterCtx context) => EmitSbc(context, false);
  123. public static void Sbcs(ILEmitterCtx context) => EmitSbc(context, true);
  124. private static void EmitSbc(ILEmitterCtx context, bool setFlags)
  125. {
  126. EmitAluLoadOpers(context);
  127. context.Emit(OpCodes.Sub);
  128. context.EmitLdflg((int)PState.CBit);
  129. Type[] mthdTypes = new Type[] { typeof(bool) };
  130. MethodInfo mthdInfo = typeof(Convert).GetMethod(nameof(Convert.ToInt32), mthdTypes);
  131. context.EmitCall(mthdInfo);
  132. context.EmitLdc_I4(1);
  133. context.Emit(OpCodes.Xor);
  134. if (context.CurrOp.RegisterSize != RegisterSize.Int32)
  135. {
  136. context.Emit(OpCodes.Conv_U8);
  137. }
  138. context.Emit(OpCodes.Sub);
  139. if (setFlags)
  140. {
  141. context.EmitZnFlagCheck();
  142. EmitSbcsCCheck(context);
  143. EmitSubsVCheck(context);
  144. }
  145. EmitAluStore(context);
  146. }
  147. public static void Sub(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Sub);
  148. public static void Subs(ILEmitterCtx context)
  149. {
  150. context.TryOptMarkCondWithoutCmp();
  151. EmitAluLoadOpers(context);
  152. context.Emit(OpCodes.Sub);
  153. context.EmitZnFlagCheck();
  154. EmitSubsCCheck(context);
  155. EmitSubsVCheck(context);
  156. EmitAluStoreS(context);
  157. }
  158. public static void Orn(ILEmitterCtx context)
  159. {
  160. EmitAluLoadOpers(context);
  161. context.Emit(OpCodes.Not);
  162. context.Emit(OpCodes.Or);
  163. EmitAluStore(context);
  164. }
  165. public static void Orr(ILEmitterCtx context) => EmitAluOp(context, OpCodes.Or);
  166. public static void Rbit(ILEmitterCtx context) => EmitFallback32_64(context,
  167. nameof(SoftFallback.ReverseBits32),
  168. nameof(SoftFallback.ReverseBits64));
  169. public static void Rev16(ILEmitterCtx context) => EmitFallback32_64(context,
  170. nameof(SoftFallback.ReverseBytes16_32),
  171. nameof(SoftFallback.ReverseBytes16_64));
  172. public static void Rev32(ILEmitterCtx context) => EmitFallback32_64(context,
  173. nameof(SoftFallback.ReverseBytes32_32),
  174. nameof(SoftFallback.ReverseBytes32_64));
  175. private static void EmitFallback32_64(ILEmitterCtx context, string name32, string name64)
  176. {
  177. OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp;
  178. context.EmitLdintzr(op.Rn);
  179. if (op.RegisterSize == RegisterSize.Int32)
  180. {
  181. SoftFallback.EmitCall(context, name32);
  182. }
  183. else
  184. {
  185. SoftFallback.EmitCall(context, name64);
  186. }
  187. context.EmitStintzr(op.Rd);
  188. }
  189. public static void Rev64(ILEmitterCtx context)
  190. {
  191. OpCodeAlu64 op = (OpCodeAlu64)context.CurrOp;
  192. context.EmitLdintzr(op.Rn);
  193. SoftFallback.EmitCall(context, nameof(SoftFallback.ReverseBytes64));
  194. context.EmitStintzr(op.Rd);
  195. }
  196. public static void Rorv(ILEmitterCtx context)
  197. {
  198. EmitAluLoadRn(context);
  199. EmitAluLoadShift(context);
  200. context.Emit(OpCodes.Shr_Un);
  201. EmitAluLoadRn(context);
  202. context.EmitLdc_I4(context.CurrOp.GetBitsCount());
  203. EmitAluLoadShift(context);
  204. context.Emit(OpCodes.Sub);
  205. context.Emit(OpCodes.Shl);
  206. context.Emit(OpCodes.Or);
  207. EmitAluStore(context);
  208. }
  209. public static void Sdiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div);
  210. public static void Udiv(ILEmitterCtx context) => EmitDiv(context, OpCodes.Div_Un);
  211. private static void EmitDiv(ILEmitterCtx context, OpCode ilOp)
  212. {
  213. // If Rm == 0, Rd = 0 (division by zero).
  214. context.EmitLdc_I(0);
  215. EmitAluLoadRm(context);
  216. context.EmitLdc_I(0);
  217. ILLabel badDiv = new ILLabel();
  218. context.Emit(OpCodes.Beq_S, badDiv);
  219. context.Emit(OpCodes.Pop);
  220. if (ilOp == OpCodes.Div)
  221. {
  222. // If Rn == INT_MIN && Rm == -1, Rd = INT_MIN (overflow).
  223. long intMin = 1L << (context.CurrOp.GetBitsCount() - 1);
  224. context.EmitLdc_I(intMin);
  225. EmitAluLoadRn(context);
  226. context.EmitLdc_I(intMin);
  227. context.Emit(OpCodes.Ceq);
  228. EmitAluLoadRm(context);
  229. context.EmitLdc_I(-1);
  230. context.Emit(OpCodes.Ceq);
  231. context.Emit(OpCodes.And);
  232. context.Emit(OpCodes.Brtrue_S, badDiv);
  233. context.Emit(OpCodes.Pop);
  234. }
  235. EmitAluLoadRn(context);
  236. EmitAluLoadRm(context);
  237. context.Emit(ilOp);
  238. context.MarkLabel(badDiv);
  239. EmitAluStore(context);
  240. }
  241. private static void EmitAluOp(ILEmitterCtx context, OpCode ilOp)
  242. {
  243. EmitAluLoadOpers(context);
  244. context.Emit(ilOp);
  245. EmitAluStore(context);
  246. }
  247. private static void EmitAluOpShift(ILEmitterCtx context, OpCode ilOp)
  248. {
  249. EmitAluLoadRn(context);
  250. EmitAluLoadShift(context);
  251. context.Emit(ilOp);
  252. EmitAluStore(context);
  253. }
  254. private static void EmitAluLoadShift(ILEmitterCtx context)
  255. {
  256. EmitAluLoadRm(context);
  257. context.EmitLdc_I(context.CurrOp.GetBitsCount() - 1);
  258. context.Emit(OpCodes.And);
  259. // Note: Only 32-bits shift values are valid, so when the value is 64-bits
  260. // we need to cast it to a 32-bits integer. This is fine because we
  261. // AND the value and only keep the lower 5 or 6 bits anyway -- it
  262. // could very well fit on a byte.
  263. if (context.CurrOp.RegisterSize != RegisterSize.Int32)
  264. {
  265. context.Emit(OpCodes.Conv_I4);
  266. }
  267. }
  268. private static void EmitZeroCvFlags(ILEmitterCtx context)
  269. {
  270. context.EmitLdc_I4(0);
  271. context.EmitStflg((int)PState.VBit);
  272. context.EmitLdc_I4(0);
  273. context.EmitStflg((int)PState.CBit);
  274. }
  275. public static void EmitAluStore(ILEmitterCtx context) => EmitAluStore(context, false);
  276. public static void EmitAluStoreS(ILEmitterCtx context) => EmitAluStore(context, true);
  277. public static void EmitAluStore(ILEmitterCtx context, bool setFlags)
  278. {
  279. IOpCodeAlu64 op = (IOpCodeAlu64)context.CurrOp;
  280. if (setFlags || op is IOpCodeAluRs64)
  281. {
  282. context.EmitStintzr(op.Rd);
  283. }
  284. else
  285. {
  286. context.EmitStint(op.Rd);
  287. }
  288. }
  289. }
  290. }