InstEmitSimdShift32.cs 14 KB

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