InstEmitSimdShift32.cs 12 KB

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