InstEmitSimdShift32.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. using ARMeilleure.Decoders;
  2. using ARMeilleure.IntermediateRepresentation;
  3. using ARMeilleure.State;
  4. using ARMeilleure.Translation;
  5. using System;
  6. using System.Diagnostics;
  7. using System.Reflection;
  8. using static ARMeilleure.Instructions.InstEmitHelper;
  9. using static ARMeilleure.Instructions.InstEmitSimdHelper;
  10. using static ARMeilleure.Instructions.InstEmitSimdHelper32;
  11. using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
  12. namespace ARMeilleure.Instructions
  13. {
  14. static partial class InstEmit32
  15. {
  16. public static void Vqrshrn(ArmEmitterContext context)
  17. {
  18. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  19. EmitRoundShrImmSaturatingNarrowOp(context, op.U ? ShrImmSaturatingNarrowFlags.VectorZxZx : ShrImmSaturatingNarrowFlags.VectorSxSx);
  20. }
  21. public static void Vqrshrun(ArmEmitterContext context)
  22. {
  23. EmitRoundShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx);
  24. }
  25. public static void Vqshrn(ArmEmitterContext context)
  26. {
  27. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  28. EmitShrImmSaturatingNarrowOp(context, op.U ? ShrImmSaturatingNarrowFlags.VectorZxZx : ShrImmSaturatingNarrowFlags.VectorSxSx);
  29. }
  30. public static void Vqshrun(ArmEmitterContext context)
  31. {
  32. EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.VectorSxZx);
  33. }
  34. public static void Vrshr(ArmEmitterContext context)
  35. {
  36. EmitRoundShrImmOp(context, accumulate: false);
  37. }
  38. public static void Vrshrn(ArmEmitterContext context)
  39. {
  40. EmitRoundShrImmNarrowOp(context, signed: false);
  41. }
  42. public static void Vrsra(ArmEmitterContext context)
  43. {
  44. EmitRoundShrImmOp(context, accumulate: true);
  45. }
  46. public static void Vshl(ArmEmitterContext context)
  47. {
  48. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  49. EmitVectorUnaryOpZx32(context, (op1) => context.ShiftLeft(op1, Const(op.Shift)));
  50. }
  51. public static void Vshl_I(ArmEmitterContext context)
  52. {
  53. OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
  54. if (op.U)
  55. {
  56. EmitVectorBinaryOpZx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, true));
  57. }
  58. else
  59. {
  60. EmitVectorBinaryOpSx32(context, (op1, op2) => EmitShlRegOp(context, op2, op1, op.Size, false));
  61. }
  62. }
  63. public static void Vshll(ArmEmitterContext context)
  64. {
  65. OpCode32SimdShImmLong op = (OpCode32SimdShImmLong)context.CurrOp;
  66. Operand res = context.VectorZero();
  67. int elems = op.GetBytesCount() >> op.Size;
  68. for (int index = 0; index < elems; index++)
  69. {
  70. Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
  71. if (op.Size == 2)
  72. {
  73. if (op.U)
  74. {
  75. me = context.ZeroExtend32(OperandType.I64, me);
  76. }
  77. else
  78. {
  79. me = context.SignExtend32(OperandType.I64, me);
  80. }
  81. }
  82. me = context.ShiftLeft(me, Const(op.Shift));
  83. res = EmitVectorInsert(context, res, me, index, op.Size + 1);
  84. }
  85. context.Copy(GetVecA32(op.Qd), res);
  86. }
  87. public static void Vshr(ArmEmitterContext context)
  88. {
  89. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  90. int shift = GetImmShr(op);
  91. int maxShift = (8 << op.Size) - 1;
  92. if (op.U)
  93. {
  94. EmitVectorUnaryOpZx32(context, (op1) => (shift > maxShift) ? Const(op1.Type, 0) : context.ShiftRightUI(op1, Const(shift)));
  95. }
  96. else
  97. {
  98. EmitVectorUnaryOpSx32(context, (op1) => context.ShiftRightSI(op1, Const(Math.Min(maxShift, shift))));
  99. }
  100. }
  101. public static void Vshrn(ArmEmitterContext context)
  102. {
  103. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  104. int shift = GetImmShr(op);
  105. EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
  106. }
  107. public static void Vsra(ArmEmitterContext context)
  108. {
  109. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  110. int shift = GetImmShr(op);
  111. int maxShift = (8 << op.Size) - 1;
  112. if (op.U)
  113. {
  114. EmitVectorImmBinaryQdQmOpZx32(context, (op1, op2) =>
  115. {
  116. Operand shiftRes = shift > maxShift ? Const(op2.Type, 0) : context.ShiftRightUI(op2, Const(shift));
  117. return context.Add(op1, shiftRes);
  118. });
  119. }
  120. else
  121. {
  122. EmitVectorImmBinaryQdQmOpSx32(context, (op1, op2) => context.Add(op1, context.ShiftRightSI(op2, Const(Math.Min(maxShift, shift)))));
  123. }
  124. }
  125. public static void EmitRoundShrImmOp(ArmEmitterContext context, bool accumulate)
  126. {
  127. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  128. int shift = GetImmShr(op);
  129. long roundConst = 1L << (shift - 1);
  130. if (op.U)
  131. {
  132. if (op.Size < 2)
  133. {
  134. EmitVectorUnaryOpZx32(context, (op1) =>
  135. {
  136. op1 = context.Add(op1, Const(op1.Type, roundConst));
  137. return context.ShiftRightUI(op1, Const(shift));
  138. }, accumulate);
  139. }
  140. else if (op.Size == 2)
  141. {
  142. EmitVectorUnaryOpZx32(context, (op1) =>
  143. {
  144. op1 = context.ZeroExtend32(OperandType.I64, op1);
  145. op1 = context.Add(op1, Const(op1.Type, roundConst));
  146. return context.ConvertI64ToI32(context.ShiftRightUI(op1, Const(shift)));
  147. }, accumulate);
  148. }
  149. else /* if (op.Size == 3) */
  150. {
  151. EmitVectorUnaryOpZx32(context, (op1) => EmitShrImm64(context, op1, signed: false, roundConst, shift), accumulate);
  152. }
  153. }
  154. else
  155. {
  156. if (op.Size < 2)
  157. {
  158. EmitVectorUnaryOpSx32(context, (op1) =>
  159. {
  160. op1 = context.Add(op1, Const(op1.Type, roundConst));
  161. return context.ShiftRightSI(op1, Const(shift));
  162. }, accumulate);
  163. }
  164. else if (op.Size == 2)
  165. {
  166. EmitVectorUnaryOpSx32(context, (op1) =>
  167. {
  168. op1 = context.SignExtend32(OperandType.I64, op1);
  169. op1 = context.Add(op1, Const(op1.Type, roundConst));
  170. return context.ConvertI64ToI32(context.ShiftRightSI(op1, Const(shift)));
  171. }, accumulate);
  172. }
  173. else /* if (op.Size == 3) */
  174. {
  175. EmitVectorUnaryOpZx32(context, (op1) => EmitShrImm64(context, op1, signed: true, roundConst, shift), accumulate);
  176. }
  177. }
  178. }
  179. private static void EmitRoundShrImmNarrowOp(ArmEmitterContext context, bool signed)
  180. {
  181. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  182. int shift = GetImmShr(op);
  183. long roundConst = 1L << (shift - 1);
  184. EmitVectorUnaryNarrowOp32(context, (op1) =>
  185. {
  186. if (op.Size <= 1)
  187. {
  188. op1 = context.Add(op1, Const(op1.Type, roundConst));
  189. op1 = signed ? context.ShiftRightSI(op1, Const(shift)) : context.ShiftRightUI(op1, Const(shift));
  190. }
  191. else /* if (op.Size == 2 && round) */
  192. {
  193. op1 = EmitShrImm64(context, op1, signed, roundConst, shift); // shift <= 32
  194. }
  195. return op1;
  196. }, signed);
  197. }
  198. private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned)
  199. {
  200. if (shiftLsB.Type == OperandType.I64)
  201. {
  202. shiftLsB = context.ConvertI64ToI32(shiftLsB);
  203. }
  204. shiftLsB = context.SignExtend8(OperandType.I32, shiftLsB);
  205. Debug.Assert((uint)size < 4u);
  206. Operand negShiftLsB = context.Negate(shiftLsB);
  207. Operand isPositive = context.ICompareGreaterOrEqual(shiftLsB, Const(0));
  208. Operand shl = context.ShiftLeft(op, shiftLsB);
  209. Operand shr = unsigned ? context.ShiftRightUI(op, negShiftLsB) : context.ShiftRightSI(op, negShiftLsB);
  210. Operand res = context.ConditionalSelect(isPositive, shl, shr);
  211. if (unsigned)
  212. {
  213. Operand isOutOfRange = context.BitwiseOr(
  214. context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size)),
  215. context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size)));
  216. return context.ConditionalSelect(isOutOfRange, Const(op.Type, 0), res);
  217. }
  218. else
  219. {
  220. Operand isOutOfRange0 = context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size));
  221. Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size));
  222. // Also zero if shift is too negative, but value was positive.
  223. isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(op.Type, 0))));
  224. Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1);
  225. return context.ConditionalSelect(isOutOfRange0, Const(op.Type, 0), context.ConditionalSelect(isOutOfRangeN, min, res));
  226. }
  227. }
  228. [Flags]
  229. private enum ShrImmSaturatingNarrowFlags
  230. {
  231. Scalar = 1 << 0,
  232. SignedSrc = 1 << 1,
  233. SignedDst = 1 << 2,
  234. Round = 1 << 3,
  235. ScalarSxSx = Scalar | SignedSrc | SignedDst,
  236. ScalarSxZx = Scalar | SignedSrc,
  237. ScalarZxZx = Scalar,
  238. VectorSxSx = SignedSrc | SignedDst,
  239. VectorSxZx = SignedSrc,
  240. VectorZxZx = 0
  241. }
  242. private static void EmitRoundShrImmSaturatingNarrowOp(ArmEmitterContext context, ShrImmSaturatingNarrowFlags flags)
  243. {
  244. EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.Round | flags);
  245. }
  246. private static void EmitShrImmSaturatingNarrowOp(ArmEmitterContext context, ShrImmSaturatingNarrowFlags flags)
  247. {
  248. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  249. bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0;
  250. bool signedSrc = (flags & ShrImmSaturatingNarrowFlags.SignedSrc) != 0;
  251. bool signedDst = (flags & ShrImmSaturatingNarrowFlags.SignedDst) != 0;
  252. bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0;
  253. if (scalar)
  254. {
  255. // TODO: Support scalar operation.
  256. throw new NotImplementedException();
  257. }
  258. int shift = GetImmShr(op);
  259. long roundConst = 1L << (shift - 1);
  260. EmitVectorUnaryNarrowOp32(context, (op1) =>
  261. {
  262. if (op.Size <= 1 || !round)
  263. {
  264. if (round)
  265. {
  266. op1 = context.Add(op1, Const(op1.Type, roundConst));
  267. }
  268. op1 = signedSrc ? context.ShiftRightSI(op1, Const(shift)) : context.ShiftRightUI(op1, Const(shift));
  269. }
  270. else /* if (op.Size == 2 && round) */
  271. {
  272. op1 = EmitShrImm64(context, op1, signedSrc, roundConst, shift); // shift <= 32
  273. }
  274. return EmitSatQ(context, op1, 8 << op.Size, signedSrc, signedDst);
  275. }, signedSrc);
  276. }
  277. private static int GetImmShr(OpCode32SimdShImm op)
  278. {
  279. return (8 << op.Size) - op.Shift; // Shr amount is flipped.
  280. }
  281. // dst64 = (Int(src64, signed) + roundConst) >> shift;
  282. private static Operand EmitShrImm64(
  283. ArmEmitterContext context,
  284. Operand value,
  285. bool signed,
  286. long roundConst,
  287. int shift)
  288. {
  289. MethodInfo info = signed
  290. ? typeof(SoftFallback).GetMethod(nameof(SoftFallback.SignedShrImm64))
  291. : typeof(SoftFallback).GetMethod(nameof(SoftFallback.UnsignedShrImm64));
  292. return context.Call(info, value, Const(roundConst), Const(shift));
  293. }
  294. private static Operand EmitSatQ(ArmEmitterContext context, Operand value, int eSize, bool signedSrc, bool signedDst)
  295. {
  296. Debug.Assert(eSize <= 32);
  297. long intMin = signedDst ? -(1L << (eSize - 1)) : 0;
  298. long intMax = signedDst ? (1L << (eSize - 1)) - 1 : (1L << eSize) - 1;
  299. Operand gt = signedSrc
  300. ? context.ICompareGreater(value, Const(value.Type, intMax))
  301. : context.ICompareGreaterUI(value, Const(value.Type, intMax));
  302. Operand lt = signedSrc
  303. ? context.ICompareLess(value, Const(value.Type, intMin))
  304. : context.ICompareLessUI(value, Const(value.Type, intMin));
  305. value = context.ConditionalSelect(gt, Const(value.Type, intMax), value);
  306. value = context.ConditionalSelect(lt, Const(value.Type, intMin), value);
  307. Operand lblNoSat = Label();
  308. context.BranchIfFalse(lblNoSat, context.BitwiseOr(gt, lt));
  309. SetFpFlag(context, FPState.QcFlag, Const(1));
  310. context.MarkLabel(lblNoSat);
  311. return value;
  312. }
  313. }
  314. }