InstEmitAlu.cs 12 KB

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