InstEmitSimdCmp.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. using ChocolArm64.Decoders;
  2. using ChocolArm64.State;
  3. using ChocolArm64.Translation;
  4. using System;
  5. using System.Reflection.Emit;
  6. using System.Runtime.Intrinsics;
  7. using System.Runtime.Intrinsics.X86;
  8. using static ChocolArm64.Instructions.InstEmitAluHelper;
  9. using static ChocolArm64.Instructions.InstEmitSimdHelper;
  10. namespace ChocolArm64.Instructions
  11. {
  12. static partial class InstEmit
  13. {
  14. public static void Cmeq_S(ILEmitterCtx context)
  15. {
  16. EmitCmp(context, OpCodes.Beq_S, scalar: true);
  17. }
  18. public static void Cmeq_V(ILEmitterCtx context)
  19. {
  20. if (context.CurrOp is OpCodeSimdReg64 op)
  21. {
  22. if (op.Size < 3 && Optimizations.UseSse2)
  23. {
  24. EmitSse2Op(context, nameof(Sse2.CompareEqual));
  25. }
  26. else if (op.Size == 3 && Optimizations.UseSse41)
  27. {
  28. EmitSse41Op(context, nameof(Sse41.CompareEqual));
  29. }
  30. else
  31. {
  32. EmitCmp(context, OpCodes.Beq_S, scalar: false);
  33. }
  34. }
  35. else
  36. {
  37. EmitCmp(context, OpCodes.Beq_S, scalar: false);
  38. }
  39. }
  40. public static void Cmge_S(ILEmitterCtx context)
  41. {
  42. EmitCmp(context, OpCodes.Bge_S, scalar: true);
  43. }
  44. public static void Cmge_V(ILEmitterCtx context)
  45. {
  46. EmitCmp(context, OpCodes.Bge_S, scalar: false);
  47. }
  48. public static void Cmgt_S(ILEmitterCtx context)
  49. {
  50. EmitCmp(context, OpCodes.Bgt_S, scalar: true);
  51. }
  52. public static void Cmgt_V(ILEmitterCtx context)
  53. {
  54. if (context.CurrOp is OpCodeSimdReg64 op)
  55. {
  56. if (op.Size < 3 && Optimizations.UseSse2)
  57. {
  58. EmitSse2Op(context, nameof(Sse2.CompareGreaterThan));
  59. }
  60. else if (op.Size == 3 && Optimizations.UseSse42)
  61. {
  62. EmitSse42Op(context, nameof(Sse42.CompareGreaterThan));
  63. }
  64. else
  65. {
  66. EmitCmp(context, OpCodes.Bgt_S, scalar: false);
  67. }
  68. }
  69. else
  70. {
  71. EmitCmp(context, OpCodes.Bgt_S, scalar: false);
  72. }
  73. }
  74. public static void Cmhi_S(ILEmitterCtx context)
  75. {
  76. EmitCmp(context, OpCodes.Bgt_Un_S, scalar: true);
  77. }
  78. public static void Cmhi_V(ILEmitterCtx context)
  79. {
  80. EmitCmp(context, OpCodes.Bgt_Un_S, scalar: false);
  81. }
  82. public static void Cmhs_S(ILEmitterCtx context)
  83. {
  84. EmitCmp(context, OpCodes.Bge_Un_S, scalar: true);
  85. }
  86. public static void Cmhs_V(ILEmitterCtx context)
  87. {
  88. EmitCmp(context, OpCodes.Bge_Un_S, scalar: false);
  89. }
  90. public static void Cmle_S(ILEmitterCtx context)
  91. {
  92. EmitCmp(context, OpCodes.Ble_S, scalar: true);
  93. }
  94. public static void Cmle_V(ILEmitterCtx context)
  95. {
  96. EmitCmp(context, OpCodes.Ble_S, scalar: false);
  97. }
  98. public static void Cmlt_S(ILEmitterCtx context)
  99. {
  100. EmitCmp(context, OpCodes.Blt_S, scalar: true);
  101. }
  102. public static void Cmlt_V(ILEmitterCtx context)
  103. {
  104. EmitCmp(context, OpCodes.Blt_S, scalar: false);
  105. }
  106. public static void Cmtst_S(ILEmitterCtx context)
  107. {
  108. EmitCmtst(context, scalar: true);
  109. }
  110. public static void Cmtst_V(ILEmitterCtx context)
  111. {
  112. EmitCmtst(context, scalar: false);
  113. }
  114. public static void Fccmp_S(ILEmitterCtx context)
  115. {
  116. OpCodeSimdFcond64 op = (OpCodeSimdFcond64)context.CurrOp;
  117. ILLabel lblTrue = new ILLabel();
  118. ILLabel lblEnd = new ILLabel();
  119. context.EmitCondBranch(lblTrue, op.Cond);
  120. context.EmitLdc_I4(op.Nzcv);
  121. EmitSetNzcv(context);
  122. context.Emit(OpCodes.Br, lblEnd);
  123. context.MarkLabel(lblTrue);
  124. EmitFcmpE(context, signalNaNs: false);
  125. context.MarkLabel(lblEnd);
  126. }
  127. public static void Fccmpe_S(ILEmitterCtx context)
  128. {
  129. OpCodeSimdFcond64 op = (OpCodeSimdFcond64)context.CurrOp;
  130. ILLabel lblTrue = new ILLabel();
  131. ILLabel lblEnd = new ILLabel();
  132. context.EmitCondBranch(lblTrue, op.Cond);
  133. context.EmitLdc_I4(op.Nzcv);
  134. EmitSetNzcv(context);
  135. context.Emit(OpCodes.Br, lblEnd);
  136. context.MarkLabel(lblTrue);
  137. EmitFcmpE(context, signalNaNs: true);
  138. context.MarkLabel(lblEnd);
  139. }
  140. public static void Fcmeq_S(ILEmitterCtx context)
  141. {
  142. if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse
  143. && Optimizations.UseSse2)
  144. {
  145. EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareEqualScalar));
  146. }
  147. else
  148. {
  149. EmitScalarFcmp(context, OpCodes.Beq_S);
  150. }
  151. }
  152. public static void Fcmeq_V(ILEmitterCtx context)
  153. {
  154. if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse
  155. && Optimizations.UseSse2)
  156. {
  157. EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareEqual));
  158. }
  159. else
  160. {
  161. EmitVectorFcmp(context, OpCodes.Beq_S);
  162. }
  163. }
  164. public static void Fcmge_S(ILEmitterCtx context)
  165. {
  166. if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse
  167. && Optimizations.UseSse2)
  168. {
  169. EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqualScalar));
  170. }
  171. else
  172. {
  173. EmitScalarFcmp(context, OpCodes.Bge_S);
  174. }
  175. }
  176. public static void Fcmge_V(ILEmitterCtx context)
  177. {
  178. if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse
  179. && Optimizations.UseSse2)
  180. {
  181. EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanOrEqual));
  182. }
  183. else
  184. {
  185. EmitVectorFcmp(context, OpCodes.Bge_S);
  186. }
  187. }
  188. public static void Fcmgt_S(ILEmitterCtx context)
  189. {
  190. if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse
  191. && Optimizations.UseSse2)
  192. {
  193. EmitScalarSseOrSse2OpF(context, nameof(Sse.CompareGreaterThanScalar));
  194. }
  195. else
  196. {
  197. EmitScalarFcmp(context, OpCodes.Bgt_S);
  198. }
  199. }
  200. public static void Fcmgt_V(ILEmitterCtx context)
  201. {
  202. if (context.CurrOp is OpCodeSimdReg64 && Optimizations.UseSse
  203. && Optimizations.UseSse2)
  204. {
  205. EmitVectorSseOrSse2OpF(context, nameof(Sse.CompareGreaterThan));
  206. }
  207. else
  208. {
  209. EmitVectorFcmp(context, OpCodes.Bgt_S);
  210. }
  211. }
  212. public static void Fcmle_S(ILEmitterCtx context)
  213. {
  214. EmitScalarFcmp(context, OpCodes.Ble_S);
  215. }
  216. public static void Fcmle_V(ILEmitterCtx context)
  217. {
  218. EmitVectorFcmp(context, OpCodes.Ble_S);
  219. }
  220. public static void Fcmlt_S(ILEmitterCtx context)
  221. {
  222. EmitScalarFcmp(context, OpCodes.Blt_S);
  223. }
  224. public static void Fcmlt_V(ILEmitterCtx context)
  225. {
  226. EmitVectorFcmp(context, OpCodes.Blt_S);
  227. }
  228. public static void Fcmp_S(ILEmitterCtx context)
  229. {
  230. EmitFcmpE(context, signalNaNs: false);
  231. }
  232. public static void Fcmpe_S(ILEmitterCtx context)
  233. {
  234. EmitFcmpE(context, signalNaNs: true);
  235. }
  236. private static void EmitFcmpE(ILEmitterCtx context, bool signalNaNs)
  237. {
  238. OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
  239. bool cmpWithZero = !(op is OpCodeSimdFcond64) ? op.Bit3 : false;
  240. if (Optimizations.FastFP && Optimizations.UseSse2)
  241. {
  242. if (op.Size == 0)
  243. {
  244. Type[] typesCmp = new Type[] { typeof(Vector128<float>), typeof(Vector128<float>) };
  245. ILLabel lblNaN = new ILLabel();
  246. ILLabel lblEnd = new ILLabel();
  247. context.EmitLdvec(op.Rn);
  248. context.Emit(OpCodes.Dup);
  249. context.EmitStvectmp();
  250. if (cmpWithZero)
  251. {
  252. VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
  253. }
  254. else
  255. {
  256. context.EmitLdvec(op.Rm);
  257. }
  258. context.Emit(OpCodes.Dup);
  259. context.EmitStvectmp2();
  260. context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareOrderedScalar), typesCmp));
  261. VectorHelper.EmitCall(context, nameof(VectorHelper.VectorSingleZero));
  262. context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareEqualOrderedScalar), typesCmp));
  263. context.Emit(OpCodes.Brtrue_S, lblNaN);
  264. context.EmitLdc_I4(0);
  265. context.EmitLdvectmp();
  266. context.EmitLdvectmp2();
  267. context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareGreaterThanOrEqualOrderedScalar), typesCmp));
  268. context.EmitLdvectmp();
  269. context.EmitLdvectmp2();
  270. context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareEqualOrderedScalar), typesCmp));
  271. context.EmitLdvectmp();
  272. context.EmitLdvectmp2();
  273. context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.CompareLessThanOrderedScalar), typesCmp));
  274. context.EmitStflg((int)PState.NBit);
  275. context.EmitStflg((int)PState.ZBit);
  276. context.EmitStflg((int)PState.CBit);
  277. context.EmitStflg((int)PState.VBit);
  278. context.Emit(OpCodes.Br_S, lblEnd);
  279. context.MarkLabel(lblNaN);
  280. context.EmitLdc_I4(1);
  281. context.Emit(OpCodes.Dup);
  282. context.EmitLdc_I4(0);
  283. context.Emit(OpCodes.Dup);
  284. context.EmitStflg((int)PState.NBit);
  285. context.EmitStflg((int)PState.ZBit);
  286. context.EmitStflg((int)PState.CBit);
  287. context.EmitStflg((int)PState.VBit);
  288. context.MarkLabel(lblEnd);
  289. }
  290. else /* if (op.Size == 1) */
  291. {
  292. Type[] typesCmp = new Type[] { typeof(Vector128<double>), typeof(Vector128<double>) };
  293. ILLabel lblNaN = new ILLabel();
  294. ILLabel lblEnd = new ILLabel();
  295. EmitLdvecWithCastToDouble(context, op.Rn);
  296. context.Emit(OpCodes.Dup);
  297. context.EmitStvectmp();
  298. if (cmpWithZero)
  299. {
  300. VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero));
  301. }
  302. else
  303. {
  304. EmitLdvecWithCastToDouble(context, op.Rm);
  305. }
  306. context.Emit(OpCodes.Dup);
  307. context.EmitStvectmp2();
  308. context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareOrderedScalar), typesCmp));
  309. VectorHelper.EmitCall(context, nameof(VectorHelper.VectorDoubleZero));
  310. context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqualOrderedScalar), typesCmp));
  311. context.Emit(OpCodes.Brtrue_S, lblNaN);
  312. context.EmitLdc_I4(0);
  313. context.EmitLdvectmp();
  314. context.EmitLdvectmp2();
  315. context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareGreaterThanOrEqualOrderedScalar), typesCmp));
  316. context.EmitLdvectmp();
  317. context.EmitLdvectmp2();
  318. context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareEqualOrderedScalar), typesCmp));
  319. context.EmitLdvectmp();
  320. context.EmitLdvectmp2();
  321. context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.CompareLessThanOrderedScalar), typesCmp));
  322. context.EmitStflg((int)PState.NBit);
  323. context.EmitStflg((int)PState.ZBit);
  324. context.EmitStflg((int)PState.CBit);
  325. context.EmitStflg((int)PState.VBit);
  326. context.Emit(OpCodes.Br_S, lblEnd);
  327. context.MarkLabel(lblNaN);
  328. context.EmitLdc_I4(1);
  329. context.Emit(OpCodes.Dup);
  330. context.EmitLdc_I4(0);
  331. context.Emit(OpCodes.Dup);
  332. context.EmitStflg((int)PState.NBit);
  333. context.EmitStflg((int)PState.ZBit);
  334. context.EmitStflg((int)PState.CBit);
  335. context.EmitStflg((int)PState.VBit);
  336. context.MarkLabel(lblEnd);
  337. }
  338. }
  339. else
  340. {
  341. EmitVectorExtractF(context, op.Rn, 0, op.Size);
  342. if (cmpWithZero)
  343. {
  344. if (op.Size == 0)
  345. {
  346. context.EmitLdc_R4(0f);
  347. }
  348. else // if (op.Size == 1)
  349. {
  350. context.EmitLdc_R8(0d);
  351. }
  352. }
  353. else
  354. {
  355. EmitVectorExtractF(context, op.Rm, 0, op.Size);
  356. }
  357. context.EmitLdc_I4(!signalNaNs ? 0 : 1);
  358. EmitSoftFloatCall(context, nameof(SoftFloat32.FPCompare));
  359. EmitSetNzcv(context);
  360. }
  361. }
  362. private static void EmitCmp(ILEmitterCtx context, OpCode ilOp, bool scalar)
  363. {
  364. OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
  365. int bytes = op.GetBitsCount() >> 3;
  366. int elems = !scalar ? bytes >> op.Size : 1;
  367. ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size));
  368. for (int index = 0; index < elems; index++)
  369. {
  370. EmitVectorExtractSx(context, op.Rn, index, op.Size);
  371. if (op is OpCodeSimdReg64 binOp)
  372. {
  373. EmitVectorExtractSx(context, binOp.Rm, index, op.Size);
  374. }
  375. else
  376. {
  377. context.EmitLdc_I8(0L);
  378. }
  379. ILLabel lblTrue = new ILLabel();
  380. ILLabel lblEnd = new ILLabel();
  381. context.Emit(ilOp, lblTrue);
  382. EmitVectorInsert(context, op.Rd, index, op.Size, 0);
  383. context.Emit(OpCodes.Br_S, lblEnd);
  384. context.MarkLabel(lblTrue);
  385. EmitVectorInsert(context, op.Rd, index, op.Size, (long)szMask);
  386. context.MarkLabel(lblEnd);
  387. }
  388. if ((op.RegisterSize == RegisterSize.Simd64) || scalar)
  389. {
  390. EmitVectorZeroUpper(context, op.Rd);
  391. }
  392. }
  393. private static void EmitCmtst(ILEmitterCtx context, bool scalar)
  394. {
  395. OpCodeSimdReg64 op = (OpCodeSimdReg64)context.CurrOp;
  396. int bytes = op.GetBitsCount() >> 3;
  397. int elems = !scalar ? bytes >> op.Size : 1;
  398. ulong szMask = ulong.MaxValue >> (64 - (8 << op.Size));
  399. for (int index = 0; index < elems; index++)
  400. {
  401. EmitVectorExtractZx(context, op.Rn, index, op.Size);
  402. EmitVectorExtractZx(context, op.Rm, index, op.Size);
  403. ILLabel lblTrue = new ILLabel();
  404. ILLabel lblEnd = new ILLabel();
  405. context.Emit(OpCodes.And);
  406. context.EmitLdc_I8(0L);
  407. context.Emit(OpCodes.Bne_Un_S, lblTrue);
  408. EmitVectorInsert(context, op.Rd, index, op.Size, 0);
  409. context.Emit(OpCodes.Br_S, lblEnd);
  410. context.MarkLabel(lblTrue);
  411. EmitVectorInsert(context, op.Rd, index, op.Size, (long)szMask);
  412. context.MarkLabel(lblEnd);
  413. }
  414. if ((op.RegisterSize == RegisterSize.Simd64) || scalar)
  415. {
  416. EmitVectorZeroUpper(context, op.Rd);
  417. }
  418. }
  419. private static void EmitScalarFcmp(ILEmitterCtx context, OpCode ilOp)
  420. {
  421. EmitFcmp(context, ilOp, 0, scalar: true);
  422. }
  423. private static void EmitVectorFcmp(ILEmitterCtx context, OpCode ilOp)
  424. {
  425. OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
  426. int sizeF = op.Size & 1;
  427. int bytes = op.GetBitsCount() >> 3;
  428. int elems = bytes >> sizeF + 2;
  429. for (int index = 0; index < elems; index++)
  430. {
  431. EmitFcmp(context, ilOp, index, scalar: false);
  432. }
  433. if (op.RegisterSize == RegisterSize.Simd64)
  434. {
  435. EmitVectorZeroUpper(context, op.Rd);
  436. }
  437. }
  438. private static void EmitFcmp(ILEmitterCtx context, OpCode ilOp, int index, bool scalar)
  439. {
  440. OpCodeSimd64 op = (OpCodeSimd64)context.CurrOp;
  441. int sizeF = op.Size & 1;
  442. ulong szMask = ulong.MaxValue >> (64 - (32 << sizeF));
  443. EmitVectorExtractF(context, op.Rn, index, sizeF);
  444. if (op is OpCodeSimdReg64 binOp)
  445. {
  446. EmitVectorExtractF(context, binOp.Rm, index, sizeF);
  447. }
  448. else if (sizeF == 0)
  449. {
  450. context.EmitLdc_R4(0f);
  451. }
  452. else /* if (sizeF == 1) */
  453. {
  454. context.EmitLdc_R8(0d);
  455. }
  456. ILLabel lblTrue = new ILLabel();
  457. ILLabel lblEnd = new ILLabel();
  458. context.Emit(ilOp, lblTrue);
  459. if (scalar)
  460. {
  461. EmitVectorZeroAll(context, op.Rd);
  462. }
  463. else
  464. {
  465. EmitVectorInsert(context, op.Rd, index, sizeF + 2, 0);
  466. }
  467. context.Emit(OpCodes.Br_S, lblEnd);
  468. context.MarkLabel(lblTrue);
  469. if (scalar)
  470. {
  471. EmitVectorInsert(context, op.Rd, index, 3, (long)szMask);
  472. EmitVectorZeroUpper(context, op.Rd);
  473. }
  474. else
  475. {
  476. EmitVectorInsert(context, op.Rd, index, sizeF + 2, (long)szMask);
  477. }
  478. context.MarkLabel(lblEnd);
  479. }
  480. }
  481. }