InstEmitSimdShift32.cs 11 KB

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