InstEmitSimdShift32.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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 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. private static Operand EmitShlRegOp(ArmEmitterContext context, Operand op, Operand shiftLsB, int size, bool unsigned)
  114. {
  115. if (shiftLsB.Type == OperandType.I64)
  116. {
  117. shiftLsB = context.ConvertI64ToI32(shiftLsB);
  118. }
  119. shiftLsB = context.SignExtend8(OperandType.I32, shiftLsB);
  120. Debug.Assert((uint)size < 4u);
  121. Operand negShiftLsB = context.Negate(shiftLsB);
  122. Operand isPositive = context.ICompareGreaterOrEqual(shiftLsB, Const(0));
  123. Operand shl = context.ShiftLeft(op, shiftLsB);
  124. Operand shr = unsigned ? context.ShiftRightUI(op, negShiftLsB) : context.ShiftRightSI(op, negShiftLsB);
  125. Operand res = context.ConditionalSelect(isPositive, shl, shr);
  126. if (unsigned)
  127. {
  128. Operand isOutOfRange = context.BitwiseOr(
  129. context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size)),
  130. context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size)));
  131. return context.ConditionalSelect(isOutOfRange, Const(op.Type, 0), res);
  132. }
  133. else
  134. {
  135. Operand isOutOfRange0 = context.ICompareGreaterOrEqual(shiftLsB, Const(8 << size));
  136. Operand isOutOfRangeN = context.ICompareGreaterOrEqual(negShiftLsB, Const(8 << size));
  137. // Also zero if shift is too negative, but value was positive.
  138. isOutOfRange0 = context.BitwiseOr(isOutOfRange0, context.BitwiseAnd(isOutOfRangeN, context.ICompareGreaterOrEqual(op, Const(op.Type, 0))));
  139. Operand min = (op.Type == OperandType.I64) ? Const(-1L) : Const(-1);
  140. return context.ConditionalSelect(isOutOfRange0, Const(op.Type, 0), context.ConditionalSelect(isOutOfRangeN, min, res));
  141. }
  142. }
  143. [Flags]
  144. private enum ShrImmSaturatingNarrowFlags
  145. {
  146. Scalar = 1 << 0,
  147. SignedSrc = 1 << 1,
  148. SignedDst = 1 << 2,
  149. Round = 1 << 3,
  150. ScalarSxSx = Scalar | SignedSrc | SignedDst,
  151. ScalarSxZx = Scalar | SignedSrc,
  152. ScalarZxZx = Scalar,
  153. VectorSxSx = SignedSrc | SignedDst,
  154. VectorSxZx = SignedSrc,
  155. VectorZxZx = 0
  156. }
  157. private static void EmitRoundShrImmSaturatingNarrowOp(ArmEmitterContext context, ShrImmSaturatingNarrowFlags flags)
  158. {
  159. EmitShrImmSaturatingNarrowOp(context, ShrImmSaturatingNarrowFlags.Round | flags);
  160. }
  161. private static void EmitShrImmSaturatingNarrowOp(ArmEmitterContext context, ShrImmSaturatingNarrowFlags flags)
  162. {
  163. OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
  164. bool scalar = (flags & ShrImmSaturatingNarrowFlags.Scalar) != 0;
  165. bool signedSrc = (flags & ShrImmSaturatingNarrowFlags.SignedSrc) != 0;
  166. bool signedDst = (flags & ShrImmSaturatingNarrowFlags.SignedDst) != 0;
  167. bool round = (flags & ShrImmSaturatingNarrowFlags.Round) != 0;
  168. if (scalar)
  169. {
  170. // TODO: Support scalar operation.
  171. throw new NotImplementedException();
  172. }
  173. int shift = GetImmShr(op);
  174. long roundConst = 1L << (shift - 1);
  175. EmitVectorUnaryNarrowOp32(context, (op1) =>
  176. {
  177. if (op.Size <= 1 || !round)
  178. {
  179. if (round)
  180. {
  181. op1 = context.Add(op1, Const(op1.Type, roundConst));
  182. }
  183. op1 = signedSrc ? context.ShiftRightSI(op1, Const(shift)) : context.ShiftRightUI(op1, Const(shift));
  184. }
  185. else /* if (op.Size == 2 && round) */
  186. {
  187. op1 = EmitShrImm64(context, op1, signedSrc, roundConst, shift); // shift <= 32
  188. }
  189. return EmitSatQ(context, op1, 8 << op.Size, signedDst);
  190. }, signedSrc);
  191. }
  192. private static int GetImmShr(OpCode32SimdShImm op)
  193. {
  194. return (8 << op.Size) - op.Shift; // Shr amount is flipped.
  195. }
  196. // dst64 = (Int(src64, signed) + roundConst) >> shift;
  197. private static Operand EmitShrImm64(
  198. ArmEmitterContext context,
  199. Operand value,
  200. bool signed,
  201. long roundConst,
  202. int shift)
  203. {
  204. Delegate dlg = signed
  205. ? (Delegate)new _S64_S64_S64_S32(SoftFallback.SignedShrImm64)
  206. : (Delegate)new _U64_U64_S64_S32(SoftFallback.UnsignedShrImm64);
  207. return context.Call(dlg, value, Const(roundConst), Const(shift));
  208. }
  209. private static Operand EmitSatQ(ArmEmitterContext context, Operand value, int eSize, bool signed)
  210. {
  211. Debug.Assert(eSize <= 32);
  212. long intMin = signed ? -(1L << (eSize - 1)) : 0;
  213. long intMax = signed ? (1L << (eSize - 1)) - 1 : (1L << eSize) - 1;
  214. Operand gt = context.ICompareGreater(value, Const(value.Type, intMax));
  215. Operand lt = context.ICompareLess(value, Const(value.Type, intMin));
  216. value = context.ConditionalSelect(gt, Const(value.Type, intMax), value);
  217. value = context.ConditionalSelect(lt, Const(value.Type, intMin), value);
  218. Operand lblNoSat = Label();
  219. context.BranchIfFalse(lblNoSat, context.BitwiseOr(gt, lt));
  220. // TODO: Set QC (to 1) on FPSCR here.
  221. context.MarkLabel(lblNoSat);
  222. return value;
  223. }
  224. }
  225. }