VectorHelper.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. using ChocolArm64.State;
  2. using ChocolArm64.Translation;
  3. using System;
  4. using System.Runtime.CompilerServices;
  5. using System.Runtime.Intrinsics;
  6. using System.Runtime.Intrinsics.X86;
  7. namespace ChocolArm64.Instructions
  8. {
  9. static class VectorHelper
  10. {
  11. public static void EmitCall(ILEmitterCtx context, string name64, string name128)
  12. {
  13. bool isSimd64 = context.CurrOp.RegisterSize == RegisterSize.Simd64;
  14. context.EmitCall(typeof(VectorHelper), isSimd64 ? name64 : name128);
  15. }
  16. public static void EmitCall(ILEmitterCtx context, string mthdName)
  17. {
  18. context.EmitCall(typeof(VectorHelper), mthdName);
  19. }
  20. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  21. public static int SatF32ToS32(float value)
  22. {
  23. if (float.IsNaN(value)) return 0;
  24. return value >= int.MaxValue ? int.MaxValue :
  25. value <= int.MinValue ? int.MinValue : (int)value;
  26. }
  27. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  28. public static long SatF32ToS64(float value)
  29. {
  30. if (float.IsNaN(value)) return 0;
  31. return value >= long.MaxValue ? long.MaxValue :
  32. value <= long.MinValue ? long.MinValue : (long)value;
  33. }
  34. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  35. public static uint SatF32ToU32(float value)
  36. {
  37. if (float.IsNaN(value)) return 0;
  38. return value >= uint.MaxValue ? uint.MaxValue :
  39. value <= uint.MinValue ? uint.MinValue : (uint)value;
  40. }
  41. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  42. public static ulong SatF32ToU64(float value)
  43. {
  44. if (float.IsNaN(value)) return 0;
  45. return value >= ulong.MaxValue ? ulong.MaxValue :
  46. value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
  47. }
  48. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  49. public static int SatF64ToS32(double value)
  50. {
  51. if (double.IsNaN(value)) return 0;
  52. return value >= int.MaxValue ? int.MaxValue :
  53. value <= int.MinValue ? int.MinValue : (int)value;
  54. }
  55. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  56. public static long SatF64ToS64(double value)
  57. {
  58. if (double.IsNaN(value)) return 0;
  59. return value >= long.MaxValue ? long.MaxValue :
  60. value <= long.MinValue ? long.MinValue : (long)value;
  61. }
  62. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  63. public static uint SatF64ToU32(double value)
  64. {
  65. if (double.IsNaN(value)) return 0;
  66. return value >= uint.MaxValue ? uint.MaxValue :
  67. value <= uint.MinValue ? uint.MinValue : (uint)value;
  68. }
  69. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  70. public static ulong SatF64ToU64(double value)
  71. {
  72. if (double.IsNaN(value)) return 0;
  73. return value >= ulong.MaxValue ? ulong.MaxValue :
  74. value <= ulong.MinValue ? ulong.MinValue : (ulong)value;
  75. }
  76. public static double Round(double value, CpuThreadState state)
  77. {
  78. switch (state.FPRoundingMode())
  79. {
  80. case RoundMode.ToNearest: return Math.Round (value);
  81. case RoundMode.TowardsPlusInfinity: return Math.Ceiling (value);
  82. case RoundMode.TowardsMinusInfinity: return Math.Floor (value);
  83. case RoundMode.TowardsZero: return Math.Truncate(value);
  84. }
  85. throw new InvalidOperationException();
  86. }
  87. public static float RoundF(float value, CpuThreadState state)
  88. {
  89. switch (state.FPRoundingMode())
  90. {
  91. case RoundMode.ToNearest: return MathF.Round (value);
  92. case RoundMode.TowardsPlusInfinity: return MathF.Ceiling (value);
  93. case RoundMode.TowardsMinusInfinity: return MathF.Floor (value);
  94. case RoundMode.TowardsZero: return MathF.Truncate(value);
  95. }
  96. throw new InvalidOperationException();
  97. }
  98. public static Vector128<float> Tbl1_V64(
  99. Vector128<float> vector,
  100. Vector128<float> tb0)
  101. {
  102. return Tbl(vector, 8, tb0);
  103. }
  104. public static Vector128<float> Tbl1_V128(
  105. Vector128<float> vector,
  106. Vector128<float> tb0)
  107. {
  108. return Tbl(vector, 16, tb0);
  109. }
  110. public static Vector128<float> Tbl2_V64(
  111. Vector128<float> vector,
  112. Vector128<float> tb0,
  113. Vector128<float> tb1)
  114. {
  115. return Tbl(vector, 8, tb0, tb1);
  116. }
  117. public static Vector128<float> Tbl2_V128(
  118. Vector128<float> vector,
  119. Vector128<float> tb0,
  120. Vector128<float> tb1)
  121. {
  122. return Tbl(vector, 16, tb0, tb1);
  123. }
  124. public static Vector128<float> Tbl3_V64(
  125. Vector128<float> vector,
  126. Vector128<float> tb0,
  127. Vector128<float> tb1,
  128. Vector128<float> tb2)
  129. {
  130. return Tbl(vector, 8, tb0, tb1, tb2);
  131. }
  132. public static Vector128<float> Tbl3_V128(
  133. Vector128<float> vector,
  134. Vector128<float> tb0,
  135. Vector128<float> tb1,
  136. Vector128<float> tb2)
  137. {
  138. return Tbl(vector, 16, tb0, tb1, tb2);
  139. }
  140. public static Vector128<float> Tbl4_V64(
  141. Vector128<float> vector,
  142. Vector128<float> tb0,
  143. Vector128<float> tb1,
  144. Vector128<float> tb2,
  145. Vector128<float> tb3)
  146. {
  147. return Tbl(vector, 8, tb0, tb1, tb2, tb3);
  148. }
  149. public static Vector128<float> Tbl4_V128(
  150. Vector128<float> vector,
  151. Vector128<float> tb0,
  152. Vector128<float> tb1,
  153. Vector128<float> tb2,
  154. Vector128<float> tb3)
  155. {
  156. return Tbl(vector, 16, tb0, tb1, tb2, tb3);
  157. }
  158. private static Vector128<float> Tbl(Vector128<float> vector, int bytes, params Vector128<float>[] tb)
  159. {
  160. Vector128<float> res = new Vector128<float>();
  161. byte[] table = new byte[tb.Length * 16];
  162. for (byte index = 0; index < tb.Length; index++)
  163. for (byte index2 = 0; index2 < 16; index2++)
  164. {
  165. table[index * 16 + index2] = (byte)VectorExtractIntZx(tb[index], index2, 0);
  166. }
  167. for (byte index = 0; index < bytes; index++)
  168. {
  169. byte tblIdx = (byte)VectorExtractIntZx(vector, index, 0);
  170. if (tblIdx < table.Length)
  171. {
  172. res = VectorInsertInt(table[tblIdx], res, index, 0);
  173. }
  174. }
  175. return res;
  176. }
  177. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  178. public static double VectorExtractDouble(Vector128<float> vector, byte index)
  179. {
  180. if (Sse41.IsSupported)
  181. {
  182. return BitConverter.Int64BitsToDouble(Sse41.Extract(Sse.StaticCast<float, long>(vector), index));
  183. }
  184. else if (Sse2.IsSupported)
  185. {
  186. return BitConverter.Int64BitsToDouble((long)VectorExtractIntZx(vector, index, 3));
  187. }
  188. throw new PlatformNotSupportedException();
  189. }
  190. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  191. public static long VectorExtractIntSx(Vector128<float> vector, byte index, int size)
  192. {
  193. if (Sse41.IsSupported)
  194. {
  195. if (size == 0)
  196. {
  197. return (sbyte)Sse41.Extract(Sse.StaticCast<float, byte>(vector), index);
  198. }
  199. else if (size == 1)
  200. {
  201. return (short)Sse2.Extract(Sse.StaticCast<float, ushort>(vector), index);
  202. }
  203. else if (size == 2)
  204. {
  205. return Sse41.Extract(Sse.StaticCast<float, int>(vector), index);
  206. }
  207. else if (size == 3)
  208. {
  209. return Sse41.Extract(Sse.StaticCast<float, long>(vector), index);
  210. }
  211. else
  212. {
  213. throw new ArgumentOutOfRangeException(nameof(size));
  214. }
  215. }
  216. else if (Sse2.IsSupported)
  217. {
  218. if (size == 0)
  219. {
  220. return (sbyte)VectorExtractIntZx(vector, index, size);
  221. }
  222. else if (size == 1)
  223. {
  224. return (short)VectorExtractIntZx(vector, index, size);
  225. }
  226. else if (size == 2)
  227. {
  228. return (int)VectorExtractIntZx(vector, index, size);
  229. }
  230. else if (size == 3)
  231. {
  232. return (long)VectorExtractIntZx(vector, index, size);
  233. }
  234. else
  235. {
  236. throw new ArgumentOutOfRangeException(nameof(size));
  237. }
  238. }
  239. throw new PlatformNotSupportedException();
  240. }
  241. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  242. public static ulong VectorExtractIntZx(Vector128<float> vector, byte index, int size)
  243. {
  244. if (Sse41.IsSupported)
  245. {
  246. if (size == 0)
  247. {
  248. return Sse41.Extract(Sse.StaticCast<float, byte>(vector), index);
  249. }
  250. else if (size == 1)
  251. {
  252. return Sse2.Extract(Sse.StaticCast<float, ushort>(vector), index);
  253. }
  254. else if (size == 2)
  255. {
  256. return Sse41.Extract(Sse.StaticCast<float, uint>(vector), index);
  257. }
  258. else if (size == 3)
  259. {
  260. return Sse41.Extract(Sse.StaticCast<float, ulong>(vector), index);
  261. }
  262. else
  263. {
  264. throw new ArgumentOutOfRangeException(nameof(size));
  265. }
  266. }
  267. else if (Sse2.IsSupported)
  268. {
  269. int shortIdx = size == 0
  270. ? index >> 1
  271. : index << (size - 1);
  272. ushort value = Sse2.Extract(Sse.StaticCast<float, ushort>(vector), (byte)shortIdx);
  273. if (size == 0)
  274. {
  275. return (byte)(value >> (index & 1) * 8);
  276. }
  277. else if (size == 1)
  278. {
  279. return value;
  280. }
  281. else if (size == 2 || size == 3)
  282. {
  283. ushort value1 = Sse2.Extract(Sse.StaticCast<float, ushort>(vector), (byte)(shortIdx + 1));
  284. if (size == 2)
  285. {
  286. return (uint)(value | (value1 << 16));
  287. }
  288. ushort value2 = Sse2.Extract(Sse.StaticCast<float, ushort>(vector), (byte)(shortIdx + 2));
  289. ushort value3 = Sse2.Extract(Sse.StaticCast<float, ushort>(vector), (byte)(shortIdx + 3));
  290. return ((ulong)value << 0) |
  291. ((ulong)value1 << 16) |
  292. ((ulong)value2 << 32) |
  293. ((ulong)value3 << 48);
  294. }
  295. else
  296. {
  297. throw new ArgumentOutOfRangeException(nameof(size));
  298. }
  299. }
  300. throw new PlatformNotSupportedException();
  301. }
  302. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  303. public static float VectorExtractSingle(Vector128<float> vector, byte index)
  304. {
  305. if (Sse41.IsSupported)
  306. {
  307. return Sse41.Extract(vector, index);
  308. }
  309. else if (Sse2.IsSupported)
  310. {
  311. Vector128<ushort> shortVector = Sse.StaticCast<float, ushort>(vector);
  312. int low = Sse2.Extract(shortVector, (byte)(index * 2 + 0));
  313. int high = Sse2.Extract(shortVector, (byte)(index * 2 + 1));
  314. return BitConverter.Int32BitsToSingle(low | (high << 16));
  315. }
  316. throw new PlatformNotSupportedException();
  317. }
  318. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  319. public static Vector128<float> VectorInsertDouble(double value, Vector128<float> vector, byte index)
  320. {
  321. return VectorInsertInt((ulong)BitConverter.DoubleToInt64Bits(value), vector, index, 3);
  322. }
  323. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  324. public static Vector128<float> VectorInsertInt(ulong value, Vector128<float> vector, byte index, int size)
  325. {
  326. if (Sse41.IsSupported)
  327. {
  328. if (size == 0)
  329. {
  330. return Sse.StaticCast<byte, float>(Sse41.Insert(Sse.StaticCast<float, byte>(vector), (byte)value, index));
  331. }
  332. else if (size == 1)
  333. {
  334. return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse.StaticCast<float, ushort>(vector), (ushort)value, index));
  335. }
  336. else if (size == 2)
  337. {
  338. return Sse.StaticCast<uint, float>(Sse41.Insert(Sse.StaticCast<float, uint>(vector), (uint)value, index));
  339. }
  340. else if (size == 3)
  341. {
  342. return Sse.StaticCast<ulong, float>(Sse41.Insert(Sse.StaticCast<float, ulong>(vector), value, index));
  343. }
  344. else
  345. {
  346. throw new ArgumentOutOfRangeException(nameof(size));
  347. }
  348. }
  349. else if (Sse2.IsSupported)
  350. {
  351. Vector128<ushort> shortVector = Sse.StaticCast<float, ushort>(vector);
  352. int shortIdx = size == 0
  353. ? index >> 1
  354. : index << (size - 1);
  355. if (size == 0)
  356. {
  357. ushort shortVal = Sse2.Extract(Sse.StaticCast<float, ushort>(vector), (byte)shortIdx);
  358. int shift = (index & 1) * 8;
  359. shortVal &= (ushort)(0xff00 >> shift);
  360. shortVal |= (ushort)((byte)value << shift);
  361. return Sse.StaticCast<ushort, float>(Sse2.Insert(shortVector, shortVal, (byte)shortIdx));
  362. }
  363. else if (size == 1)
  364. {
  365. return Sse.StaticCast<ushort, float>(Sse2.Insert(Sse.StaticCast<float, ushort>(vector), (ushort)value, index));
  366. }
  367. else if (size == 2 || size == 3)
  368. {
  369. shortVector = Sse2.Insert(shortVector, (ushort)(value >> 0), (byte)(shortIdx + 0));
  370. shortVector = Sse2.Insert(shortVector, (ushort)(value >> 16), (byte)(shortIdx + 1));
  371. if (size == 3)
  372. {
  373. shortVector = Sse2.Insert(shortVector, (ushort)(value >> 32), (byte)(shortIdx + 2));
  374. shortVector = Sse2.Insert(shortVector, (ushort)(value >> 48), (byte)(shortIdx + 3));
  375. }
  376. return Sse.StaticCast<ushort, float>(shortVector);
  377. }
  378. else
  379. {
  380. throw new ArgumentOutOfRangeException(nameof(size));
  381. }
  382. }
  383. throw new PlatformNotSupportedException();
  384. }
  385. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  386. public static Vector128<float> VectorInsertSingle(float value, Vector128<float> vector, byte index)
  387. {
  388. if (Sse41.IsSupported)
  389. {
  390. //Note: The if/else if is necessary to enable the JIT to
  391. //produce a single INSERTPS instruction instead of the
  392. //jump table fallback.
  393. if (index == 0)
  394. {
  395. return Sse41.Insert(vector, value, 0x00);
  396. }
  397. else if (index == 1)
  398. {
  399. return Sse41.Insert(vector, value, 0x10);
  400. }
  401. else if (index == 2)
  402. {
  403. return Sse41.Insert(vector, value, 0x20);
  404. }
  405. else if (index == 3)
  406. {
  407. return Sse41.Insert(vector, value, 0x30);
  408. }
  409. else
  410. {
  411. throw new ArgumentOutOfRangeException(nameof(index));
  412. }
  413. }
  414. else if (Sse2.IsSupported)
  415. {
  416. int intValue = BitConverter.SingleToInt32Bits(value);
  417. ushort low = (ushort)(intValue >> 0);
  418. ushort high = (ushort)(intValue >> 16);
  419. Vector128<ushort> shortVector = Sse.StaticCast<float, ushort>(vector);
  420. shortVector = Sse2.Insert(shortVector, low, (byte)(index * 2 + 0));
  421. shortVector = Sse2.Insert(shortVector, high, (byte)(index * 2 + 1));
  422. return Sse.StaticCast<ushort, float>(shortVector);
  423. }
  424. throw new PlatformNotSupportedException();
  425. }
  426. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  427. public static Vector128<float> Sse41VectorInsertScalarSingle(float value, Vector128<float> vector)
  428. {
  429. //Note: 0b1110 is the mask to zero the upper bits.
  430. return Sse41.Insert(vector, value, 0b1110);
  431. }
  432. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  433. public static Vector128<float> VectorSingleZero()
  434. {
  435. if (Sse.IsSupported)
  436. {
  437. return Sse.SetZeroVector128();
  438. }
  439. throw new PlatformNotSupportedException();
  440. }
  441. }
  442. }