InstEmitSimdCvt32.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. using ARMeilleure.Decoders;
  2. using ARMeilleure.IntermediateRepresentation;
  3. using ARMeilleure.Translation;
  4. using System;
  5. using System.Diagnostics;
  6. using static ARMeilleure.Instructions.InstEmitSimdHelper;
  7. using static ARMeilleure.Instructions.InstEmitSimdHelper32;
  8. using static ARMeilleure.IntermediateRepresentation.OperandHelper;
  9. namespace ARMeilleure.Instructions
  10. {
  11. static partial class InstEmit32
  12. {
  13. private static int FlipVdBits(int vd, bool lowBit)
  14. {
  15. if (lowBit)
  16. {
  17. // Move the low bit to the top.
  18. return ((vd & 0x1) << 4) | (vd >> 1);
  19. }
  20. else
  21. {
  22. // Move the high bit to the bottom.
  23. return ((vd & 0xf) << 1) | (vd >> 4);
  24. }
  25. }
  26. private static Operand EmitSaturateFloatToInt(ArmEmitterContext context, Operand op1, bool unsigned)
  27. {
  28. if (op1.Type == OperandType.FP64)
  29. {
  30. if (unsigned)
  31. {
  32. return context.Call(new _U32_F64(SoftFallback.SatF64ToU32), op1);
  33. }
  34. else
  35. {
  36. return context.Call(new _S32_F64(SoftFallback.SatF64ToS32), op1);
  37. }
  38. }
  39. else
  40. {
  41. if (unsigned)
  42. {
  43. return context.Call(new _U32_F32(SoftFallback.SatF32ToU32), op1);
  44. }
  45. else
  46. {
  47. return context.Call(new _S32_F32(SoftFallback.SatF32ToS32), op1);
  48. }
  49. }
  50. }
  51. public static void Vcvt_V(ArmEmitterContext context)
  52. {
  53. OpCode32Simd op = (OpCode32Simd)context.CurrOp;
  54. bool unsigned = (op.Opc & 1) != 0;
  55. bool toInteger = (op.Opc & 2) != 0;
  56. OperandType floatSize = (op.Size == 2) ? OperandType.FP32 : OperandType.FP64;
  57. if (toInteger)
  58. {
  59. EmitVectorUnaryOpF32(context, (op1) =>
  60. {
  61. return EmitSaturateFloatToInt(context, op1, unsigned);
  62. });
  63. }
  64. else
  65. {
  66. if (unsigned)
  67. {
  68. EmitVectorUnaryOpZx32(context, (op1) => EmitFPConvert(context, op1, floatSize, false));
  69. }
  70. else
  71. {
  72. EmitVectorUnaryOpSx32(context, (op1) => EmitFPConvert(context, op1, floatSize, true));
  73. }
  74. }
  75. }
  76. public static void Vcvt_FD(ArmEmitterContext context)
  77. {
  78. OpCode32SimdS op = (OpCode32SimdS)context.CurrOp;
  79. int vm = op.Vm;
  80. int vd;
  81. if (op.Size == 3)
  82. {
  83. vd = FlipVdBits(op.Vd, false);
  84. // Double to single.
  85. Operand fp = ExtractScalar(context, OperandType.FP64, vm);
  86. Operand res = context.ConvertToFP(OperandType.FP32, fp);
  87. InsertScalar(context, vd, res);
  88. }
  89. else
  90. {
  91. vd = FlipVdBits(op.Vd, true);
  92. // Single to double.
  93. Operand fp = ExtractScalar(context, OperandType.FP32, vm);
  94. Operand res = context.ConvertToFP(OperandType.FP64, fp);
  95. InsertScalar(context, vd, res);
  96. }
  97. }
  98. public static void Vcvt_FI(ArmEmitterContext context)
  99. {
  100. OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
  101. bool toInteger = (op.Opc2 & 0b100) != 0;
  102. OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
  103. if (toInteger)
  104. {
  105. bool unsigned = (op.Opc2 & 1) == 0;
  106. bool roundWithFpscr = op.Opc != 1;
  107. Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
  108. Operand asInteger;
  109. // TODO: Fast Path.
  110. if (roundWithFpscr)
  111. {
  112. // These need to get the FPSCR value, so it's worth noting we'd need to do a c# call at some point.
  113. if (floatSize == OperandType.FP64)
  114. {
  115. if (unsigned)
  116. {
  117. asInteger = context.Call(new _U32_F64(SoftFallback.DoubleToUInt32), toConvert);
  118. }
  119. else
  120. {
  121. asInteger = context.Call(new _S32_F64(SoftFallback.DoubleToInt32), toConvert);
  122. }
  123. }
  124. else
  125. {
  126. if (unsigned)
  127. {
  128. asInteger = context.Call(new _U32_F32(SoftFallback.FloatToUInt32), toConvert);
  129. }
  130. else
  131. {
  132. asInteger = context.Call(new _S32_F32(SoftFallback.FloatToInt32), toConvert);
  133. }
  134. }
  135. }
  136. else
  137. {
  138. // Round towards zero.
  139. asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
  140. }
  141. InsertScalar(context, op.Vd, asInteger);
  142. }
  143. else
  144. {
  145. bool unsigned = op.Opc == 0;
  146. Operand toConvert = ExtractScalar(context, OperandType.I32, op.Vm);
  147. Operand asFloat = EmitFPConvert(context, toConvert, floatSize, !unsigned);
  148. InsertScalar(context, op.Vd, asFloat);
  149. }
  150. }
  151. public static Operand EmitRoundMathCall(ArmEmitterContext context, MidpointRounding roundMode, Operand n)
  152. {
  153. IOpCode32Simd op = (IOpCode32Simd)context.CurrOp;
  154. Delegate dlg;
  155. if ((op.Size & 1) == 0)
  156. {
  157. dlg = new _F32_F32_MidpointRounding(MathF.Round);
  158. }
  159. else /* if ((op.Size & 1) == 1) */
  160. {
  161. dlg = new _F64_F64_MidpointRounding(Math.Round);
  162. }
  163. return context.Call(dlg, n, Const((int)roundMode));
  164. }
  165. public static void Vcvt_R(ArmEmitterContext context)
  166. {
  167. OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
  168. OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
  169. bool unsigned = (op.Opc & 1) == 0;
  170. Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
  171. switch (op.Opc2)
  172. {
  173. case 0b00: // Away
  174. toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
  175. break;
  176. case 0b01: // Nearest
  177. toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
  178. break;
  179. case 0b10: // Towards positive infinity
  180. toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert);
  181. break;
  182. case 0b11: // Towards negative infinity
  183. toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert);
  184. break;
  185. }
  186. Operand asInteger;
  187. asInteger = EmitSaturateFloatToInt(context, toConvert, unsigned);
  188. InsertScalar(context, op.Vd, asInteger);
  189. }
  190. public static void Vrint_RM(ArmEmitterContext context)
  191. {
  192. OpCode32SimdCvtFI op = (OpCode32SimdCvtFI)context.CurrOp;
  193. OperandType floatSize = op.RegisterSize == RegisterSize.Int64 ? OperandType.FP64 : OperandType.FP32;
  194. Operand toConvert = ExtractScalar(context, floatSize, op.Vm);
  195. switch (op.Opc2)
  196. {
  197. case 0b00: // Away
  198. toConvert = EmitRoundMathCall(context, MidpointRounding.AwayFromZero, toConvert);
  199. break;
  200. case 0b01: // Nearest
  201. toConvert = EmitRoundMathCall(context, MidpointRounding.ToEven, toConvert);
  202. break;
  203. case 0b10: // Towards positive infinity
  204. toConvert = EmitUnaryMathCall(context, MathF.Ceiling, Math.Ceiling, toConvert);
  205. break;
  206. case 0b11: // Towards negative infinity
  207. toConvert = EmitUnaryMathCall(context, MathF.Floor, Math.Floor, toConvert);
  208. break;
  209. }
  210. InsertScalar(context, op.Vd, toConvert);
  211. }
  212. public static void Vrint_Z(ArmEmitterContext context)
  213. {
  214. EmitScalarUnaryOpF32(context, (op1) => EmitUnaryMathCall(context, MathF.Truncate, Math.Truncate, op1));
  215. }
  216. private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)
  217. {
  218. Debug.Assert(value.Type == OperandType.I32 || value.Type == OperandType.I64);
  219. if (signed)
  220. {
  221. return context.ConvertToFP(type, value);
  222. }
  223. else
  224. {
  225. return context.ConvertToFPUI(type, value);
  226. }
  227. }
  228. }
  229. }