You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

functions.hpp 5.2KB

5 years ago
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. #pragma once
  2. #include <simd/Vector.hpp>
  3. #include <simd/sse_mathfun_extension.h>
  4. #include <common.hpp>
  5. #include <math.hpp>
  6. namespace rack {
  7. namespace simd {
  8. // Functions based on instructions
  9. /** `~a & b` */
  10. inline float_4 andnot(float_4 a, float_4 b) {
  11. return float_4(_mm_andnot_ps(a.v, b.v));
  12. }
  13. /** Returns an integer with each bit corresponding to the most significant bit of each element.
  14. For example, `movemask(float_4::mask())` returns 0xf.
  15. */
  16. inline int movemask(float_4 a) {
  17. return _mm_movemask_ps(a.v);
  18. }
  19. /** Returns an integer with each bit corresponding to the most significant bit of each element.
  20. For example, `movemask(int32_4::mask())` returns 0xf.
  21. */
  22. inline int movemask(int32_4 a) {
  23. return _mm_movemask_ps(_mm_castsi128_ps(a.v));
  24. }
  25. /** Returns the approximate reciprocal square root.
  26. Much faster than `1/sqrt(x)`.
  27. */
  28. inline float_4 rsqrt(float_4 x) {
  29. return float_4(_mm_rsqrt_ps(x.v));
  30. }
  31. /** Returns the approximate reciprocal.
  32. Much faster than `1/x`.
  33. */
  34. inline float_4 rcp(float_4 x) {
  35. return float_4(_mm_rcp_ps(x.v));
  36. }
  37. // Nonstandard convenience functions
  38. inline float ifelse(bool cond, float a, float b) {
  39. return cond ? a : b;
  40. }
  41. /** Given a mask, returns a if mask is 0xffffffff per element, b if mask is 0x00000000 */
  42. inline float_4 ifelse(float_4 mask, float_4 a, float_4 b) {
  43. return (a & mask) | andnot(mask, b);
  44. }
  45. /** Returns a vector where element N is all 1's if the N'th bit of `a` is 1, or all 0's if the N'th bit of `a` is 0.
  46. */
  47. template <typename T>
  48. T movemaskInverse(int a);
  49. template <>
  50. inline int32_4 movemaskInverse<int32_4>(int a) {
  51. // Pick out N'th bit of `a` and check if it's 1.
  52. int32_4 mask1234 = int32_4(1, 2, 4, 8);
  53. return (mask1234 & int32_4(a)) == mask1234;
  54. }
  55. template <>
  56. inline float_4 movemaskInverse<float_4>(int a) {
  57. return float_4::cast(movemaskInverse<int32_4>(a));
  58. }
  59. // Standard math functions from std::
  60. /* Import std:: math functions into the simd namespace so you can use `sin(T)` etc in templated functions and get both the scalar and vector versions.
  61. Example:
  62. template <typename T>
  63. T sin_plus_cos(T x) {
  64. return simd::sin(x) + simd::cos(x);
  65. }
  66. */
  67. using std::fmax;
  68. inline float_4 fmax(float_4 x, float_4 b) {
  69. return float_4(_mm_max_ps(x.v, b.v));
  70. }
  71. using std::fmin;
  72. inline float_4 fmin(float_4 x, float_4 b) {
  73. return float_4(_mm_min_ps(x.v, b.v));
  74. }
  75. using std::sqrt;
  76. inline float_4 sqrt(float_4 x) {
  77. return float_4(_mm_sqrt_ps(x.v));
  78. }
  79. using std::log;
  80. inline float_4 log(float_4 x) {
  81. return float_4(sse_mathfun_log_ps(x.v));
  82. }
  83. using std::log10;
  84. inline float_4 log10(float_4 x) {
  85. return float_4(sse_mathfun_log_ps(x.v)) / std::log(10.f);
  86. }
  87. using std::log2;
  88. inline float_4 log2(float_4 x) {
  89. return float_4(sse_mathfun_log_ps(x.v)) / std::log(2.f);
  90. }
  91. using std::exp;
  92. inline float_4 exp(float_4 x) {
  93. return float_4(sse_mathfun_exp_ps(x.v));
  94. }
  95. using std::sin;
  96. inline float_4 sin(float_4 x) {
  97. return float_4(sse_mathfun_sin_ps(x.v));
  98. }
  99. using std::cos;
  100. inline float_4 cos(float_4 x) {
  101. return float_4(sse_mathfun_cos_ps(x.v));
  102. }
  103. using std::tan;
  104. inline float_4 tan(float_4 x) {
  105. return float_4(sse_mathfun_tan_ps(x.v));
  106. }
  107. using std::atan;
  108. inline float_4 atan(float_4 x) {
  109. return float_4(sse_mathfun_atan_ps(x.v));
  110. }
  111. using std::atan2;
  112. inline float_4 atan2(float_4 x, float_4 y) {
  113. return float_4(sse_mathfun_atan2_ps(x.v, y.v));
  114. }
  115. using std::trunc;
  116. inline float_4 trunc(float_4 a) {
  117. return float_4(_mm_cvtepi32_ps(_mm_cvttps_epi32(a.v)));
  118. }
  119. using std::floor;
  120. inline float_4 floor(float_4 a) {
  121. float_4 b = trunc(a);
  122. b -= (b > a) & 1.f;
  123. return b;
  124. }
  125. using std::ceil;
  126. inline float_4 ceil(float_4 a) {
  127. float_4 b = trunc(a);
  128. b += (b < a) & 1.f;
  129. return b;
  130. }
  131. using std::round;
  132. inline float_4 round(float_4 a) {
  133. a += ifelse(a < 0, -0.5f, 0.5f);
  134. float_4 b = trunc(a);
  135. return b;
  136. }
  137. using std::fmod;
  138. inline float_4 fmod(float_4 a, float_4 b) {
  139. return a - trunc(a / b) * b;
  140. }
  141. using std::hypot;
  142. inline float_4 hypot(float_4 a, float_4 b) {
  143. return sqrt(a * a + b * b);
  144. }
  145. using std::fabs;
  146. inline float_4 fabs(float_4 a) {
  147. // Sign bit
  148. int32_4 mask = ~0x80000000;
  149. return a & float_4::cast(mask);
  150. }
  151. using std::abs;
  152. inline float_4 abs(float_4 a) {
  153. return fabs(a);
  154. }
  155. inline float_4 abs(std::complex<float_4> a) {
  156. return hypot(a.real(), a.imag());
  157. }
  158. using std::arg;
  159. inline float_4 arg(std::complex<float_4> a) {
  160. return atan2(a.imag(), a.real());
  161. }
  162. using std::pow;
  163. inline float_4 pow(float_4 a, float_4 b) {
  164. return exp(b * log(a));
  165. }
  166. inline float_4 pow(float a, float_4 b) {
  167. return exp(b * std::log(a));
  168. }
  169. template <typename T>
  170. T pow(T a, int b) {
  171. // Optimal with `-O3 -funsafe-math-optimizations` when b is known at compile-time
  172. T p = 1;
  173. for (int i = 1; i <= b; i *= 2) {
  174. if (i & b)
  175. p *= a;
  176. a *= a;
  177. }
  178. return p;
  179. }
  180. // From math.hpp
  181. using math::clamp;
  182. inline float_4 clamp(float_4 x, float_4 a = 0.f, float_4 b = 1.f) {
  183. return fmin(fmax(x, a), b);
  184. }
  185. using math::rescale;
  186. inline float_4 rescale(float_4 x, float_4 xMin, float_4 xMax, float_4 yMin, float_4 yMax) {
  187. return yMin + (x - xMin) / (xMax - xMin) * (yMax - yMin);
  188. }
  189. using math::crossfade;
  190. inline float_4 crossfade(float_4 a, float_4 b, float_4 p) {
  191. return a + (b - a) * p;
  192. }
  193. using math::sgn;
  194. inline float_4 sgn(float_4 x) {
  195. float_4 signbit = x & -0.f;
  196. float_4 nonzero = (x != 0.f);
  197. return signbit | (nonzero & 1.f);
  198. }
  199. } // namespace simd
  200. } // namespace rack