1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2016 Pedro Gonnet (pedro.gonnet@gmail.com)
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 #ifndef THIRD_PARTY_EIGEN3_EIGEN_SRC_CORE_ARCH_AVX512_MATHFUNCTIONS_H_
11 #define THIRD_PARTY_EIGEN3_EIGEN_SRC_CORE_ARCH_AVX512_MATHFUNCTIONS_H_
12 
13 namespace Eigen {
14 
15 namespace internal {
16 
17 // Disable the code for older versions of gcc that don't support many of the required avx512 instrinsics.
18 #if EIGEN_GNUC_AT_LEAST(5, 3) || EIGEN_COMP_CLANG  || EIGEN_COMP_MSVC >= 1923
19 
20 #define _EIGEN_DECLARE_CONST_Packet16f(NAME, X) \
21   const Packet16f p16f_##NAME = pset1<Packet16f>(X)
22 
23 #define _EIGEN_DECLARE_CONST_Packet16f_FROM_INT(NAME, X) \
24   const Packet16f p16f_##NAME =  preinterpret<Packet16f,Packet16i>(pset1<Packet16i>(X))
25 
26 #define _EIGEN_DECLARE_CONST_Packet8d(NAME, X) \
27   const Packet8d p8d_##NAME = pset1<Packet8d>(X)
28 
29 #define _EIGEN_DECLARE_CONST_Packet8d_FROM_INT64(NAME, X) \
30   const Packet8d p8d_##NAME = _mm512_castsi512_pd(_mm512_set1_epi64(X))
31 
32 #define _EIGEN_DECLARE_CONST_Packet16bf(NAME, X) \
33   const Packet16bf p16bf_##NAME = pset1<Packet16bf>(X)
34 
35 #define _EIGEN_DECLARE_CONST_Packet16bf_FROM_INT(NAME, X) \
36   const Packet16bf p16bf_##NAME =  preinterpret<Packet16bf,Packet16i>(pset1<Packet16i>(X))
37 
38 template <>
39 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet16f
40 plog<Packet16f>(const Packet16f& _x) {
41   return plog_float(_x);
42 }
43 
44 template <>
45 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet8d
46 plog<Packet8d>(const Packet8d& _x) {
47   return plog_double(_x);
48 }
49 
F16_PACKET_FUNCTION(Packet16f,Packet16h,plog)50 F16_PACKET_FUNCTION(Packet16f, Packet16h, plog)
51 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, plog)
52 
53 template <>
54 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet16f
55 plog2<Packet16f>(const Packet16f& _x) {
56   return plog2_float(_x);
57 }
58 
59 template <>
60 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet8d
61 plog2<Packet8d>(const Packet8d& _x) {
62   return plog2_double(_x);
63 }
64 
F16_PACKET_FUNCTION(Packet16f,Packet16h,plog2)65 F16_PACKET_FUNCTION(Packet16f, Packet16h, plog2)
66 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, plog2)
67 
68 // Exponential function. Works by writing "x = m*log(2) + r" where
69 // "m = floor(x/log(2)+1/2)" and "r" is the remainder. The result is then
70 // "exp(x) = 2^m*exp(r)" where exp(r) is in the range [-1,1).
71 template <>
72 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet16f
73 pexp<Packet16f>(const Packet16f& _x) {
74   _EIGEN_DECLARE_CONST_Packet16f(1, 1.0f);
75   _EIGEN_DECLARE_CONST_Packet16f(half, 0.5f);
76   _EIGEN_DECLARE_CONST_Packet16f(127, 127.0f);
77 
78   _EIGEN_DECLARE_CONST_Packet16f(exp_hi, 88.3762626647950f);
79   _EIGEN_DECLARE_CONST_Packet16f(exp_lo, -88.3762626647949f);
80 
81   _EIGEN_DECLARE_CONST_Packet16f(cephes_LOG2EF, 1.44269504088896341f);
82 
83   _EIGEN_DECLARE_CONST_Packet16f(cephes_exp_p0, 1.9875691500E-4f);
84   _EIGEN_DECLARE_CONST_Packet16f(cephes_exp_p1, 1.3981999507E-3f);
85   _EIGEN_DECLARE_CONST_Packet16f(cephes_exp_p2, 8.3334519073E-3f);
86   _EIGEN_DECLARE_CONST_Packet16f(cephes_exp_p3, 4.1665795894E-2f);
87   _EIGEN_DECLARE_CONST_Packet16f(cephes_exp_p4, 1.6666665459E-1f);
88   _EIGEN_DECLARE_CONST_Packet16f(cephes_exp_p5, 5.0000001201E-1f);
89 
90   // Clamp x.
91   Packet16f x = pmax(pmin(_x, p16f_exp_hi), p16f_exp_lo);
92 
93   // Express exp(x) as exp(m*ln(2) + r), start by extracting
94   // m = floor(x/ln(2) + 0.5).
95   Packet16f m = _mm512_floor_ps(pmadd(x, p16f_cephes_LOG2EF, p16f_half));
96 
97   // Get r = x - m*ln(2). Note that we can do this without losing more than one
98   // ulp precision due to the FMA instruction.
99   _EIGEN_DECLARE_CONST_Packet16f(nln2, -0.6931471805599453f);
100   Packet16f r = _mm512_fmadd_ps(m, p16f_nln2, x);
101   Packet16f r2 = pmul(r, r);
102   Packet16f r3 = pmul(r2, r);
103 
104   // Evaluate the polynomial approximant,improved by instruction-level parallelism.
105   Packet16f y, y1, y2;
106   y  = pmadd(p16f_cephes_exp_p0, r, p16f_cephes_exp_p1);
107   y1 = pmadd(p16f_cephes_exp_p3, r, p16f_cephes_exp_p4);
108   y2 = padd(r, p16f_1);
109   y  = pmadd(y, r, p16f_cephes_exp_p2);
110   y1 = pmadd(y1, r, p16f_cephes_exp_p5);
111   y  = pmadd(y, r3, y1);
112   y  = pmadd(y, r2, y2);
113 
114   // Build emm0 = 2^m.
115   Packet16i emm0 = _mm512_cvttps_epi32(padd(m, p16f_127));
116   emm0 = _mm512_slli_epi32(emm0, 23);
117 
118   // Return 2^m * exp(r).
119   return pmax(pmul(y, _mm512_castsi512_ps(emm0)), _x);
120 }
121 
122 /*template <>
123 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet8d
124 pexp<Packet8d>(const Packet8d& _x) {
125   Packet8d x = _x;
126 
127   _EIGEN_DECLARE_CONST_Packet8d(1, 1.0);
128   _EIGEN_DECLARE_CONST_Packet8d(2, 2.0);
129 
130   _EIGEN_DECLARE_CONST_Packet8d(exp_hi, 709.437);
131   _EIGEN_DECLARE_CONST_Packet8d(exp_lo, -709.436139303);
132 
133   _EIGEN_DECLARE_CONST_Packet8d(cephes_LOG2EF, 1.4426950408889634073599);
134 
135   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_p0, 1.26177193074810590878e-4);
136   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_p1, 3.02994407707441961300e-2);
137   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_p2, 9.99999999999999999910e-1);
138 
139   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_q0, 3.00198505138664455042e-6);
140   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_q1, 2.52448340349684104192e-3);
141   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_q2, 2.27265548208155028766e-1);
142   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_q3, 2.00000000000000000009e0);
143 
144   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_C1, 0.693145751953125);
145   _EIGEN_DECLARE_CONST_Packet8d(cephes_exp_C2, 1.42860682030941723212e-6);
146 
147   // clamp x
148   x = pmax(pmin(x, p8d_exp_hi), p8d_exp_lo);
149 
150   // Express exp(x) as exp(g + n*log(2)).
151   const Packet8d n =
152       _mm512_mul_round_pd(p8d_cephes_LOG2EF, x, _MM_FROUND_TO_NEAREST_INT);
153 
154   // Get the remainder modulo log(2), i.e. the "g" described above. Subtract
155   // n*log(2) out in two steps, i.e. n*C1 + n*C2, C1+C2=log2 to get the last
156   // digits right.
157   const Packet8d nC1 = pmul(n, p8d_cephes_exp_C1);
158   const Packet8d nC2 = pmul(n, p8d_cephes_exp_C2);
159   x = psub(x, nC1);
160   x = psub(x, nC2);
161 
162   const Packet8d x2 = pmul(x, x);
163 
164   // Evaluate the numerator polynomial of the rational interpolant.
165   Packet8d px = p8d_cephes_exp_p0;
166   px = pmadd(px, x2, p8d_cephes_exp_p1);
167   px = pmadd(px, x2, p8d_cephes_exp_p2);
168   px = pmul(px, x);
169 
170   // Evaluate the denominator polynomial of the rational interpolant.
171   Packet8d qx = p8d_cephes_exp_q0;
172   qx = pmadd(qx, x2, p8d_cephes_exp_q1);
173   qx = pmadd(qx, x2, p8d_cephes_exp_q2);
174   qx = pmadd(qx, x2, p8d_cephes_exp_q3);
175 
176   // I don't really get this bit, copied from the SSE2 routines, so...
177   // TODO(gonnet): Figure out what is going on here, perhaps find a better
178   // rational interpolant?
179   x = _mm512_div_pd(px, psub(qx, px));
180   x = pmadd(p8d_2, x, p8d_1);
181 
182   // Build e=2^n.
183   const Packet8d e = _mm512_castsi512_pd(_mm512_slli_epi64(
184       _mm512_add_epi64(_mm512_cvtpd_epi64(n), _mm512_set1_epi64(1023)), 52));
185 
186   // Construct the result 2^n * exp(g) = e * x. The max is used to catch
187   // non-finite values in the input.
188   return pmax(pmul(x, e), _x);
189   }*/
190 
F16_PACKET_FUNCTION(Packet16f,Packet16h,pexp)191 F16_PACKET_FUNCTION(Packet16f, Packet16h, pexp)
192 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, pexp)
193 
194 // Functions for sqrt.
195 // The EIGEN_FAST_MATH version uses the _mm_rsqrt_ps approximation and one step
196 // of Newton's method, at a cost of 1-2 bits of precision as opposed to the
197 // exact solution. The main advantage of this approach is not just speed, but
198 // also the fact that it can be inlined and pipelined with other computations,
199 // further reducing its effective latency.
200 #if EIGEN_FAST_MATH
201 template <>
202 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet16f
203 psqrt<Packet16f>(const Packet16f& _x) {
204   Packet16f neg_half = pmul(_x, pset1<Packet16f>(-.5f));
205   __mmask16 denormal_mask = _mm512_kand(
206       _mm512_cmp_ps_mask(_x, pset1<Packet16f>((std::numeric_limits<float>::min)()),
207                         _CMP_LT_OQ),
208       _mm512_cmp_ps_mask(_x, _mm512_setzero_ps(), _CMP_GE_OQ));
209 
210   Packet16f x = _mm512_rsqrt14_ps(_x);
211 
212   // Do a single step of Newton's iteration.
213   x = pmul(x, pmadd(neg_half, pmul(x, x), pset1<Packet16f>(1.5f)));
214 
215   // Flush results for denormals to zero.
216   return _mm512_mask_blend_ps(denormal_mask, pmul(_x,x), _mm512_setzero_ps());
217 }
218 
219 template <>
220 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet8d
221 psqrt<Packet8d>(const Packet8d& _x) {
222   Packet8d neg_half = pmul(_x, pset1<Packet8d>(-.5));
223   __mmask16 denormal_mask = _mm512_kand(
224       _mm512_cmp_pd_mask(_x, pset1<Packet8d>((std::numeric_limits<double>::min)()),
225                         _CMP_LT_OQ),
226       _mm512_cmp_pd_mask(_x, _mm512_setzero_pd(), _CMP_GE_OQ));
227 
228   Packet8d x = _mm512_rsqrt14_pd(_x);
229 
230   // Do a single step of Newton's iteration.
231   x = pmul(x, pmadd(neg_half, pmul(x, x), pset1<Packet8d>(1.5)));
232 
233   // Do a second step of Newton's iteration.
234   x = pmul(x, pmadd(neg_half, pmul(x, x), pset1<Packet8d>(1.5)));
235 
236   return _mm512_mask_blend_pd(denormal_mask, pmul(_x,x), _mm512_setzero_pd());
237 }
238 #else
239 template <>
240 EIGEN_STRONG_INLINE Packet16f psqrt<Packet16f>(const Packet16f& x) {
241   return _mm512_sqrt_ps(x);
242 }
243 
244 template <>
245 EIGEN_STRONG_INLINE Packet8d psqrt<Packet8d>(const Packet8d& x) {
246   return _mm512_sqrt_pd(x);
247 }
248 #endif
249 
F16_PACKET_FUNCTION(Packet16f,Packet16h,psqrt)250 F16_PACKET_FUNCTION(Packet16f, Packet16h, psqrt)
251 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, psqrt)
252 
253 // prsqrt for float.
254 #if defined(EIGEN_VECTORIZE_AVX512ER)
255 
256 template <>
257 EIGEN_STRONG_INLINE Packet16f prsqrt<Packet16f>(const Packet16f& x) {
258   return _mm512_rsqrt28_ps(x);
259 }
260 #elif EIGEN_FAST_MATH
261 
262 template <>
263 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet16f
264 prsqrt<Packet16f>(const Packet16f& _x) {
265   _EIGEN_DECLARE_CONST_Packet16f_FROM_INT(inf, 0x7f800000);
266   _EIGEN_DECLARE_CONST_Packet16f(one_point_five, 1.5f);
267   _EIGEN_DECLARE_CONST_Packet16f(minus_half, -0.5f);
268 
269   Packet16f neg_half = pmul(_x, p16f_minus_half);
270 
271   // Identity infinite, negative and denormal arguments.
272   __mmask16 inf_mask = _mm512_cmp_ps_mask(_x, p16f_inf, _CMP_EQ_OQ);
273   __mmask16 not_pos_mask = _mm512_cmp_ps_mask(_x, _mm512_setzero_ps(), _CMP_LE_OQ);
274   __mmask16 not_finite_pos_mask = not_pos_mask | inf_mask;
275 
276   // Compute an approximate result using the rsqrt intrinsic, forcing +inf
277   // for denormals for consistency with AVX and SSE implementations.
278   Packet16f y_approx = _mm512_rsqrt14_ps(_x);
279 
280   // Do a single step of Newton-Raphson iteration to improve the approximation.
281   // This uses the formula y_{n+1} = y_n * (1.5 - y_n * (0.5 * x) * y_n).
282   // It is essential to evaluate the inner term like this because forming
283   // y_n^2 may over- or underflow.
284   Packet16f y_newton = pmul(y_approx, pmadd(y_approx, pmul(neg_half, y_approx), p16f_one_point_five));
285 
286   // Select the result of the Newton-Raphson step for positive finite arguments.
287   // For other arguments, choose the output of the intrinsic. This will
288   // return rsqrt(+inf) = 0, rsqrt(x) = NaN if x < 0, and rsqrt(0) = +inf.
289   return _mm512_mask_blend_ps(not_finite_pos_mask, y_newton, y_approx);
290 }
291 #else
292 
293 template <>
294 EIGEN_STRONG_INLINE Packet16f prsqrt<Packet16f>(const Packet16f& x) {
295   _EIGEN_DECLARE_CONST_Packet16f(one, 1.0f);
296   return _mm512_div_ps(p16f_one, _mm512_sqrt_ps(x));
297 }
298 #endif
299 
F16_PACKET_FUNCTION(Packet16f,Packet16h,prsqrt)300 F16_PACKET_FUNCTION(Packet16f, Packet16h, prsqrt)
301 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, prsqrt)
302 
303 // prsqrt for double.
304 #if EIGEN_FAST_MATH
305 template <>
306 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet8d
307 prsqrt<Packet8d>(const Packet8d& _x) {
308   _EIGEN_DECLARE_CONST_Packet8d(one_point_five, 1.5);
309   _EIGEN_DECLARE_CONST_Packet8d(minus_half, -0.5);
310   _EIGEN_DECLARE_CONST_Packet8d_FROM_INT64(inf, 0x7ff0000000000000LL);
311 
312   Packet8d neg_half = pmul(_x, p8d_minus_half);
313 
314   // Identity infinite, negative and denormal arguments.
315   __mmask8 inf_mask = _mm512_cmp_pd_mask(_x, p8d_inf, _CMP_EQ_OQ);
316   __mmask8 not_pos_mask = _mm512_cmp_pd_mask(_x, _mm512_setzero_pd(), _CMP_LE_OQ);
317   __mmask8 not_finite_pos_mask = not_pos_mask | inf_mask;
318 
319   // Compute an approximate result using the rsqrt intrinsic, forcing +inf
320   // for denormals for consistency with AVX and SSE implementations.
321 #if defined(EIGEN_VECTORIZE_AVX512ER)
322   Packet8d y_approx = _mm512_rsqrt28_pd(_x);
323 #else
324   Packet8d y_approx = _mm512_rsqrt14_pd(_x);
325 #endif
326   // Do one or two steps of Newton-Raphson's to improve the approximation, depending on the
327   // starting accuracy (either 2^-14 or 2^-28, depending on whether AVX512ER is available).
328   // The Newton-Raphson algorithm has quadratic convergence and roughly doubles the number
329   // of correct digits for each step.
330   // This uses the formula y_{n+1} = y_n * (1.5 - y_n * (0.5 * x) * y_n).
331   // It is essential to evaluate the inner term like this because forming
332   // y_n^2 may over- or underflow.
333   Packet8d y_newton = pmul(y_approx, pmadd(neg_half, pmul(y_approx, y_approx), p8d_one_point_five));
334 #if !defined(EIGEN_VECTORIZE_AVX512ER)
335   y_newton = pmul(y_newton, pmadd(y_newton, pmul(neg_half, y_newton), p8d_one_point_five));
336 #endif
337   // Select the result of the Newton-Raphson step for positive finite arguments.
338   // For other arguments, choose the output of the intrinsic. This will
339   // return rsqrt(+inf) = 0, rsqrt(x) = NaN if x < 0, and rsqrt(0) = +inf.
340   return _mm512_mask_blend_pd(not_finite_pos_mask, y_newton, y_approx);
341 }
342 #else
343 template <>
344 EIGEN_STRONG_INLINE Packet8d prsqrt<Packet8d>(const Packet8d& x) {
345   _EIGEN_DECLARE_CONST_Packet8d(one, 1.0f);
346   return _mm512_div_pd(p8d_one, _mm512_sqrt_pd(x));
347 }
348 #endif
349 
350 template<> EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED
351 Packet16f plog1p<Packet16f>(const Packet16f& _x) {
352   return generic_plog1p(_x);
353 }
354 
F16_PACKET_FUNCTION(Packet16f,Packet16h,plog1p)355 F16_PACKET_FUNCTION(Packet16f, Packet16h, plog1p)
356 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, plog1p)
357 
358 template<> EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED
359 Packet16f pexpm1<Packet16f>(const Packet16f& _x) {
360   return generic_expm1(_x);
361 }
362 
F16_PACKET_FUNCTION(Packet16f,Packet16h,pexpm1)363 F16_PACKET_FUNCTION(Packet16f, Packet16h, pexpm1)
364 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, pexpm1)
365 
366 #endif
367 
368 
369 template <>
370 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet16f
371 psin<Packet16f>(const Packet16f& _x) {
372   return psin_float(_x);
373 }
374 
375 template <>
376 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet16f
377 pcos<Packet16f>(const Packet16f& _x) {
378   return pcos_float(_x);
379 }
380 
381 template <>
382 EIGEN_DEFINE_FUNCTION_ALLOWING_MULTIPLE_DEFINITIONS EIGEN_UNUSED Packet16f
383 ptanh<Packet16f>(const Packet16f& _x) {
384   return internal::generic_fast_tanh_float(_x);
385 }
386 
387 F16_PACKET_FUNCTION(Packet16f, Packet16h, psin)
388 F16_PACKET_FUNCTION(Packet16f, Packet16h, pcos)
389 F16_PACKET_FUNCTION(Packet16f, Packet16h, ptanh)
390 
391 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, psin)
392 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, pcos)
393 BF16_PACKET_FUNCTION(Packet16f, Packet16bf, ptanh)
394 
395 }  // end namespace internal
396 
397 }  // end namespace Eigen
398 
399 #endif  // THIRD_PARTY_EIGEN3_EIGEN_SRC_CORE_ARCH_AVX512_MATHFUNCTIONS_H_
400