arm.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. * Copyright (c), Recep Aslantas.
  3. *
  4. * MIT License (MIT), http://opensource.org/licenses/MIT
  5. * Full license can be found in the LICENSE file
  6. */
  7. #ifndef cglm_simd_arm_h
  8. #define cglm_simd_arm_h
  9. #include "intrin.h"
  10. #ifdef CGLM_SIMD_ARM
  11. #if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || defined(__aarch64__)
  12. # define CGLM_ARM64 1
  13. #else
  14. # define CGLM_ARM64 0
  15. #endif
  16. #define glmm_load(p) vld1q_f32(p)
  17. #define glmm_store(p, a) vst1q_f32(p, a)
  18. #define glmm_set1(x) vdupq_n_f32(x)
  19. #define glmm_128 float32x4_t
  20. #define glmm_splat_x(x) vdupq_lane_f32(vget_low_f32(x), 0)
  21. #define glmm_splat_y(x) vdupq_lane_f32(vget_low_f32(x), 1)
  22. #define glmm_splat_z(x) vdupq_lane_f32(vget_high_f32(x), 0)
  23. #define glmm_splat_w(x) vdupq_lane_f32(vget_high_f32(x), 1)
  24. #define glmm_xor(a, b) \
  25. vreinterpretq_f32_s32(veorq_s32(vreinterpretq_s32_f32(a), \
  26. vreinterpretq_s32_f32(b)))
  27. #define glmm_swplane(v) vextq_f32(v, v, 2)
  28. #define glmm_low(x) vget_low_f32(x)
  29. #define glmm_high(x) vget_high_f32(x)
  30. #define glmm_combine_ll(x, y) vcombine_f32(vget_low_f32(x), vget_low_f32(y))
  31. #define glmm_combine_hl(x, y) vcombine_f32(vget_high_f32(x), vget_low_f32(y))
  32. #define glmm_combine_lh(x, y) vcombine_f32(vget_low_f32(x), vget_high_f32(y))
  33. #define glmm_combine_hh(x, y) vcombine_f32(vget_high_f32(x), vget_high_f32(y))
  34. #if defined(_WIN32) && defined(_MSC_VER)
  35. /* # define glmm_float32x4_init(x, y, z, w) { .n128_f32 = { x, y, z, w } } */
  36. CGLM_INLINE
  37. float32x4_t
  38. glmm_float32x4_init(float x, float y, float z, float w) {
  39. CGLM_ALIGN(16) float v[4] = {x, y, z, w};
  40. return vld1q_f32(v);
  41. }
  42. #else
  43. # define glmm_float32x4_init(x, y, z, w) { x, y, z, w }
  44. #endif
  45. #define glmm_float32x4_SIGNMASK_PNPN glmm_float32x4_init( 0.f, -0.f, 0.f, -0.f)
  46. #define glmm_float32x4_SIGNMASK_NPNP glmm_float32x4_init(-0.f, 0.f, -0.f, 0.f)
  47. #define glmm_float32x4_SIGNMASK_NPPN glmm_float32x4_init(-0.f, 0.f, 0.f, -0.f)
  48. static inline float32x4_t glmm_abs(float32x4_t v) { return vabsq_f32(v); }
  49. static inline float32x4_t glmm_min(float32x4_t a, float32x4_t b) { return vminq_f32(a, b); }
  50. static inline float32x4_t glmm_max(float32x4_t a, float32x4_t b) { return vmaxq_f32(a, b); }
  51. static inline
  52. float32x4_t
  53. glmm_vhadd(float32x4_t v) {
  54. #if CGLM_ARM64
  55. float32x4_t p;
  56. p = vpaddq_f32(v, v); /* [a+b, c+d, a+b, c+d] */
  57. return vpaddq_f32(p, p); /* [t, t, t, t] */;
  58. #else
  59. return vaddq_f32(vaddq_f32(glmm_splat_x(v), glmm_splat_y(v)),
  60. vaddq_f32(glmm_splat_z(v), glmm_splat_w(v)));
  61. #endif
  62. /* TODO: measure speed of this compare to above */
  63. /* return vdupq_n_f32(vaddvq_f32(v)); */
  64. /*
  65. return vaddq_f32(vaddq_f32(glmm_splat_x(v), glmm_splat_y(v)),
  66. vaddq_f32(glmm_splat_z(v), glmm_splat_w(v)));
  67. */
  68. /*
  69. this seems slower:
  70. v = vaddq_f32(v, vrev64q_f32(v));
  71. return vaddq_f32(v, vcombine_f32(vget_high_f32(v), vget_low_f32(v)));
  72. */
  73. }
  74. static inline
  75. float
  76. glmm_hadd(float32x4_t v) {
  77. #if CGLM_ARM64
  78. return vaddvq_f32(v);
  79. #else
  80. v = vaddq_f32(v, vrev64q_f32(v));
  81. v = vaddq_f32(v, vcombine_f32(vget_high_f32(v), vget_low_f32(v)));
  82. return vgetq_lane_f32(v, 0);
  83. #endif
  84. }
  85. static inline
  86. float
  87. glmm_hmin(float32x4_t v) {
  88. float32x2_t t;
  89. t = vpmin_f32(vget_low_f32(v), vget_high_f32(v));
  90. t = vpmin_f32(t, t);
  91. return vget_lane_f32(t, 0);
  92. }
  93. static inline
  94. float
  95. glmm_hmax(float32x4_t v) {
  96. float32x2_t t;
  97. t = vpmax_f32(vget_low_f32(v), vget_high_f32(v));
  98. t = vpmax_f32(t, t);
  99. return vget_lane_f32(t, 0);
  100. }
  101. static inline
  102. float
  103. glmm_dot(float32x4_t a, float32x4_t b) {
  104. return glmm_hadd(vmulq_f32(a, b));
  105. }
  106. static inline
  107. float32x4_t
  108. glmm_vdot(float32x4_t a, float32x4_t b) {
  109. return glmm_vhadd(vmulq_f32(a, b));
  110. }
  111. static inline
  112. float
  113. glmm_norm(float32x4_t a) {
  114. return sqrtf(glmm_dot(a, a));
  115. }
  116. static inline
  117. float
  118. glmm_norm2(float32x4_t a) {
  119. return glmm_dot(a, a);
  120. }
  121. static inline
  122. float
  123. glmm_norm_one(float32x4_t a) {
  124. return glmm_hadd(glmm_abs(a));
  125. }
  126. static inline
  127. float
  128. glmm_norm_inf(float32x4_t a) {
  129. return glmm_hmax(glmm_abs(a));
  130. }
  131. static inline
  132. float32x4_t
  133. glmm_div(float32x4_t a, float32x4_t b) {
  134. #if CGLM_ARM64
  135. return vdivq_f32(a, b);
  136. #else
  137. /* 2 iterations of Newton-Raphson refinement of reciprocal */
  138. float32x4_t r0, r1;
  139. r0 = vrecpeq_f32(b);
  140. r1 = vrecpsq_f32(r0, b);
  141. r0 = vmulq_f32(r1, r0);
  142. r1 = vrecpsq_f32(r0, b);
  143. r0 = vmulq_f32(r1, r0);
  144. return vmulq_f32(a, r0);
  145. #endif
  146. }
  147. static inline
  148. float32x4_t
  149. glmm_fmadd(float32x4_t a, float32x4_t b, float32x4_t c) {
  150. #if CGLM_ARM64
  151. return vfmaq_f32(c, a, b); /* why vfmaq_f32 is slower than vmlaq_f32 ??? */
  152. #else
  153. return vmlaq_f32(c, a, b);
  154. #endif
  155. }
  156. static inline
  157. float32x4_t
  158. glmm_fnmadd(float32x4_t a, float32x4_t b, float32x4_t c) {
  159. #if CGLM_ARM64
  160. return vfmsq_f32(c, a, b);
  161. #else
  162. return vmlsq_f32(c, a, b);
  163. #endif
  164. }
  165. static inline
  166. float32x4_t
  167. glmm_fmsub(float32x4_t a, float32x4_t b, float32x4_t c) {
  168. return glmm_fmadd(a, b, vnegq_f32(c));
  169. }
  170. static inline
  171. float32x4_t
  172. glmm_fnmsub(float32x4_t a, float32x4_t b, float32x4_t c) {
  173. return vsubq_f32(vdupq_n_f32(0.0f), glmm_fmadd(a, b, c));
  174. }
  175. #endif
  176. #endif /* cglm_simd_arm_h */