summaryrefslogtreecommitdiff
path: root/include/cglm/simd
diff options
context:
space:
mode:
authorAaditya Dhruv <[email protected]>2026-01-25 15:10:37 -0600
committerAaditya Dhruv <[email protected]>2026-01-25 15:10:37 -0600
commit118980e02e59ff31871df59dce257075394f3533 (patch)
tree26fba4492bb4b561d21bf49b35d892a821d54fab /include/cglm/simd
parent0e6e1245b70df4dfcba135d50e1b53d1a8ef7eb8 (diff)
wip
Diffstat (limited to 'include/cglm/simd')
-rw-r--r--include/cglm/simd/arm.h206
-rw-r--r--include/cglm/simd/avx/affine.h66
-rw-r--r--include/cglm/simd/avx/mat4.h115
-rw-r--r--include/cglm/simd/intrin.h153
-rw-r--r--include/cglm/simd/neon/affine.h121
-rw-r--r--include/cglm/simd/neon/mat2.h44
-rw-r--r--include/cglm/simd/neon/mat4.h468
-rw-r--r--include/cglm/simd/neon/quat.h57
-rw-r--r--include/cglm/simd/sse2/affine.h115
-rw-r--r--include/cglm/simd/sse2/mat2.h48
-rw-r--r--include/cglm/simd/sse2/mat3.h76
-rw-r--r--include/cglm/simd/sse2/mat4.h573
-rw-r--r--include/cglm/simd/sse2/quat.h54
-rw-r--r--include/cglm/simd/wasm.h198
-rw-r--r--include/cglm/simd/wasm/affine.h127
-rw-r--r--include/cglm/simd/wasm/mat2.h50
-rw-r--r--include/cglm/simd/wasm/mat3.h85
-rw-r--r--include/cglm/simd/wasm/mat4.h454
-rw-r--r--include/cglm/simd/wasm/quat.h55
-rw-r--r--include/cglm/simd/x86.h365
20 files changed, 3430 insertions, 0 deletions
diff --git a/include/cglm/simd/arm.h b/include/cglm/simd/arm.h
new file mode 100644
index 0000000..9f51742
--- /dev/null
+++ b/include/cglm/simd/arm.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_simd_arm_h
+#define cglm_simd_arm_h
+#include "intrin.h"
+#ifdef CGLM_SIMD_ARM
+
+#if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC) || defined(__aarch64__)
+# define CGLM_ARM64 1
+#else
+# define CGLM_ARM64 0
+#endif
+
+#define glmm_load(p) vld1q_f32(p)
+#define glmm_store(p, a) vst1q_f32(p, a)
+
+#define glmm_set1(x) vdupq_n_f32(x)
+#define glmm_set1_ptr(x) vdupq_n_f32(*x)
+#define glmm_set1_rval(x) vdupq_n_f32(x)
+#define glmm_128 float32x4_t
+
+#define glmm_splat_x(x) vdupq_lane_f32(vget_low_f32(x), 0)
+#define glmm_splat_y(x) vdupq_lane_f32(vget_low_f32(x), 1)
+#define glmm_splat_z(x) vdupq_lane_f32(vget_high_f32(x), 0)
+#define glmm_splat_w(x) vdupq_lane_f32(vget_high_f32(x), 1)
+
+#define glmm_xor(a, b) \
+ vreinterpretq_f32_s32(veorq_s32(vreinterpretq_s32_f32(a), \
+ vreinterpretq_s32_f32(b)))
+
+#define glmm_swplane(v) vextq_f32(v, v, 2)
+#define glmm_low(x) vget_low_f32(x)
+#define glmm_high(x) vget_high_f32(x)
+
+#define glmm_combine_ll(x, y) vcombine_f32(vget_low_f32(x), vget_low_f32(y))
+#define glmm_combine_hl(x, y) vcombine_f32(vget_high_f32(x), vget_low_f32(y))
+#define glmm_combine_lh(x, y) vcombine_f32(vget_low_f32(x), vget_high_f32(y))
+#define glmm_combine_hh(x, y) vcombine_f32(vget_high_f32(x), vget_high_f32(y))
+
+#if defined(_WIN32) && defined(_MSC_VER)
+/* # define glmm_float32x4_init(x, y, z, w) { .n128_f32 = { x, y, z, w } } */
+CGLM_INLINE
+float32x4_t
+glmm_float32x4_init(float x, float y, float z, float w) {
+ CGLM_ALIGN(16) float v[4] = {x, y, z, w};
+ return vld1q_f32(v);
+}
+#else
+# define glmm_float32x4_init(x, y, z, w) { x, y, z, w }
+#endif
+
+#define glmm_float32x4_SIGNMASK_PNPN glmm_float32x4_init( 0.f, -0.f, 0.f, -0.f)
+#define glmm_float32x4_SIGNMASK_NPNP glmm_float32x4_init(-0.f, 0.f, -0.f, 0.f)
+#define glmm_float32x4_SIGNMASK_NPPN glmm_float32x4_init(-0.f, 0.f, 0.f, -0.f)
+
+static inline float32x4_t glmm_abs(float32x4_t v) { return vabsq_f32(v); }
+static inline float32x4_t glmm_min(float32x4_t a, float32x4_t b) { return vminq_f32(a, b); }
+static inline float32x4_t glmm_max(float32x4_t a, float32x4_t b) { return vmaxq_f32(a, b); }
+
+static inline
+float32x4_t
+glmm_vhadd(float32x4_t v) {
+#if CGLM_ARM64
+ float32x4_t p;
+ p = vpaddq_f32(v, v); /* [a+b, c+d, a+b, c+d] */
+ return vpaddq_f32(p, p); /* [t, t, t, t] */;
+#else
+ return vaddq_f32(vaddq_f32(glmm_splat_x(v), glmm_splat_y(v)),
+ vaddq_f32(glmm_splat_z(v), glmm_splat_w(v)));
+#endif
+ /* TODO: measure speed of this compare to above */
+ /* return vdupq_n_f32(vaddvq_f32(v)); */
+
+ /*
+ return vaddq_f32(vaddq_f32(glmm_splat_x(v), glmm_splat_y(v)),
+ vaddq_f32(glmm_splat_z(v), glmm_splat_w(v)));
+ */
+ /*
+ this seems slower:
+ v = vaddq_f32(v, vrev64q_f32(v));
+ return vaddq_f32(v, vcombine_f32(vget_high_f32(v), vget_low_f32(v)));
+ */
+}
+
+static inline
+float
+glmm_hadd(float32x4_t v) {
+#if CGLM_ARM64
+ return vaddvq_f32(v);
+#else
+ v = vaddq_f32(v, vrev64q_f32(v));
+ v = vaddq_f32(v, vcombine_f32(vget_high_f32(v), vget_low_f32(v)));
+ return vgetq_lane_f32(v, 0);
+#endif
+}
+
+static inline
+float
+glmm_hmin(float32x4_t v) {
+ float32x2_t t;
+ t = vpmin_f32(vget_low_f32(v), vget_high_f32(v));
+ t = vpmin_f32(t, t);
+ return vget_lane_f32(t, 0);
+}
+
+static inline
+float
+glmm_hmax(float32x4_t v) {
+ float32x2_t t;
+ t = vpmax_f32(vget_low_f32(v), vget_high_f32(v));
+ t = vpmax_f32(t, t);
+ return vget_lane_f32(t, 0);
+}
+
+static inline
+float
+glmm_dot(float32x4_t a, float32x4_t b) {
+ return glmm_hadd(vmulq_f32(a, b));
+}
+
+static inline
+float32x4_t
+glmm_vdot(float32x4_t a, float32x4_t b) {
+ return glmm_vhadd(vmulq_f32(a, b));
+}
+
+static inline
+float
+glmm_norm(float32x4_t a) {
+ return sqrtf(glmm_dot(a, a));
+}
+
+static inline
+float
+glmm_norm2(float32x4_t a) {
+ return glmm_dot(a, a);
+}
+
+static inline
+float
+glmm_norm_one(float32x4_t a) {
+ return glmm_hadd(glmm_abs(a));
+}
+
+static inline
+float
+glmm_norm_inf(float32x4_t a) {
+ return glmm_hmax(glmm_abs(a));
+}
+
+static inline
+float32x4_t
+glmm_div(float32x4_t a, float32x4_t b) {
+#if CGLM_ARM64
+ return vdivq_f32(a, b);
+#else
+ /* 2 iterations of Newton-Raphson refinement of reciprocal */
+ float32x4_t r0, r1;
+ r0 = vrecpeq_f32(b);
+ r1 = vrecpsq_f32(r0, b);
+ r0 = vmulq_f32(r1, r0);
+ r1 = vrecpsq_f32(r0, b);
+ r0 = vmulq_f32(r1, r0);
+ return vmulq_f32(a, r0);
+#endif
+}
+
+static inline
+float32x4_t
+glmm_fmadd(float32x4_t a, float32x4_t b, float32x4_t c) {
+#if CGLM_ARM64
+ return vfmaq_f32(c, a, b); /* why vfmaq_f32 is slower than vmlaq_f32 ??? */
+#else
+ return vmlaq_f32(c, a, b);
+#endif
+}
+
+static inline
+float32x4_t
+glmm_fnmadd(float32x4_t a, float32x4_t b, float32x4_t c) {
+#if CGLM_ARM64
+ return vfmsq_f32(c, a, b);
+#else
+ return vmlsq_f32(c, a, b);
+#endif
+}
+
+static inline
+float32x4_t
+glmm_fmsub(float32x4_t a, float32x4_t b, float32x4_t c) {
+ return glmm_fmadd(a, b, vnegq_f32(c));
+}
+
+static inline
+float32x4_t
+glmm_fnmsub(float32x4_t a, float32x4_t b, float32x4_t c) {
+ return vsubq_f32(vdupq_n_f32(0.0f), glmm_fmadd(a, b, c));
+}
+
+#endif
+#endif /* cglm_simd_arm_h */
diff --git a/include/cglm/simd/avx/affine.h b/include/cglm/simd/avx/affine.h
new file mode 100644
index 0000000..b02ff0c
--- /dev/null
+++ b/include/cglm/simd/avx/affine.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_affine_mat_avx_h
+#define cglm_affine_mat_avx_h
+#ifdef __AVX__
+
+#include "../../common.h"
+#include "../intrin.h"
+
+#include <immintrin.h>
+
+CGLM_INLINE
+void
+glm_mul_avx(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ __m256 y0, y1, y2, y3, y4, y5, y6, y7, y8, y9;
+
+ y0 = glmm_load256(m2[0]); /* h g f e d c b a */
+ y1 = glmm_load256(m2[2]); /* p o n m l k j i */
+
+ y2 = glmm_load256(m1[0]); /* h g f e d c b a */
+ y3 = glmm_load256(m1[2]); /* p o n m l k j i */
+
+ /* 0x03: 0b00000011 */
+ y4 = _mm256_permute2f128_ps(y2, y2, 0x03); /* d c b a h g f e */
+ y5 = _mm256_permute2f128_ps(y3, y3, 0x03); /* l k j i p o n m */
+
+ /* f f f f a a a a */
+ /* h h h h c c c c */
+ /* e e e e b b b b */
+ /* g g g g d d d d */
+ y6 = _mm256_permutevar_ps(y0, _mm256_set_epi32(1, 1, 1, 1, 0, 0, 0, 0));
+ y7 = _mm256_permutevar_ps(y0, _mm256_set_epi32(3, 3, 3, 3, 2, 2, 2, 2));
+ y8 = _mm256_permutevar_ps(y0, _mm256_set_epi32(0, 0, 0, 0, 1, 1, 1, 1));
+ y9 = _mm256_permutevar_ps(y0, _mm256_set_epi32(2, 2, 2, 2, 3, 3, 3, 3));
+
+ glmm_store256(dest[0],
+ _mm256_add_ps(_mm256_add_ps(_mm256_mul_ps(y2, y6),
+ _mm256_mul_ps(y3, y7)),
+ _mm256_add_ps(_mm256_mul_ps(y4, y8),
+ _mm256_mul_ps(y5, y9))));
+
+ /* n n n n i i i i */
+ /* p p p p k k k k */
+ /* m m m m j j j j */
+ /* o o o o l l l l */
+ y6 = _mm256_permutevar_ps(y1, _mm256_set_epi32(1, 1, 1, 1, 0, 0, 0, 0));
+ y7 = _mm256_permutevar_ps(y1, _mm256_set_epi32(3, 3, 3, 3, 2, 2, 2, 2));
+ y8 = _mm256_permutevar_ps(y1, _mm256_set_epi32(0, 0, 0, 0, 1, 1, 1, 1));
+ y9 = _mm256_permutevar_ps(y1, _mm256_set_epi32(2, 2, 2, 2, 3, 3, 3, 3));
+
+ glmm_store256(dest[2],
+ _mm256_add_ps(_mm256_add_ps(_mm256_mul_ps(y2, y6),
+ _mm256_mul_ps(y3, y7)),
+ _mm256_add_ps(_mm256_mul_ps(y4, y8),
+ _mm256_mul_ps(y5, y9))));
+}
+
+#endif
+#endif /* cglm_affine_mat_avx_h */
diff --git a/include/cglm/simd/avx/mat4.h b/include/cglm/simd/avx/mat4.h
new file mode 100644
index 0000000..33771c2
--- /dev/null
+++ b/include/cglm/simd/avx/mat4.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat_simd_avx_h
+#define cglm_mat_simd_avx_h
+#ifdef __AVX__
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mat4_scale_avx(mat4 m, float s) {
+ __m256 y0, y1, y2, y3, y4;
+
+ y0 = glmm_load256(m[0]); /* h g f e d c b a */
+ y1 = glmm_load256(m[2]); /* p o n m l k j i */
+
+ y2 = _mm256_broadcast_ss(&s);
+
+ y3 = _mm256_mul_ps(y0, y2);
+ y4 = _mm256_mul_ps(y1, y2);
+
+ glmm_store256(m[0], y3);
+ glmm_store256(m[2], y4);
+}
+
+/* TODO: this must be tested and compared to SSE version, may be slower!!! */
+CGLM_INLINE
+void
+glm_mat4_transp_avx(mat4 m, mat4 dest) {
+ __m256 y0, y1, y2, y3;
+
+ y0 = glmm_load256(m[0]); /* h g f e d c b a */
+ y1 = glmm_load256(m[2]); /* p o n m l k j i */
+
+ y2 = _mm256_unpacklo_ps(y0, y1); /* n f m e j b i a */
+ y3 = _mm256_unpackhi_ps(y0, y1); /* p h o g l d k c */
+
+ y0 = _mm256_permute2f128_ps(y2, y3, 0x20); /* l d k c j b i a */
+ y1 = _mm256_permute2f128_ps(y2, y3, 0x31); /* p h o g n f m e */
+
+ y2 = _mm256_unpacklo_ps(y0, y1); /* o k g c m i e a */
+ y3 = _mm256_unpackhi_ps(y0, y1); /* p l h d n j f b */
+
+ y0 = _mm256_permute2f128_ps(y2, y3, 0x20); /* n j f b m i e a */
+ y1 = _mm256_permute2f128_ps(y2, y3, 0x31); /* p l h d o k g c */
+
+ glmm_store256(dest[0], y0);
+ glmm_store256(dest[2], y1);
+}
+
+CGLM_INLINE
+void
+glm_mat4_mul_avx(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ __m256 y0, y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11, y12, y13;
+ __m256i yi0, yi1, yi2, yi3;
+
+ y0 = glmm_load256(m2[0]); /* h g f e d c b a */
+ y1 = glmm_load256(m2[2]); /* p o n m l k j i */
+
+ y2 = glmm_load256(m1[0]); /* h g f e d c b a */
+ y3 = glmm_load256(m1[2]); /* p o n m l k j i */
+
+ /* 0x03: 0b00000011 */
+ y4 = _mm256_permute2f128_ps(y2, y2, 0x03); /* d c b a h g f e */
+ y5 = _mm256_permute2f128_ps(y3, y3, 0x03); /* l k j i p o n m */
+
+ yi0 = _mm256_set_epi32(1, 1, 1, 1, 0, 0, 0, 0);
+ yi1 = _mm256_set_epi32(3, 3, 3, 3, 2, 2, 2, 2);
+ yi2 = _mm256_set_epi32(0, 0, 0, 0, 1, 1, 1, 1);
+ yi3 = _mm256_set_epi32(2, 2, 2, 2, 3, 3, 3, 3);
+
+ /* f f f f a a a a */
+ /* h h h h c c c c */
+ /* e e e e b b b b */
+ /* g g g g d d d d */
+ y6 = _mm256_permutevar_ps(y0, yi0);
+ y7 = _mm256_permutevar_ps(y0, yi1);
+ y8 = _mm256_permutevar_ps(y0, yi2);
+ y9 = _mm256_permutevar_ps(y0, yi3);
+
+ /* n n n n i i i i */
+ /* p p p p k k k k */
+ /* m m m m j j j j */
+ /* o o o o l l l l */
+ y10 = _mm256_permutevar_ps(y1, yi0);
+ y11 = _mm256_permutevar_ps(y1, yi1);
+ y12 = _mm256_permutevar_ps(y1, yi2);
+ y13 = _mm256_permutevar_ps(y1, yi3);
+
+ y0 = _mm256_mul_ps(y2, y6);
+ y1 = _mm256_mul_ps(y2, y10);
+
+ y0 = glmm256_fmadd(y3, y7, y0);
+ y1 = glmm256_fmadd(y3, y11, y1);
+
+ y0 = glmm256_fmadd(y4, y8, y0);
+ y1 = glmm256_fmadd(y4, y12, y1);
+
+ y0 = glmm256_fmadd(y5, y9, y0);
+ y1 = glmm256_fmadd(y5, y13, y1);
+
+ glmm_store256(dest[0], y0);
+ glmm_store256(dest[2], y1);
+}
+
+#endif
+#endif /* cglm_mat_simd_avx_h */
diff --git a/include/cglm/simd/intrin.h b/include/cglm/simd/intrin.h
new file mode 100644
index 0000000..c477f34
--- /dev/null
+++ b/include/cglm/simd/intrin.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_intrin_h
+#define cglm_intrin_h
+
+#if defined(_MSC_VER) && !defined(_M_ARM64EC)
+# if (defined(_M_AMD64) || defined(_M_X64)) || _M_IX86_FP == 2
+# ifndef __SSE__
+# define __SSE__
+# endif
+# ifndef __SSE2__
+# define __SSE2__
+# endif
+# elif _M_IX86_FP == 1
+# ifndef __SSE__
+# define __SSE__
+# endif
+# endif
+/* do not use alignment for older visual studio versions */
+/* also ARM32 also causes similar error, disable it for now on ARM32 too */
+# if _MSC_VER < 1913 || _M_ARM /* Visual Studio 2017 version 15.6 */
+# define CGLM_ALL_UNALIGNED
+# endif
+#endif
+
+#ifdef __AVX__
+# include <immintrin.h>
+# define CGLM_AVX_FP 1
+# ifndef __SSE2__
+# define __SSE2__
+# endif
+# ifndef __SSE3__
+# define __SSE3__
+# endif
+# ifndef __SSE4__
+# define __SSE4__
+# endif
+# ifndef __SSE4_1__
+# define __SSE4_1__
+# endif
+# ifndef __SSE4_2__
+# define __SSE4_2__
+# endif
+# ifndef CGLM_SIMD_x86
+# define CGLM_SIMD_x86
+# endif
+#endif
+
+#if defined(__SSE__)
+# include <xmmintrin.h>
+# define CGLM_SSE_FP 1
+# ifndef CGLM_SIMD_x86
+# define CGLM_SIMD_x86
+# endif
+#endif
+
+#if defined(__SSE2__)
+# include <emmintrin.h>
+# define CGLM_SSE2_FP 1
+# ifndef CGLM_SIMD_x86
+# define CGLM_SIMD_x86
+# endif
+#endif
+
+#if defined(__SSE3__)
+# include <pmmintrin.h>
+# ifndef CGLM_SIMD_x86
+# define CGLM_SIMD_x86
+# endif
+#endif
+
+#if defined(__SSE4_1__)
+# include <smmintrin.h>
+# ifndef CGLM_SIMD_x86
+# define CGLM_SIMD_x86
+# endif
+#endif
+
+#if defined(__SSE4_2__)
+# include <nmmintrin.h>
+# ifndef CGLM_SIMD_x86
+# define CGLM_SIMD_x86
+# endif
+#endif
+
+/* ARM Neon */
+#if defined(_WIN32) && defined(_MSC_VER)
+/* TODO: non-ARM stuff already inported, will this be better option */
+/* # include <intrin.h> */
+
+# if defined(_M_ARM64) || defined(_M_HYBRID_X86_ARM64) || defined(_M_ARM64EC)
+# include <arm64intr.h>
+# include <arm64_neon.h>
+# ifndef CGLM_NEON_FP
+# define CGLM_NEON_FP 1
+# endif
+# ifndef CGLM_SIMD_ARM
+# define CGLM_SIMD_ARM
+# endif
+# elif defined(_M_ARM)
+# include <armintr.h>
+# include <arm_neon.h>
+# ifndef CGLM_NEON_FP
+# define CGLM_NEON_FP 1
+# endif
+# ifndef CGLM_SIMD_ARM
+# define CGLM_SIMD_ARM
+# endif
+# endif
+
+#else /* non-windows */
+# if defined(__ARM_NEON) || defined(__ARM_NEON__)
+# include <arm_neon.h>
+# if defined(__ARM_NEON_FP) || defined(__ARM_FP)
+# define CGLM_NEON_FP 1
+# endif
+# ifndef CGLM_SIMD_ARM
+# define CGLM_SIMD_ARM
+# endif
+# endif
+#endif
+
+/* WebAssembly */
+#if defined(__wasm__) && defined(__wasm_simd128__)
+# ifndef CGLM_SIMD_WASM
+# define CGLM_SIMD_WASM
+# endif
+#endif
+
+#if defined(CGLM_SIMD_x86) || defined(CGLM_SIMD_ARM) || defined(CGLM_SIMD_WASM)
+# ifndef CGLM_SIMD
+# define CGLM_SIMD
+# endif
+#endif
+
+#if defined(CGLM_SIMD_x86) && !defined(CGLM_SIMD_WASM)
+# include "x86.h"
+#endif
+
+#if defined(CGLM_SIMD_ARM)
+# include "arm.h"
+#endif
+
+#if defined(CGLM_SIMD_WASM)
+# include "wasm.h"
+#endif
+
+#endif /* cglm_intrin_h */
diff --git a/include/cglm/simd/neon/affine.h b/include/cglm/simd/neon/affine.h
new file mode 100644
index 0000000..b0a65a6
--- /dev/null
+++ b/include/cglm/simd/neon/affine.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_affine_neon_h
+#define cglm_affine_neon_h
+#if defined(CGLM_NEON_FP)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mul_neon(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ glmm_128 l, r0, r1, r2, r3, v0, v1, v2, v3;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+ r3 = glmm_load(m2[3]);
+
+ v0 = vmulq_f32(glmm_splat_x(r0), l);
+ v1 = vmulq_f32(glmm_splat_x(r1), l);
+ v2 = vmulq_f32(glmm_splat_x(r2), l);
+ v3 = vmulq_f32(glmm_splat_x(r3), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_y(r3), l, v3);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_z(r3), l, v3);
+
+ v3 = glmm_fmadd(glmm_splat_w(r3), glmm_load(m1[3]), v3);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], v3);
+}
+
+CGLM_INLINE
+void
+glm_mul_rot_neon(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ glmm_128 l, r0, r1, r2, v0, v1, v2;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+
+ v0 = vmulq_f32(glmm_splat_x(r0), l);
+ v1 = vmulq_f32(glmm_splat_x(r1), l);
+ v2 = vmulq_f32(glmm_splat_x(r2), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], glmm_load(m1[3]));
+}
+
+CGLM_INLINE
+void
+glm_inv_tr_neon(mat4 mat) {
+ float32x4x4_t vmat;
+ glmm_128 r0, r1, r2, x0;
+
+ vmat = vld4q_f32(mat[0]);
+ r0 = vmat.val[0];
+ r1 = vmat.val[1];
+ r2 = vmat.val[2];
+
+ x0 = glmm_fmadd(r0, glmm_splat_w(r0),
+ glmm_fmadd(r1, glmm_splat_w(r1),
+ vmulq_f32(r2, glmm_splat_w(r2))));
+ x0 = vnegq_f32(x0);
+
+ glmm_store(mat[0], r0);
+ glmm_store(mat[1], r1);
+ glmm_store(mat[2], r2);
+ glmm_store(mat[3], x0);
+
+ mat[0][3] = 0.0f;
+ mat[1][3] = 0.0f;
+ mat[2][3] = 0.0f;
+ mat[3][3] = 1.0f;
+
+ /* TODO: ?
+ zo = vget_high_f32(r3);
+ vst1_lane_f32(&mat[0][3], zo, 0);
+ vst1_lane_f32(&mat[1][3], zo, 0);
+ vst1_lane_f32(&mat[2][3], zo, 0);
+ vst1_lane_f32(&mat[3][3], zo, 1);
+ */
+}
+
+#endif
+#endif /* cglm_affine_neon_h */
diff --git a/include/cglm/simd/neon/mat2.h b/include/cglm/simd/neon/mat2.h
new file mode 100644
index 0000000..7d0d9eb
--- /dev/null
+++ b/include/cglm/simd/neon/mat2.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat2_neon_h
+#define cglm_mat2_neon_h
+#if defined(CGLM_NEON_FP)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mat2_mul_neon(mat2 m1, mat2 m2, mat2 dest) {
+ float32x4x2_t a1;
+ glmm_128 x0, x1, x2;
+ float32x2_t dc, ba;
+
+ x1 = glmm_load(m1[0]); /* d c b a */
+ x2 = glmm_load(m2[0]); /* h g f e */
+
+ dc = vget_high_f32(x1);
+ ba = vget_low_f32(x1);
+
+ /* g g e e, h h f f */
+ a1 = vtrnq_f32(x2, x2);
+
+ /*
+ dest[0][0] = a * e + c * f;
+ dest[0][1] = b * e + d * f;
+ dest[1][0] = a * g + c * h;
+ dest[1][1] = b * g + d * h;
+ */
+ x0 = glmm_fmadd(vcombine_f32(ba, ba), a1.val[0],
+ vmulq_f32(vcombine_f32(dc, dc), a1.val[1]));
+
+ glmm_store(dest[0], x0);
+}
+
+#endif
+#endif /* cglm_mat2_neon_h */
diff --git a/include/cglm/simd/neon/mat4.h b/include/cglm/simd/neon/mat4.h
new file mode 100644
index 0000000..6cf9811
--- /dev/null
+++ b/include/cglm/simd/neon/mat4.h
@@ -0,0 +1,468 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat4_neon_h
+#define cglm_mat4_neon_h
+#if defined(CGLM_NEON_FP)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mat4_scale_neon(mat4 m, float s) {
+ float32x4_t v0;
+
+ v0 = vdupq_n_f32(s);
+
+ vst1q_f32(m[0], vmulq_f32(vld1q_f32(m[0]), v0));
+ vst1q_f32(m[1], vmulq_f32(vld1q_f32(m[1]), v0));
+ vst1q_f32(m[2], vmulq_f32(vld1q_f32(m[2]), v0));
+ vst1q_f32(m[3], vmulq_f32(vld1q_f32(m[3]), v0));
+}
+
+CGLM_INLINE
+void
+glm_mat4_transp_neon(mat4 m, mat4 dest) {
+ float32x4x4_t vmat;
+
+ vmat = vld4q_f32(m[0]);
+
+ vst1q_f32(dest[0], vmat.val[0]);
+ vst1q_f32(dest[1], vmat.val[1]);
+ vst1q_f32(dest[2], vmat.val[2]);
+ vst1q_f32(dest[3], vmat.val[3]);
+}
+
+CGLM_INLINE
+void
+glm_mat4_mul_neon(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ glmm_128 l, r0, r1, r2, r3, v0, v1, v2, v3;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+ r3 = glmm_load(m2[3]);
+
+ v0 = vmulq_f32(glmm_splat_x(r0), l);
+ v1 = vmulq_f32(glmm_splat_x(r1), l);
+ v2 = vmulq_f32(glmm_splat_x(r2), l);
+ v3 = vmulq_f32(glmm_splat_x(r3), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_y(r3), l, v3);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_z(r3), l, v3);
+
+ l = glmm_load(m1[3]);
+ v0 = glmm_fmadd(glmm_splat_w(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_w(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_w(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_w(r3), l, v3);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], v3);
+}
+
+CGLM_INLINE
+void
+glm_mat4_mulv_neon(mat4 m, vec4 v, vec4 dest) {
+ float32x4_t l0, l1, l2, l3;
+ float32x2_t vlo, vhi;
+
+ l0 = vld1q_f32(m[0]);
+ l1 = vld1q_f32(m[1]);
+ l2 = vld1q_f32(m[2]);
+ l3 = vld1q_f32(m[3]);
+
+ vlo = vld1_f32(&v[0]);
+ vhi = vld1_f32(&v[2]);
+
+ l0 = vmulq_lane_f32(l0, vlo, 0);
+ l0 = vmlaq_lane_f32(l0, l1, vlo, 1);
+ l0 = vmlaq_lane_f32(l0, l2, vhi, 0);
+ l0 = vmlaq_lane_f32(l0, l3, vhi, 1);
+
+ vst1q_f32(dest, l0);
+}
+
+CGLM_INLINE
+float
+glm_mat4_det_neon(mat4 mat) {
+ float32x4_t r0, r1, r2, r3, x0, x1, x2;
+ float32x2_t ij, op, mn, kl, nn, mm, jj, ii, gh, ef, t12, t34;
+ float32x4x2_t a1;
+ float32x4_t x3 = glmm_float32x4_SIGNMASK_PNPN;
+
+ /* 127 <- 0, [square] det(A) = det(At) */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = vrev64q_f32(glmm_load(mat[1])); /* g h e f */
+ r2 = vrev64q_f32(glmm_load(mat[2])); /* l k i j */
+ r3 = vrev64q_f32(glmm_load(mat[3])); /* o p m n */
+
+ gh = vget_high_f32(r1);
+ ef = vget_low_f32(r1);
+ kl = vget_high_f32(r2);
+ ij = vget_low_f32(r2);
+ op = vget_high_f32(r3);
+ mn = vget_low_f32(r3);
+ mm = vdup_lane_f32(mn, 1);
+ nn = vdup_lane_f32(mn, 0);
+ ii = vdup_lane_f32(ij, 1);
+ jj = vdup_lane_f32(ij, 0);
+
+ /*
+ t[1] = j * p - n * l;
+ t[2] = j * o - n * k;
+ t[3] = i * p - m * l;
+ t[4] = i * o - m * k;
+ */
+ x0 = glmm_fnmadd(vcombine_f32(kl, kl), vcombine_f32(nn, mm),
+ vmulq_f32(vcombine_f32(op, op), vcombine_f32(jj, ii)));
+
+ t12 = vget_low_f32(x0);
+ t34 = vget_high_f32(x0);
+
+ /* 1 3 1 3 2 4 2 4 */
+ a1 = vuzpq_f32(x0, x0);
+
+ /*
+ t[0] = k * p - o * l;
+ t[0] = k * p - o * l;
+ t[5] = i * n - m * j;
+ t[5] = i * n - m * j;
+ */
+ x1 = glmm_fnmadd(vcombine_f32(vdup_lane_f32(kl, 0), jj),
+ vcombine_f32(vdup_lane_f32(op, 1), mm),
+ vmulq_f32(vcombine_f32(vdup_lane_f32(op, 0), nn),
+ vcombine_f32(vdup_lane_f32(kl, 1), ii)));
+
+ /*
+ a * (f * t[0] - g * t[1] + h * t[2])
+ - b * (e * t[0] - g * t[3] + h * t[4])
+ + c * (e * t[1] - f * t[3] + h * t[5])
+ - d * (e * t[2] - f * t[4] + g * t[5])
+ */
+ x2 = glmm_fnmadd(vcombine_f32(vdup_lane_f32(gh, 1), vdup_lane_f32(ef, 0)),
+ vcombine_f32(vget_low_f32(a1.val[0]), t34),
+ vmulq_f32(vcombine_f32(ef, vdup_lane_f32(ef, 1)),
+ vcombine_f32(vget_low_f32(x1), t12)));
+
+ x2 = glmm_fmadd(vcombine_f32(vdup_lane_f32(gh, 0), gh),
+ vcombine_f32(vget_low_f32(a1.val[1]), vget_high_f32(x1)), x2);
+
+ x2 = glmm_xor(x2, x3);
+
+ return glmm_hadd(vmulq_f32(x2, r0));
+}
+
+/* old one */
+#if 0
+CGLM_INLINE
+void
+glm_mat4_inv_neon(mat4 mat, mat4 dest) {
+ float32x4_t r0, r1, r2, r3,
+ v0, v1, v2, v3,
+ t0, t1, t2, t3, t4, t5,
+ x0, x1, x2, x3, x4, x5, x6, x7, x8;
+ float32x4x2_t a1;
+ float32x2_t lp, ko, hg, jn, im, fe, ae, bf, cg, dh;
+ float32x4_t x9 = glmm_float32x4_SIGNMASK_NPNP;
+
+ x8 = vrev64q_f32(x9);
+
+ /* 127 <- 0 */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r2 = glmm_load(mat[2]); /* l k j i */
+ r3 = glmm_load(mat[3]); /* p o n m */
+
+ /* l p k o, j n i m */
+ a1 = vzipq_f32(r3, r2);
+
+ jn = vget_high_f32(a1.val[0]);
+ im = vget_low_f32(a1.val[0]);
+ lp = vget_high_f32(a1.val[1]);
+ ko = vget_low_f32(a1.val[1]);
+ hg = vget_high_f32(r1);
+
+ x1 = vcombine_f32(vdup_lane_f32(lp, 0), lp); /* l p p p */
+ x2 = vcombine_f32(vdup_lane_f32(ko, 0), ko); /* k o o o */
+ x0 = vcombine_f32(vdup_lane_f32(lp, 1), vdup_lane_f32(hg, 1)); /* h h l l */
+ x3 = vcombine_f32(vdup_lane_f32(ko, 1), vdup_lane_f32(hg, 0)); /* g g k k */
+
+ /* t1[0] = k * p - o * l;
+ t1[0] = k * p - o * l;
+ t2[0] = g * p - o * h;
+ t3[0] = g * l - k * h; */
+ t0 = glmm_fnmadd(x2, x0, vmulq_f32(x3, x1));
+
+ fe = vget_low_f32(r1);
+ x4 = vcombine_f32(vdup_lane_f32(jn, 0), jn); /* j n n n */
+ x5 = vcombine_f32(vdup_lane_f32(jn, 1), vdup_lane_f32(fe, 1)); /* f f j j */
+
+ /* t1[1] = j * p - n * l;
+ t1[1] = j * p - n * l;
+ t2[1] = f * p - n * h;
+ t3[1] = f * l - j * h; */
+ t1 = glmm_fnmadd(x4, x0, vmulq_f32(x5, x1));
+
+ /* t1[2] = j * o - n * k
+ t1[2] = j * o - n * k;
+ t2[2] = f * o - n * g;
+ t3[2] = f * k - j * g; */
+ t2 = glmm_fnmadd(x4, x3, vmulq_f32(x5, x2));
+
+ x6 = vcombine_f32(vdup_lane_f32(im, 1), vdup_lane_f32(fe, 0)); /* e e i i */
+ x7 = vcombine_f32(vdup_lane_f32(im, 0), im); /* i m m m */
+
+ /* t1[3] = i * p - m * l;
+ t1[3] = i * p - m * l;
+ t2[3] = e * p - m * h;
+ t3[3] = e * l - i * h; */
+ t3 = glmm_fnmadd(x7, x0, vmulq_f32(x6, x1));
+
+ /* t1[4] = i * o - m * k;
+ t1[4] = i * o - m * k;
+ t2[4] = e * o - m * g;
+ t3[4] = e * k - i * g; */
+ t4 = glmm_fnmadd(x7, x3, vmulq_f32(x6, x2));
+
+ /* t1[5] = i * n - m * j;
+ t1[5] = i * n - m * j;
+ t2[5] = e * n - m * f;
+ t3[5] = e * j - i * f; */
+ t5 = glmm_fnmadd(x7, x5, vmulq_f32(x6, x4));
+
+ /* h d f b, g c e a */
+ a1 = vtrnq_f32(r0, r1);
+
+ x4 = vrev64q_f32(a1.val[0]); /* c g a e */
+ x5 = vrev64q_f32(a1.val[1]); /* d h b f */
+
+ ae = vget_low_f32(x4);
+ cg = vget_high_f32(x4);
+ bf = vget_low_f32(x5);
+ dh = vget_high_f32(x5);
+
+ x0 = vcombine_f32(ae, vdup_lane_f32(ae, 1)); /* a a a e */
+ x1 = vcombine_f32(bf, vdup_lane_f32(bf, 1)); /* b b b f */
+ x2 = vcombine_f32(cg, vdup_lane_f32(cg, 1)); /* c c c g */
+ x3 = vcombine_f32(dh, vdup_lane_f32(dh, 1)); /* d d d h */
+
+ /*
+ dest[0][0] = f * t1[0] - g * t1[1] + h * t1[2];
+ dest[0][1] =-(b * t1[0] - c * t1[1] + d * t1[2]);
+ dest[0][2] = b * t2[0] - c * t2[1] + d * t2[2];
+ dest[0][3] =-(b * t3[0] - c * t3[1] + d * t3[2]); */
+ v0 = glmm_xor(glmm_fmadd(x3, t2, glmm_fnmadd(x2, t1, vmulq_f32(x1, t0))), x8);
+
+ /*
+ dest[2][0] = e * t1[1] - f * t1[3] + h * t1[5];
+ dest[2][1] =-(a * t1[1] - b * t1[3] + d * t1[5]);
+ dest[2][2] = a * t2[1] - b * t2[3] + d * t2[5];
+ dest[2][3] =-(a * t3[1] - b * t3[3] + d * t3[5]);*/
+ v2 = glmm_xor(glmm_fmadd(x3, t5, glmm_fnmadd(x1, t3, vmulq_f32(x0, t1))), x8);
+
+ /*
+ dest[1][0] =-(e * t1[0] - g * t1[3] + h * t1[4]);
+ dest[1][1] = a * t1[0] - c * t1[3] + d * t1[4];
+ dest[1][2] =-(a * t2[0] - c * t2[3] + d * t2[4]);
+ dest[1][3] = a * t3[0] - c * t3[3] + d * t3[4]; */
+ v1 = glmm_xor(glmm_fmadd(x3, t4, glmm_fnmadd(x2, t3, vmulq_f32(x0, t0))), x9);
+
+ /*
+ dest[3][0] =-(e * t1[2] - f * t1[4] + g * t1[5]);
+ dest[3][1] = a * t1[2] - b * t1[4] + c * t1[5];
+ dest[3][2] =-(a * t2[2] - b * t2[4] + c * t2[5]);
+ dest[3][3] = a * t3[2] - b * t3[4] + c * t3[5]; */
+ v3 = glmm_xor(glmm_fmadd(x2, t5, glmm_fnmadd(x1, t4, vmulq_f32(x0, t2))), x9);
+
+ /* determinant */
+ x0 = vcombine_f32(vget_low_f32(vzipq_f32(v0, v1).val[0]),
+ vget_low_f32(vzipq_f32(v2, v3).val[0]));
+
+ /*
+ x0 = glmm_div(glmm_set1_rval(1.0f), glmm_vhadd(vmulq_f32(x0, r0)));
+
+ glmm_store(dest[0], vmulq_f32(v0, x0));
+ glmm_store(dest[1], vmulq_f32(v1, x0));
+ glmm_store(dest[2], vmulq_f32(v2, x0));
+ glmm_store(dest[3], vmulq_f32(v3, x0));
+ */
+
+ x0 = glmm_vhadd(vmulq_f32(x0, r0));
+
+ glmm_store(dest[0], glmm_div(v0, x0));
+ glmm_store(dest[1], glmm_div(v1, x0));
+ glmm_store(dest[2], glmm_div(v2, x0));
+ glmm_store(dest[3], glmm_div(v3, x0));
+}
+#endif
+
+CGLM_INLINE
+void
+glm_mat4_inv_neon(mat4 mat, mat4 dest) {
+ float32x4_t r0, r1, r2, r3,
+ v0, v1, v2, v3, v4, v5,
+ t0, t1, t2;
+ float32x4x2_t a0, a1, a2, a3, a4;
+ float32x4_t s1 = glmm_float32x4_SIGNMASK_PNPN, s2;
+
+#if !CGLM_ARM64
+ float32x2_t l0, l1;
+#endif
+
+ s2 = vrev64q_f32(s1);
+
+ /* 127 <- 0 */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r2 = glmm_load(mat[2]); /* l k j i */
+ r3 = glmm_load(mat[3]); /* p o n m */
+
+ a1 = vzipq_f32(r0, r2); /* l d k c, j b i a */
+ a2 = vzipq_f32(r1, r3); /* p h o g, n f m e */
+ a3 = vzipq_f32(a2.val[0], a1.val[0]); /* j n b f, i m a e */
+ a4 = vzipq_f32(a2.val[1], a1.val[1]); /* l p d h, k o c g */
+
+ v0 = vextq_f32(a1.val[0], a1.val[1], 2); /* k c j b */
+ v1 = vextq_f32(a2.val[0], a2.val[1], 2); /* o g n f */
+ v2 = vextq_f32(a1.val[1], a2.val[0], 2); /* m e l d */
+ v3 = vextq_f32(a2.val[1], a1.val[0], 2); /* i a p h */
+ v4 = vextq_f32(v1, v2, 2); /* l d o g */
+ v5 = vextq_f32(v0, v3, 2); /* p h k c */
+
+ /* c2 = c * h - g * d c12 = a * g - c * e c8 = a * f - b * e
+ c1 = k * p - o * l c11 = i * o - k * m c7 = i * n - j * m
+ c4 = h * a - d * e c6 = b * h - d * f c10 = b * g - c * f
+ c3 = p * i - l * m c5 = j * p - l * n c9 = j * o - k * n */
+ t0 = vmulq_f32(v5, v3);
+ t1 = vmulq_f32(a1.val[0], a2.val[1]);
+ t2 = vmulq_f32(a1.val[0], v1);
+
+ t0 = glmm_fnmadd(v4, v2, t0);
+ t1 = glmm_fnmadd(a1.val[1], a2.val[0], t1);
+ t2 = glmm_fnmadd(v0, a2.val[0], t2);
+
+ t0 = vrev64q_f32(t0);
+ t1 = vrev64q_f32(t1);
+ t2 = vrev64q_f32(t2);
+
+ /* det */
+ v0 = vrev64q_f32(t2);
+ v1 = vextq_f32(t1, t1, 2);
+ v0 = vmulq_f32(t0, v0);
+ v1 = vrev64q_f32(v1);
+ v1 = vmulq_f32(v1, t1);
+
+ /* c3 * c10 + c4 * c9 + c1 * c8 + c2 * c7 */
+#if CGLM_ARM64
+ v0 = vpaddq_f32(v0, v0);
+ v0 = vpaddq_f32(v0, v0);
+#else
+ l0 = vget_low_f32(v0);
+ l1 = vget_high_f32(v0);
+
+ l0 = vpadd_f32(l0, l0); /* [a+b, a+b] */
+ l1 = vpadd_f32(l1, l1); /* [c+d, c+d] */
+ l0 = vadd_f32(l0, l1); /* [sum, sum] */
+
+ v0 = vcombine_f32(l0, l0);
+#endif
+
+ /* c5 * c12 + c6 * c11 */
+#if CGLM_ARM64
+ v1 = vpaddq_f32(v1, v1);
+#else
+ l0 = vget_low_f32(v1);
+ l1 = vget_high_f32(v1);
+
+ l0 = vpadd_f32(l0, l0); /* [a+b, a+b] */
+ l1 = vpadd_f32(l1, l1); /* [c+d, c+d] */
+
+ v1 = vcombine_f32(l0, l1);
+#endif
+
+ v0 = vsubq_f32(v0, v1); /* det */
+
+ /* inv div */
+ v1 = vdupq_n_f32(1.0f);
+ v0 = glmm_div(v1, v0); /* inv div */
+
+ /* multiply t0,t1,t2 by idt to reduce 1mul below: 2eor+4mul vs 3mul+4eor */
+ t0 = vmulq_f32(t0, v0);
+ t1 = vmulq_f32(t1, v0);
+ t2 = vmulq_f32(t2, v0);
+
+ a0 = vzipq_f32(t0, t0); /* c4 c4 c3 c3, c2 c2 c1 c1 */
+ a1 = vzipq_f32(t1, t1); /* c6 c6 c5 c5, c12 c12 c11 c11 */
+ a2 = vzipq_f32(t2, t2); /* c10 c10 c9 c9, c8 c8 c7 c7 */
+
+ /* result */
+
+ /* dest[0][0] = (f * c1 - g * c5 + h * c9) * idt;
+ dest[0][1] = (b * c1 - c * c5 + d * c9) * ndt;
+ dest[0][2] = (n * c2 - o * c6 + p * c10) * idt;
+ dest[0][3] = (j * c2 - k * c6 + l * c10) * ndt;
+
+ dest[1][0] = (e * c1 - g * c3 + h * c11) * ndt;
+ dest[1][1] = (a * c1 - c * c3 + d * c11) * idt;
+ dest[1][2] = (m * c2 - o * c4 + p * c12) * ndt;
+ dest[1][3] = (i * c2 - k * c4 + l * c12) * idt;
+
+ dest[2][0] = (e * c5 - f * c3 + h * c7) * idt;
+ dest[2][1] = (a * c5 - b * c3 + d * c7) * ndt;
+ dest[2][2] = (m * c6 - n * c4 + p * c8) * idt;
+ dest[2][3] = (i * c6 - j * c4 + l * c8) * ndt;
+
+ dest[3][0] = (e * c9 - f * c11 + g * c7) * ndt;
+ dest[3][1] = (a * c9 - b * c11 + c * c7) * idt;
+ dest[3][2] = (m * c10 - n * c12 + o * c8) * ndt;
+ dest[3][3] = (i * c10 - j * c12 + k * c8) * idt; */
+
+ r0 = vmulq_f32(a3.val[1], a0.val[0]);
+ r1 = vmulq_f32(a3.val[0], a0.val[0]);
+ r2 = vmulq_f32(a3.val[0], a1.val[1]);
+ r3 = vmulq_f32(a3.val[0], a2.val[1]);
+
+ r0 = glmm_fnmadd(a4.val[0], a1.val[1], r0);
+ r1 = glmm_fnmadd(a4.val[0], a0.val[1], r1);
+ r2 = glmm_fnmadd(a3.val[1], a0.val[1], r2);
+ r3 = glmm_fnmadd(a3.val[1], a1.val[0], r3);
+
+ r0 = glmm_fmadd(a4.val[1], a2.val[1], r0);
+ r1 = glmm_fmadd(a4.val[1], a1.val[0], r1);
+ r2 = glmm_fmadd(a4.val[1], a2.val[0], r2);
+ r3 = glmm_fmadd(a4.val[0], a2.val[0], r3);
+
+ /* 4xor may be fastart then 4mul, see above */
+ r0 = glmm_xor(r0, s1);
+ r1 = glmm_xor(r1, s2);
+ r2 = glmm_xor(r2, s1);
+ r3 = glmm_xor(r3, s2);
+
+ glmm_store(dest[0], r0);
+ glmm_store(dest[1], r1);
+ glmm_store(dest[2], r2);
+ glmm_store(dest[3], r3);
+}
+
+#endif
+#endif /* cglm_mat4_neon_h */
diff --git a/include/cglm/simd/neon/quat.h b/include/cglm/simd/neon/quat.h
new file mode 100644
index 0000000..55dc1da
--- /dev/null
+++ b/include/cglm/simd/neon/quat.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_quat_neon_h
+#define cglm_quat_neon_h
+#if defined(CGLM_NEON_FP)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_quat_mul_neon(versor p, versor q, versor dest) {
+ /*
+ + (a1 b2 + b1 a2 + c1 d2 − d1 c2)i
+ + (a1 c2 − b1 d2 + c1 a2 + d1 b2)j
+ + (a1 d2 + b1 c2 − c1 b2 + d1 a2)k
+ a1 a2 − b1 b2 − c1 c2 − d1 d2
+ */
+
+ glmm_128 xp, xq, xqr, r, x, y, z, s2, s3;
+ glmm_128 s1 = glmm_float32x4_SIGNMASK_NPPN;
+
+ float32x2_t qh, ql;
+
+ xp = glmm_load(p); /* 3 2 1 0 */
+ xq = glmm_load(q);
+
+ r = vmulq_f32(glmm_splat_w(xp), xq);
+ x = glmm_splat_x(xp);
+ y = glmm_splat_y(xp);
+ z = glmm_splat_z(xp);
+
+ ql = vget_high_f32(s1);
+ s3 = vcombine_f32(ql, ql);
+ s2 = vzipq_f32(s3, s3).val[0];
+
+ xqr = vrev64q_f32(xq);
+ qh = vget_high_f32(xqr);
+ ql = vget_low_f32(xqr);
+
+ r = glmm_fmadd(glmm_xor(x, s3), vcombine_f32(qh, ql), r);
+
+ r = glmm_fmadd(glmm_xor(y, s2), vcombine_f32(vget_high_f32(xq),
+ vget_low_f32(xq)), r);
+
+ r = glmm_fmadd(glmm_xor(z, s1), vcombine_f32(ql, qh), r);
+
+ glmm_store(dest, r);
+}
+
+#endif
+#endif /* cglm_quat_neon_h */
diff --git a/include/cglm/simd/sse2/affine.h b/include/cglm/simd/sse2/affine.h
new file mode 100644
index 0000000..0619995
--- /dev/null
+++ b/include/cglm/simd/sse2/affine.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_affine_mat_sse2_h
+#define cglm_affine_mat_sse2_h
+#if defined( __SSE__ ) || defined( __SSE2__ )
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mul_sse2(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+ glmm_128 l, r0, r1, r2, r3, v0, v1, v2, v3;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+ r3 = glmm_load(m2[3]);
+
+ v0 = _mm_mul_ps(glmm_splat_x(r0), l);
+ v1 = _mm_mul_ps(glmm_splat_x(r1), l);
+ v2 = _mm_mul_ps(glmm_splat_x(r2), l);
+ v3 = _mm_mul_ps(glmm_splat_x(r3), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_y(r3), l, v3);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_z(r3), l, v3);
+
+ l = glmm_load(m1[3]);
+ v3 = glmm_fmadd(glmm_splat_w(r3), l, v3);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], v3);
+}
+
+CGLM_INLINE
+void
+glm_mul_rot_sse2(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ glmm_128 l, r0, r1, r2, v0, v1, v2;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+
+ v0 = _mm_mul_ps(glmm_splat_x(r0), l);
+ v1 = _mm_mul_ps(glmm_splat_x(r1), l);
+ v2 = _mm_mul_ps(glmm_splat_x(r2), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], glmm_load(m1[3]));
+}
+
+CGLM_INLINE
+void
+glm_inv_tr_sse2(mat4 mat) {
+ __m128 r0, r1, r2, r3, x0, x1, x2, x3, x4, x5;
+
+ r0 = glmm_load(mat[0]);
+ r1 = glmm_load(mat[1]);
+ r2 = glmm_load(mat[2]);
+ r3 = glmm_load(mat[3]);
+ x1 = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f);
+
+ _MM_TRANSPOSE4_PS(r0, r1, r2, x1);
+
+ x2 = glmm_shuff1(r3, 0, 0, 0, 0);
+ x3 = glmm_shuff1(r3, 1, 1, 1, 1);
+ x4 = glmm_shuff1(r3, 2, 2, 2, 2);
+ x5 = glmm_float32x4_SIGNMASK_NEG;
+
+ x0 = glmm_fmadd(r0, x2, glmm_fmadd(r1, x3, _mm_mul_ps(r2, x4)));
+ x0 = _mm_xor_ps(x0, x5);
+
+ x0 = _mm_add_ps(x0, x1);
+
+ glmm_store(mat[0], r0);
+ glmm_store(mat[1], r1);
+ glmm_store(mat[2], r2);
+ glmm_store(mat[3], x0);
+}
+
+#endif
+#endif /* cglm_affine_mat_sse2_h */
diff --git a/include/cglm/simd/sse2/mat2.h b/include/cglm/simd/sse2/mat2.h
new file mode 100644
index 0000000..31b3a29
--- /dev/null
+++ b/include/cglm/simd/sse2/mat2.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat2_sse_h
+#define cglm_mat2_sse_h
+#if defined( __SSE__ ) || defined( __SSE2__ )
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mat2_mul_sse2(mat2 m1, mat2 m2, mat2 dest) {
+ __m128 x0, x1, x2, x3, x4;
+
+ x1 = glmm_load(m1[0]); /* d c b a */
+ x2 = glmm_load(m2[0]); /* h g f e */
+
+ x3 = glmm_shuff1(x2, 2, 2, 0, 0);
+ x4 = glmm_shuff1(x2, 3, 3, 1, 1);
+ x0 = _mm_movelh_ps(x1, x1);
+ x2 = _mm_movehl_ps(x1, x1);
+
+ /*
+ dest[0][0] = a * e + c * f;
+ dest[0][1] = b * e + d * f;
+ dest[1][0] = a * g + c * h;
+ dest[1][1] = b * g + d * h;
+ */
+ x0 = glmm_fmadd(x0, x3, _mm_mul_ps(x2, x4));
+
+ glmm_store(dest[0], x0);
+}
+
+CGLM_INLINE
+void
+glm_mat2_transp_sse2(mat2 m, mat2 dest) {
+ /* d c b a */
+ /* d b c a */
+ glmm_store(dest[0], glmm_shuff1(glmm_load(m[0]), 3, 1, 2, 0));
+}
+
+#endif
+#endif /* cglm_mat2_sse_h */
diff --git a/include/cglm/simd/sse2/mat3.h b/include/cglm/simd/sse2/mat3.h
new file mode 100644
index 0000000..f07320c
--- /dev/null
+++ b/include/cglm/simd/sse2/mat3.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat3_sse_h
+#define cglm_mat3_sse_h
+#if defined( __SSE__ ) || defined( __SSE2__ )
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mat3_mul_sse2(mat3 m1, mat3 m2, mat3 dest) {
+ __m128 l0, l1, l2, r0, r1, r2, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
+
+ l0 = _mm_loadu_ps(m1[0]);
+ l1 = _mm_loadu_ps(&m1[1][1]);
+
+ r0 = _mm_loadu_ps(m2[0]);
+ r1 = _mm_loadu_ps(&m2[1][1]);
+
+ x8 = glmm_shuff1(l0, 0, 2, 1, 0); /* a00 a02 a01 a00 */
+ x1 = glmm_shuff1(r0, 3, 0, 0, 0); /* b10 b00 b00 b00 */
+ x2 = _mm_shuffle_ps(l0, l1, _MM_SHUFFLE(1, 0, 3, 3)); /* a12 a11 a10 a10 */
+ x3 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(2, 0, 3, 1)); /* b20 b11 b10 b01 */
+ x0 = _mm_mul_ps(x8, x1);
+
+ x6 = glmm_shuff1(l0, 1, 0, 2, 1); /* a01 a00 a02 a01 */
+ x7 = glmm_shuff1(x3, 3, 3, 1, 1); /* b20 b20 b10 b10 */
+ l2 = _mm_load_ss(&m1[2][2]);
+ r2 = _mm_load_ss(&m2[2][2]);
+ x1 = _mm_mul_ps(x6, x7);
+ l2 = glmm_shuff1(l2, 0, 0, 1, 0); /* a22 a22 0.f a22 */
+ r2 = glmm_shuff1(r2, 0, 0, 1, 0); /* b22 b22 0.f b22 */
+
+ x4 = glmm_shuff1(x2, 0, 3, 2, 0); /* a10 a12 a11 a10 */
+ x5 = glmm_shuff1(x2, 2, 0, 3, 2); /* a11 a10 a12 a11 */
+ x6 = glmm_shuff1(x3, 2, 0, 0, 0); /* b11 b01 b01 b01 */
+ x2 = glmm_shuff1(r1, 3, 3, 0, 0); /* b21 b21 b11 b11 */
+
+ x8 = _mm_unpackhi_ps(x8, x4); /* a10 a00 a12 a02 */
+ x9 = _mm_unpackhi_ps(x7, x2); /* b21 b20 b21 b20 */
+
+ x0 = glmm_fmadd(x4, x6, x0);
+ x1 = glmm_fmadd(x5, x2, x1);
+
+ x2 = _mm_movehl_ps(l2, l1); /* a22 a22 a21 a20 */
+ x3 = glmm_shuff1(x2, 0, 2, 1, 0); /* a20 a22 a21 a20 */
+ x2 = glmm_shuff1(x2, 1, 0, 2, 1); /* a21 a20 a22 a21 */
+ x4 = _mm_shuffle_ps(r0, r1, _MM_SHUFFLE(1, 1, 2, 2)); /* b12 b12 b02 b02 */
+
+ x5 = glmm_shuff1(x4, 3, 0, 0, 0); /* b12 b02 b02 b02 */
+ x4 = _mm_movehl_ps(r2, x4); /* b22 b22 b12 b12 */
+ x0 = glmm_fmadd(x3, x5, x0);
+ x1 = glmm_fmadd(x2, x4, x1);
+
+ /*
+ Dot Product : dest[2][2] = a02 * b20 +
+ a12 * b21 +
+ a22 * b22 +
+ 0 * 00 */
+ x2 = _mm_movelh_ps(x8, l2); /* 0.f a22 a12 a02 */
+ x3 = _mm_movelh_ps(x9, r2); /* 0.f b22 b21 b20 */
+ x2 = glmm_vdots(x2, x3);
+
+ _mm_storeu_ps(&dest[0][0], x0);
+ _mm_storeu_ps(&dest[1][1], x1);
+ _mm_store_ss (&dest[2][2], x2);
+}
+
+#endif
+#endif /* cglm_mat3_sse_h */
diff --git a/include/cglm/simd/sse2/mat4.h b/include/cglm/simd/sse2/mat4.h
new file mode 100644
index 0000000..2127e72
--- /dev/null
+++ b/include/cglm/simd/sse2/mat4.h
@@ -0,0 +1,573 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat_sse_h
+#define cglm_mat_sse_h
+#if defined( __SSE__ ) || defined( __SSE2__ )
+
+#include "../../common.h"
+#include "../intrin.h"
+
+#define glm_mat4_inv_precise_sse2(mat, dest) glm_mat4_inv_sse2(mat, dest)
+
+CGLM_INLINE
+void
+glm_mat4_scale_sse2(mat4 m, float s) {
+ __m128 x0;
+ x0 = glmm_set1(s);
+
+ glmm_store(m[0], _mm_mul_ps(glmm_load(m[0]), x0));
+ glmm_store(m[1], _mm_mul_ps(glmm_load(m[1]), x0));
+ glmm_store(m[2], _mm_mul_ps(glmm_load(m[2]), x0));
+ glmm_store(m[3], _mm_mul_ps(glmm_load(m[3]), x0));
+}
+
+CGLM_INLINE
+void
+glm_mat4_transp_sse2(mat4 m, mat4 dest) {
+ __m128 r0, r1, r2, r3;
+
+ r0 = glmm_load(m[0]);
+ r1 = glmm_load(m[1]);
+ r2 = glmm_load(m[2]);
+ r3 = glmm_load(m[3]);
+
+ _MM_TRANSPOSE4_PS(r0, r1, r2, r3);
+
+ glmm_store(dest[0], r0);
+ glmm_store(dest[1], r1);
+ glmm_store(dest[2], r2);
+ glmm_store(dest[3], r3);
+}
+
+CGLM_INLINE
+void
+glm_mat4_mul_sse2(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ glmm_128 l, r0, r1, r2, r3, v0, v1, v2, v3;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+ r3 = glmm_load(m2[3]);
+
+ v0 = _mm_mul_ps(glmm_splat_x(r0), l);
+ v1 = _mm_mul_ps(glmm_splat_x(r1), l);
+ v2 = _mm_mul_ps(glmm_splat_x(r2), l);
+ v3 = _mm_mul_ps(glmm_splat_x(r3), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_y(r3), l, v3);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_z(r3), l, v3);
+
+ l = glmm_load(m1[3]);
+ v0 = glmm_fmadd(glmm_splat_w(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_w(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_w(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_w(r3), l, v3);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], v3);
+}
+
+CGLM_INLINE
+void
+glm_mat4_mulv_sse2(mat4 m, vec4 v, vec4 dest) {
+ __m128 x0, x1, m0, m1, m2, m3, v0, v1, v2, v3;
+
+ m0 = glmm_load(m[0]);
+ m1 = glmm_load(m[1]);
+ m2 = glmm_load(m[2]);
+ m3 = glmm_load(m[3]);
+
+ x0 = glmm_load(v);
+ v0 = glmm_splat_x(x0);
+ v1 = glmm_splat_y(x0);
+ v2 = glmm_splat_z(x0);
+ v3 = glmm_splat_w(x0);
+
+ x1 = _mm_mul_ps(m3, v3);
+ x1 = glmm_fmadd(m2, v2, x1);
+ x1 = glmm_fmadd(m1, v1, x1);
+ x1 = glmm_fmadd(m0, v0, x1);
+
+ glmm_store(dest, x1);
+}
+
+CGLM_INLINE
+float
+glm_mat4_det_sse2(mat4 mat) {
+ __m128 r0, r1, r2, r3, x0, x1, x2;
+
+ /* 127 <- 0, [square] det(A) = det(At) */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r2 = glmm_load(mat[2]); /* l k j i */
+ r3 = glmm_load(mat[3]); /* p o n m */
+
+ /*
+ t[1] = j * p - n * l;
+ t[2] = j * o - n * k;
+ t[3] = i * p - m * l;
+ t[4] = i * o - m * k;
+ */
+ x0 = glmm_fnmadd(glmm_shuff1(r3, 0, 0, 1, 1), glmm_shuff1(r2, 2, 3, 2, 3),
+ _mm_mul_ps(glmm_shuff1(r2, 0, 0, 1, 1),
+ glmm_shuff1(r3, 2, 3, 2, 3)));
+ /*
+ t[0] = k * p - o * l;
+ t[0] = k * p - o * l;
+ t[5] = i * n - m * j;
+ t[5] = i * n - m * j;
+ */
+ x1 = glmm_fnmadd(glmm_shuff1(r3, 0, 0, 2, 2), glmm_shuff1(r2, 1, 1, 3, 3),
+ _mm_mul_ps(glmm_shuff1(r2, 0, 0, 2, 2),
+ glmm_shuff1(r3, 1, 1, 3, 3)));
+
+ /*
+ a * (f * t[0] - g * t[1] + h * t[2])
+ - b * (e * t[0] - g * t[3] + h * t[4])
+ + c * (e * t[1] - f * t[3] + h * t[5])
+ - d * (e * t[2] - f * t[4] + g * t[5])
+ */
+ x2 = glmm_fnmadd(glmm_shuff1(r1, 1, 1, 2, 2), glmm_shuff1(x0, 3, 2, 2, 0),
+ _mm_mul_ps(glmm_shuff1(r1, 0, 0, 0, 1),
+ _mm_shuffle_ps(x1, x0, _MM_SHUFFLE(1, 0, 0, 0))));
+ x2 = glmm_fmadd(glmm_shuff1(r1, 2, 3, 3, 3),
+ _mm_shuffle_ps(x0, x1, _MM_SHUFFLE(2, 2, 3, 1)),
+ x2);
+
+ x2 = _mm_xor_ps(x2, glmm_float32x4_SIGNMASK_NPNP);
+
+ return glmm_hadd(_mm_mul_ps(x2, r0));
+}
+
+CGLM_INLINE
+void
+glm_mat4_inv_fast_sse2(mat4 mat, mat4 dest) {
+ __m128 r0, r1, r2, r3,
+ v0, v1, v2, v3,
+ t0, t1, t2, t3, t4, t5,
+ x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
+
+ /* x8 = _mm_set_ps(-0.f, 0.f, -0.f, 0.f); */
+ x8 = glmm_float32x4_SIGNMASK_NPNP;
+ x9 = glmm_shuff1(x8, 2, 1, 2, 1);
+
+ /* 127 <- 0 */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r2 = glmm_load(mat[2]); /* l k j i */
+ r3 = glmm_load(mat[3]); /* p o n m */
+
+ x0 = _mm_movehl_ps(r3, r2); /* p o l k */
+ x3 = _mm_movelh_ps(r2, r3); /* n m j i */
+ x1 = glmm_shuff1(x0, 1, 3, 3 ,3); /* l p p p */
+ x2 = glmm_shuff1(x0, 0, 2, 2, 2); /* k o o o */
+ x4 = glmm_shuff1(x3, 1, 3, 3, 3); /* j n n n */
+ x7 = glmm_shuff1(x3, 0, 2, 2, 2); /* i m m m */
+
+ x6 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(0, 0, 0, 0)); /* e e i i */
+ x5 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(1, 1, 1, 1)); /* f f j j */
+ x3 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(2, 2, 2, 2)); /* g g k k */
+ x0 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(3, 3, 3, 3)); /* h h l l */
+
+ t0 = _mm_mul_ps(x3, x1);
+ t1 = _mm_mul_ps(x5, x1);
+ t2 = _mm_mul_ps(x5, x2);
+ t3 = _mm_mul_ps(x6, x1);
+ t4 = _mm_mul_ps(x6, x2);
+ t5 = _mm_mul_ps(x6, x4);
+
+ /* t1[0] = k * p - o * l;
+ t1[0] = k * p - o * l;
+ t2[0] = g * p - o * h;
+ t3[0] = g * l - k * h; */
+ t0 = glmm_fnmadd(x2, x0, t0);
+
+ /* t1[1] = j * p - n * l;
+ t1[1] = j * p - n * l;
+ t2[1] = f * p - n * h;
+ t3[1] = f * l - j * h; */
+ t1 = glmm_fnmadd(x4, x0, t1);
+
+ /* t1[2] = j * o - n * k
+ t1[2] = j * o - n * k;
+ t2[2] = f * o - n * g;
+ t3[2] = f * k - j * g; */
+ t2 = glmm_fnmadd(x4, x3, t2);
+
+ /* t1[3] = i * p - m * l;
+ t1[3] = i * p - m * l;
+ t2[3] = e * p - m * h;
+ t3[3] = e * l - i * h; */
+ t3 = glmm_fnmadd(x7, x0, t3);
+
+ /* t1[4] = i * o - m * k;
+ t1[4] = i * o - m * k;
+ t2[4] = e * o - m * g;
+ t3[4] = e * k - i * g; */
+ t4 = glmm_fnmadd(x7, x3, t4);
+
+ /* t1[5] = i * n - m * j;
+ t1[5] = i * n - m * j;
+ t2[5] = e * n - m * f;
+ t3[5] = e * j - i * f; */
+ t5 = glmm_fnmadd(x7, x5, t5);
+
+ x4 = _mm_movelh_ps(r0, r1); /* f e b a */
+ x5 = _mm_movehl_ps(r1, r0); /* h g d c */
+
+ x0 = glmm_shuff1(x4, 0, 0, 0, 2); /* a a a e */
+ x1 = glmm_shuff1(x4, 1, 1, 1, 3); /* b b b f */
+ x2 = glmm_shuff1(x5, 0, 0, 0, 2); /* c c c g */
+ x3 = glmm_shuff1(x5, 1, 1, 1, 3); /* d d d h */
+
+ v2 = _mm_mul_ps(x0, t1);
+ v1 = _mm_mul_ps(x0, t0);
+ v3 = _mm_mul_ps(x0, t2);
+ v0 = _mm_mul_ps(x1, t0);
+
+ v2 = glmm_fnmadd(x1, t3, v2);
+ v3 = glmm_fnmadd(x1, t4, v3);
+ v0 = glmm_fnmadd(x2, t1, v0);
+ v1 = glmm_fnmadd(x2, t3, v1);
+
+ v3 = glmm_fmadd(x2, t5, v3);
+ v0 = glmm_fmadd(x3, t2, v0);
+ v2 = glmm_fmadd(x3, t5, v2);
+ v1 = glmm_fmadd(x3, t4, v1);
+
+ /*
+ dest[0][0] = f * t1[0] - g * t1[1] + h * t1[2];
+ dest[0][1] =-(b * t1[0] - c * t1[1] + d * t1[2]);
+ dest[0][2] = b * t2[0] - c * t2[1] + d * t2[2];
+ dest[0][3] =-(b * t3[0] - c * t3[1] + d * t3[2]); */
+ v0 = _mm_xor_ps(v0, x8);
+
+ /*
+ dest[2][0] = e * t1[1] - f * t1[3] + h * t1[5];
+ dest[2][1] =-(a * t1[1] - b * t1[3] + d * t1[5]);
+ dest[2][2] = a * t2[1] - b * t2[3] + d * t2[5];
+ dest[2][3] =-(a * t3[1] - b * t3[3] + d * t3[5]);*/
+ v2 = _mm_xor_ps(v2, x8);
+
+ /*
+ dest[1][0] =-(e * t1[0] - g * t1[3] + h * t1[4]);
+ dest[1][1] = a * t1[0] - c * t1[3] + d * t1[4];
+ dest[1][2] =-(a * t2[0] - c * t2[3] + d * t2[4]);
+ dest[1][3] = a * t3[0] - c * t3[3] + d * t3[4]; */
+ v1 = _mm_xor_ps(v1, x9);
+
+ /*
+ dest[3][0] =-(e * t1[2] - f * t1[4] + g * t1[5]);
+ dest[3][1] = a * t1[2] - b * t1[4] + c * t1[5];
+ dest[3][2] =-(a * t2[2] - b * t2[4] + c * t2[5]);
+ dest[3][3] = a * t3[2] - b * t3[4] + c * t3[5]; */
+ v3 = _mm_xor_ps(v3, x9);
+
+ /* determinant */
+ x0 = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(0, 0, 0, 0));
+ x1 = _mm_shuffle_ps(v2, v3, _MM_SHUFFLE(0, 0, 0, 0));
+ x0 = _mm_shuffle_ps(x0, x1, _MM_SHUFFLE(2, 0, 2, 0));
+
+ x0 = _mm_rcp_ps(glmm_vhadd(_mm_mul_ps(x0, r0)));
+
+ glmm_store(dest[0], _mm_mul_ps(v0, x0));
+ glmm_store(dest[1], _mm_mul_ps(v1, x0));
+ glmm_store(dest[2], _mm_mul_ps(v2, x0));
+ glmm_store(dest[3], _mm_mul_ps(v3, x0));
+}
+
+/* old one */
+#if 0
+CGLM_INLINE
+void
+glm_mat4_inv_sse2(mat4 mat, mat4 dest) {
+ __m128 r0, r1, r2, r3,
+ v0, v1, v2, v3,
+ t0, t1, t2, t3, t4, t5,
+ x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
+
+ /* x8 = _mm_set_ps(-0.f, 0.f, -0.f, 0.f); */
+ x8 = glmm_float32x4_SIGNMASK_NPNP;
+ x9 = glmm_shuff1(x8, 2, 1, 2, 1);
+
+ /* 127 <- 0 */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r2 = glmm_load(mat[2]); /* l k j i */
+ r3 = glmm_load(mat[3]); /* p o n m */
+
+ x0 = _mm_movehl_ps(r3, r2); /* p o l k */
+ x3 = _mm_movelh_ps(r2, r3); /* n m j i */
+ x1 = glmm_shuff1(x0, 1, 3, 3 ,3); /* l p p p */
+ x2 = glmm_shuff1(x0, 0, 2, 2, 2); /* k o o o */
+ x4 = glmm_shuff1(x3, 1, 3, 3, 3); /* j n n n */
+ x7 = glmm_shuff1(x3, 0, 2, 2, 2); /* i m m m */
+
+ x6 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(0, 0, 0, 0)); /* e e i i */
+ x5 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(1, 1, 1, 1)); /* f f j j */
+ x3 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(2, 2, 2, 2)); /* g g k k */
+ x0 = _mm_shuffle_ps(r2, r1, _MM_SHUFFLE(3, 3, 3, 3)); /* h h l l */
+
+ t0 = _mm_mul_ps(x3, x1);
+ t1 = _mm_mul_ps(x5, x1);
+ t2 = _mm_mul_ps(x5, x2);
+ t3 = _mm_mul_ps(x6, x1);
+ t4 = _mm_mul_ps(x6, x2);
+ t5 = _mm_mul_ps(x6, x4);
+
+ /* t1[0] = k * p - o * l;
+ t1[0] = k * p - o * l;
+ t2[0] = g * p - o * h;
+ t3[0] = g * l - k * h; */
+ t0 = glmm_fnmadd(x2, x0, t0);
+
+ /* t1[1] = j * p - n * l;
+ t1[1] = j * p - n * l;
+ t2[1] = f * p - n * h;
+ t3[1] = f * l - j * h; */
+ t1 = glmm_fnmadd(x4, x0, t1);
+
+ /* t1[2] = j * o - n * k
+ t1[2] = j * o - n * k;
+ t2[2] = f * o - n * g;
+ t3[2] = f * k - j * g; */
+ t2 = glmm_fnmadd(x4, x3, t2);
+
+ /* t1[3] = i * p - m * l;
+ t1[3] = i * p - m * l;
+ t2[3] = e * p - m * h;
+ t3[3] = e * l - i * h; */
+ t3 = glmm_fnmadd(x7, x0, t3);
+
+ /* t1[4] = i * o - m * k;
+ t1[4] = i * o - m * k;
+ t2[4] = e * o - m * g;
+ t3[4] = e * k - i * g; */
+ t4 = glmm_fnmadd(x7, x3, t4);
+
+ /* t1[5] = i * n - m * j;
+ t1[5] = i * n - m * j;
+ t2[5] = e * n - m * f;
+ t3[5] = e * j - i * f; */
+ t5 = glmm_fnmadd(x7, x5, t5);
+
+ x4 = _mm_movelh_ps(r0, r1); /* f e b a */
+ x5 = _mm_movehl_ps(r1, r0); /* h g d c */
+
+ x0 = glmm_shuff1(x4, 0, 0, 0, 2); /* a a a e */
+ x1 = glmm_shuff1(x4, 1, 1, 1, 3); /* b b b f */
+ x2 = glmm_shuff1(x5, 0, 0, 0, 2); /* c c c g */
+ x3 = glmm_shuff1(x5, 1, 1, 1, 3); /* d d d h */
+
+ v2 = _mm_mul_ps(x0, t1);
+ v1 = _mm_mul_ps(x0, t0);
+ v3 = _mm_mul_ps(x0, t2);
+ v0 = _mm_mul_ps(x1, t0);
+
+ v2 = glmm_fnmadd(x1, t3, v2);
+ v3 = glmm_fnmadd(x1, t4, v3);
+ v0 = glmm_fnmadd(x2, t1, v0);
+ v1 = glmm_fnmadd(x2, t3, v1);
+
+ v3 = glmm_fmadd(x2, t5, v3);
+ v0 = glmm_fmadd(x3, t2, v0);
+ v2 = glmm_fmadd(x3, t5, v2);
+ v1 = glmm_fmadd(x3, t4, v1);
+
+ /*
+ dest[0][0] = f * t1[0] - g * t1[1] + h * t1[2];
+ dest[0][1] =-(b * t1[0] - c * t1[1] + d * t1[2]);
+ dest[0][2] = b * t2[0] - c * t2[1] + d * t2[2];
+ dest[0][3] =-(b * t3[0] - c * t3[1] + d * t3[2]); */
+ v0 = _mm_xor_ps(v0, x8);
+
+ /*
+ dest[2][0] = e * t1[1] - f * t1[3] + h * t1[5];
+ dest[2][1] =-(a * t1[1] - b * t1[3] + d * t1[5]);
+ dest[2][2] = a * t2[1] - b * t2[3] + d * t2[5];
+ dest[2][3] =-(a * t3[1] - b * t3[3] + d * t3[5]);*/
+ v2 = _mm_xor_ps(v2, x8);
+
+ /*
+ dest[1][0] =-(e * t1[0] - g * t1[3] + h * t1[4]);
+ dest[1][1] = a * t1[0] - c * t1[3] + d * t1[4];
+ dest[1][2] =-(a * t2[0] - c * t2[3] + d * t2[4]);
+ dest[1][3] = a * t3[0] - c * t3[3] + d * t3[4]; */
+ v1 = _mm_xor_ps(v1, x9);
+
+ /*
+ dest[3][0] =-(e * t1[2] - f * t1[4] + g * t1[5]);
+ dest[3][1] = a * t1[2] - b * t1[4] + c * t1[5];
+ dest[3][2] =-(a * t2[2] - b * t2[4] + c * t2[5]);
+ dest[3][3] = a * t3[2] - b * t3[4] + c * t3[5]; */
+ v3 = _mm_xor_ps(v3, x9);
+
+ /* determinant */
+ x0 = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(0, 0, 0, 0));
+ x1 = _mm_shuffle_ps(v2, v3, _MM_SHUFFLE(0, 0, 0, 0));
+ x0 = _mm_shuffle_ps(x0, x1, _MM_SHUFFLE(2, 0, 2, 0));
+
+ x0 = _mm_div_ps(glmm_set1(1.0f), glmm_vhadd(_mm_mul_ps(x0, r0)));
+
+ glmm_store(dest[0], _mm_mul_ps(v0, x0));
+ glmm_store(dest[1], _mm_mul_ps(v1, x0));
+ glmm_store(dest[2], _mm_mul_ps(v2, x0));
+ glmm_store(dest[3], _mm_mul_ps(v3, x0));
+}
+#endif
+
+CGLM_INLINE
+void
+glm_mat4_inv_sse2(mat4 mat, mat4 dest) {
+ __m128 r0, r1, r2, r3, s1, s2,
+ v0, v1, v2, v3, v4, v5,
+ t0, t1, t2,
+ x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13;
+
+ /* s1 = _mm_set_ps(-0.f, 0.f, -0.f, 0.f); */
+ s1 = glmm_float32x4_SIGNMASK_NPNP;
+ s2 = glmm_shuff1(s1, 2, 1, 2, 1);
+
+ /* 127 <- 0 */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r3 = glmm_load(mat[3]); /* p o n m */
+ r2 = glmm_load(mat[2]); /* l k j i */
+
+ x4 = _mm_unpackhi_ps(r0, r2); /* l d k c */
+ x5 = _mm_unpacklo_ps(r0, r2); /* j b i a */
+ x6 = _mm_unpackhi_ps(r1, r3); /* p h o g */
+ x7 = _mm_unpacklo_ps(r1, r3); /* n f m e */
+
+ x0 = _mm_unpackhi_ps(x7, x5); /* j n b f */
+ x1 = _mm_unpacklo_ps(x7, x5); /* i m a e */
+ x2 = _mm_unpackhi_ps(x6, x4); /* l p d h */
+ x3 = _mm_unpacklo_ps(x6, x4); /* k o c g */
+
+ /* c2 = c * h - d * g c12 = a * g - c * e c8 = a * f - b * e
+ c1 = k * p - l * o c11 = i * o - k * m c7 = i * n - j * m
+ c4 = a * h - d * e c6 = b * h - d * f c10 = b * g - c * f
+ c3 = i * p - l * m c5 = j * p - l * n c9 = j * o - k * n */
+
+ x8 = _mm_shuffle_ps(x0, x3, _MM_SHUFFLE(3, 1, 3, 1)); /* k c j b */
+ x9 = _mm_shuffle_ps(x0, x3, _MM_SHUFFLE(2, 0, 2, 0)); /* o g n f */
+
+ x10 = glmm_shuff1(x2, 2, 0, 2, 0); /* p h p h */
+ x11 = glmm_shuff1(x2, 3, 1, 3, 1); /* l d l d */
+
+#if 0 /* TODO measure both */
+ x12 = _mm_shuffle_ps(x4, x5, _MM_SHUFFLE(1, 0, 1, 0)); /* i a k c */
+ x13 = _mm_shuffle_ps(x6, x7, _MM_SHUFFLE(1, 0, 1, 0)); /* m e o g */
+#else
+ x12 = _mm_movelh_ps(x4, x5); /* i a k c */
+ x13 = _mm_movelh_ps(x6, x7); /* m e o g */
+#endif
+
+ t0 = _mm_mul_ps(x12, x10);
+ t1 = _mm_mul_ps(x5, x6);
+ t2 = _mm_mul_ps(x5, x9);
+
+ t0 = glmm_fnmadd(x11, x13, t0);
+ t1 = glmm_fnmadd(x4, x7, t1);
+ t2 = glmm_fnmadd(x8, x7, t2);
+
+ /* det */
+ /* v0: c3 * c10 + c4 * c9 + c1 * c8 + c2 * c7 */
+ /* v1: c5 * c12 + c6 * c11 */
+
+ v5 = glmm_set1_rval(1.0f);
+ v0 = glmm_shuff1(t2, 2, 3, 0, 1);
+ v1 = glmm_shuff1(t1, 0, 1, 2, 3);
+ v0 = _mm_mul_ps(t0, v0);
+ v1 = _mm_mul_ps(t1, v1);
+ v2 = glmm_shuff1(v1, 1, 0, 0, 1);
+ v3 = glmm_shuff1(v0, 0, 1, 2, 3);
+ v1 = _mm_add_ps(v1, v2);
+ v0 = _mm_add_ps(v0, v3);
+ v2 = glmm_shuff1(v0, 1, 0, 0, 1);
+ v0 = _mm_add_ps(v0, v2);
+
+ v0 = _mm_sub_ps(v0, v1); /* det */
+ v0 = _mm_div_ps(v5, v0); /* idt */
+
+ /* multiply t0,t1,t2 by idt to reduce 1mul below: 2eor+4mul vs 3mul+4eor */
+ t0 = _mm_mul_ps(t0, v0);
+ t1 = _mm_mul_ps(t1, v0);
+ t2 = _mm_mul_ps(t2, v0);
+
+ v0 = glmm_shuff1(t0, 0, 0, 1, 1); /* c2 c2 c1 c1 */
+ v1 = glmm_shuff1(t0, 2, 2, 3, 3); /* c4 c4 c3 c3 */
+ v2 = glmm_shuff1(t1, 0, 0, 1, 1); /* c12 c12 c11 c11 */
+ v3 = glmm_shuff1(t1, 2, 2, 3, 3); /* c6 c6 c5 c5 */
+ v4 = glmm_shuff1(t2, 0, 0, 1, 1); /* c8 c8 c7 c7 */
+ v5 = glmm_shuff1(t2, 2, 2, 3, 3); /* c10 c10 c9 c9 */
+
+ /* result */
+
+ /* dest[0][0] = (f * c1 - g * c5 + h * c9) * idt;
+ dest[0][1] = (b * c1 - c * c5 + d * c9) * ndt;
+ dest[0][2] = (n * c2 - o * c6 + p * c10) * idt;
+ dest[0][3] = (j * c2 - k * c6 + l * c10) * ndt;
+
+ dest[1][0] = (e * c1 - g * c3 + h * c11) * ndt;
+ dest[1][1] = (a * c1 - c * c3 + d * c11) * idt;
+ dest[1][2] = (m * c2 - o * c4 + p * c12) * ndt;
+ dest[1][3] = (i * c2 - k * c4 + l * c12) * idt;
+
+ dest[2][0] = (e * c5 - f * c3 + h * c7) * idt;
+ dest[2][1] = (a * c5 - b * c3 + d * c7) * ndt;
+ dest[2][2] = (m * c6 - n * c4 + p * c8) * idt;
+ dest[2][3] = (i * c6 - j * c4 + l * c8) * ndt;
+
+ dest[3][0] = (e * c9 - f * c11 + g * c7) * ndt;
+ dest[3][1] = (a * c9 - b * c11 + c * c7) * idt;
+ dest[3][2] = (m * c10 - n * c12 + o * c8) * ndt;
+ dest[3][3] = (i * c10 - j * c12 + k * c8) * idt; */
+
+ r0 = _mm_mul_ps(x0, v0);
+ r1 = _mm_mul_ps(x1, v0);
+ r2 = _mm_mul_ps(x1, v3);
+ r3 = _mm_mul_ps(x1, v5);
+
+ r0 = glmm_fnmadd(x3, v3, r0);
+ r1 = glmm_fnmadd(x3, v1, r1);
+ r2 = glmm_fnmadd(x0, v1, r2);
+ r3 = glmm_fnmadd(x0, v2, r3);
+
+ r0 = glmm_fmadd(x2, v5, r0);
+ r1 = glmm_fmadd(x2, v2, r1);
+ r2 = glmm_fmadd(x2, v4, r2);
+ r3 = glmm_fmadd(x3, v4, r3);
+
+ /* 4xor may be fastart then 4mul, see above */
+ r0 = _mm_xor_ps(r0, s1);
+ r1 = _mm_xor_ps(r1, s2);
+ r2 = _mm_xor_ps(r2, s1);
+ r3 = _mm_xor_ps(r3, s2);
+
+ glmm_store(dest[0], r0);
+ glmm_store(dest[1], r1);
+ glmm_store(dest[2], r2);
+ glmm_store(dest[3], r3);
+}
+#endif
+#endif /* cglm_mat_sse_h */
diff --git a/include/cglm/simd/sse2/quat.h b/include/cglm/simd/sse2/quat.h
new file mode 100644
index 0000000..def0fe2
--- /dev/null
+++ b/include/cglm/simd/sse2/quat.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_quat_simd_h
+#define cglm_quat_simd_h
+#if defined( __SSE__ ) || defined( __SSE2__ )
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_quat_mul_sse2(versor p, versor q, versor dest) {
+ /*
+ + (a1 b2 + b1 a2 + c1 d2 − d1 c2)i
+ + (a1 c2 − b1 d2 + c1 a2 + d1 b2)j
+ + (a1 d2 + b1 c2 − c1 b2 + d1 a2)k
+ a1 a2 − b1 b2 − c1 c2 − d1 d2
+ */
+
+ __m128 xp, xq, x1, x2, x3, r, x, y, z;
+
+ xp = glmm_load(p); /* 3 2 1 0 */
+ xq = glmm_load(q);
+ x1 = glmm_float32x4_SIGNMASK_NPNP; /* TODO: _mm_set1_ss() + shuff ? */
+ r = _mm_mul_ps(glmm_splat_w(xp), xq);
+
+ x2 = _mm_unpackhi_ps(x1, x1);
+ x3 = glmm_shuff1(x1, 3, 2, 0, 1);
+ x = glmm_splat_x(xp);
+ y = glmm_splat_y(xp);
+ z = glmm_splat_z(xp);
+
+ x = _mm_xor_ps(x, x1);
+ y = _mm_xor_ps(y, x2);
+ z = _mm_xor_ps(z, x3);
+
+ x1 = glmm_shuff1(xq, 0, 1, 2, 3);
+ x2 = glmm_shuff1(xq, 1, 0, 3, 2);
+ x3 = glmm_shuff1(xq, 2, 3, 0, 1);
+
+ r = glmm_fmadd(x, x1, r);
+ r = glmm_fmadd(y, x2, r);
+ r = glmm_fmadd(z, x3, r);
+
+ glmm_store(dest, r);
+}
+
+#endif
+#endif /* cglm_quat_simd_h */
diff --git a/include/cglm/simd/wasm.h b/include/cglm/simd/wasm.h
new file mode 100644
index 0000000..2ced51f
--- /dev/null
+++ b/include/cglm/simd/wasm.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_simd_wasm_h
+#define cglm_simd_wasm_h
+#include "intrin.h"
+#ifdef CGLM_SIMD_WASM
+#include <wasm_simd128.h>
+
+#define glmm_load(p) wasm_v128_load(p)
+#define glmm_store(p, a) wasm_v128_store(p, (a))
+
+#define glmm_set1(x) wasm_f32x4_splat(x)
+#define glmm_set1_ptr(x) wasm_f32x4_splat(*x)
+#define glmm_set1_rval(x) wasm_f32x4_splat(x)
+#define glmm_128 v128_t
+
+#define glmm_shuff1(xmm, z, y, x, w) wasm_i32x4_shuffle(xmm, xmm, w, x, y, z)
+
+#define glmm_splat(x, lane) glmm_shuff1(x, lane, lane, lane, lane)
+
+#define glmm_splat_x(x) glmm_splat(x, 0)
+#define glmm_splat_y(x) glmm_splat(x, 1)
+#define glmm_splat_z(x) glmm_splat(x, 2)
+#define glmm_splat_w(x) glmm_splat(x, 3)
+
+#define GLMM_NEGZEROf 0x80000000 /* 0x80000000 ---> -0.0f */
+
+/* _mm_set_ps(X, Y, Z, W); */
+#define GLMM__SIGNMASKf(X, Y, Z, W) wasm_i32x4_const(X, Y, Z, W)
+
+#define glmm_float32x4_SIGNMASK_PNPN GLMM__SIGNMASKf(0, GLMM_NEGZEROf, 0, GLMM_NEGZEROf)
+#define glmm_float32x4_SIGNMASK_NPNP GLMM__SIGNMASKf(GLMM_NEGZEROf, 0, GLMM_NEGZEROf, 0)
+#define glmm_float32x4_SIGNMASK_NPPN GLMM__SIGNMASKf(GLMM_NEGZEROf, 0, 0, GLMM_NEGZEROf)
+#define glmm_float32x4_SIGNMASK_NEG wasm_i32x4_const_splat(GLMM_NEGZEROf)
+
+static inline glmm_128 glmm_abs(glmm_128 x) { return wasm_f32x4_abs(x); }
+static inline glmm_128 glmm_min(glmm_128 a, glmm_128 b) { return wasm_f32x4_pmin(b, a); }
+static inline glmm_128 glmm_max(glmm_128 a, glmm_128 b) { return wasm_f32x4_pmax(b, a); }
+
+static inline
+glmm_128
+glmm_vhadd(glmm_128 v) {
+ glmm_128 x0;
+ x0 = wasm_f32x4_add(v, glmm_shuff1(v, 0, 1, 2, 3));
+ x0 = wasm_f32x4_add(x0, glmm_shuff1(x0, 1, 0, 0, 1));
+ return x0;
+}
+
+static inline
+glmm_128
+glmm_vhadds(glmm_128 v) {
+ glmm_128 shuf, sums;
+ shuf = glmm_shuff1(v, 2, 3, 0, 1);
+ sums = wasm_f32x4_add(v, shuf);
+ /* shuf = _mm_movehl_ps(shuf, sums); */
+ shuf = wasm_i32x4_shuffle(shuf, sums, 6, 7, 2, 3);
+ sums = wasm_i32x4_shuffle(sums, wasm_f32x4_add(sums, shuf), 4, 1, 2, 3);
+ return sums;
+}
+
+static inline
+float
+glmm_hadd(glmm_128 v) {
+ return wasm_f32x4_extract_lane(glmm_vhadds(v), 0);
+}
+
+static inline
+glmm_128
+glmm_vhmin(glmm_128 v) {
+ glmm_128 x0, x1, x2;
+ x0 = glmm_shuff1(v, 2, 3, 2, 3); /* [2, 3, 2, 3] */
+ x1 = wasm_f32x4_pmin(x0, v); /* [0|2, 1|3, 2|2, 3|3] */
+ x2 = glmm_splat(x1, 1); /* [1|3, 1|3, 1|3, 1|3] */
+ return wasm_f32x4_pmin(x1, x2);
+}
+
+static inline
+float
+glmm_hmin(glmm_128 v) {
+ return wasm_f32x4_extract_lane(glmm_vhmin(v), 0);
+}
+
+static inline
+glmm_128
+glmm_vhmax(glmm_128 v) {
+ glmm_128 x0, x1, x2;
+ x0 = glmm_shuff1(v, 2, 3, 2, 3); /* [2, 3, 2, 3] */
+ x1 = wasm_f32x4_pmax(x0, v); /* [0|2, 1|3, 2|2, 3|3] */
+ x2 = glmm_splat(x1, 1); /* [1|3, 1|3, 1|3, 1|3] */
+ /* _mm_max_ss */
+ return wasm_i32x4_shuffle(x1, wasm_f32x4_pmax(x1, x2), 4, 1, 2, 3);
+}
+
+static inline
+float
+glmm_hmax(glmm_128 v) {
+ return wasm_f32x4_extract_lane(glmm_vhmax(v), 0);
+}
+
+static inline
+glmm_128
+glmm_vdots(glmm_128 a, glmm_128 b) {
+ return glmm_vhadds(wasm_f32x4_mul(a, b));
+}
+
+static inline
+glmm_128
+glmm_vdot(glmm_128 a, glmm_128 b) {
+ glmm_128 x0;
+ x0 = wasm_f32x4_mul(a, b);
+ x0 = wasm_f32x4_add(x0, glmm_shuff1(x0, 1, 0, 3, 2));
+ return wasm_f32x4_add(x0, glmm_shuff1(x0, 0, 1, 0, 1));
+}
+
+static inline
+float
+glmm_dot(glmm_128 a, glmm_128 b) {
+ return wasm_f32x4_extract_lane(glmm_vdots(a, b), 0);
+}
+
+static inline
+float
+glmm_norm(glmm_128 a) {
+ glmm_128 x0;
+ x0 = glmm_vhadds(wasm_f32x4_mul(a, a));
+ return wasm_f32x4_extract_lane(
+ wasm_i32x4_shuffle(x0, wasm_f32x4_sqrt(x0),4, 1, 2, 3), 0);
+}
+
+static inline
+float
+glmm_norm2(glmm_128 a) {
+ return wasm_f32x4_extract_lane(glmm_vhadds(wasm_f32x4_mul(a, a)), 0);
+}
+
+static inline
+float
+glmm_norm_one(glmm_128 a) {
+ return wasm_f32x4_extract_lane(glmm_vhadds(glmm_abs(a)), 0);
+}
+
+static inline
+float
+glmm_norm_inf(glmm_128 a) {
+ return wasm_f32x4_extract_lane(glmm_vhmax(glmm_abs(a)), 0);
+}
+
+static inline
+glmm_128
+glmm_load3(float v[3]) {
+ glmm_128 xy = wasm_v128_load64_zero(v);
+ return wasm_f32x4_replace_lane(xy, 2, v[2]);
+}
+
+static inline
+void
+glmm_store3(float v[3], glmm_128 vx) {
+ wasm_v128_store64_lane(v, vx, 0);
+ wasm_v128_store32_lane(&v[2], vx, 2);
+}
+
+static inline
+glmm_128
+glmm_div(glmm_128 a, glmm_128 b) {
+ return wasm_f32x4_div(a, b);
+}
+
+static inline
+glmm_128
+glmm_fmadd(glmm_128 a, glmm_128 b, glmm_128 c) {
+ return wasm_f32x4_add(c, wasm_f32x4_mul(a, b));
+}
+
+static inline
+glmm_128
+glmm_fnmadd(glmm_128 a, glmm_128 b, glmm_128 c) {
+ return wasm_f32x4_sub(c, wasm_f32x4_mul(a, b));
+}
+
+static inline
+glmm_128
+glmm_fmsub(glmm_128 a, glmm_128 b, glmm_128 c) {
+ return wasm_f32x4_sub(wasm_f32x4_mul(a, b), c);
+}
+
+static inline
+glmm_128
+glmm_fnmsub(glmm_128 a, glmm_128 b, glmm_128 c) {
+ return wasm_f32x4_neg(wasm_f32x4_add(wasm_f32x4_mul(a, b), c));
+}
+
+#endif
+#endif /* cglm_simd_wasm_h */
diff --git a/include/cglm/simd/wasm/affine.h b/include/cglm/simd/wasm/affine.h
new file mode 100644
index 0000000..80b98fb
--- /dev/null
+++ b/include/cglm/simd/wasm/affine.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_affine_mat_wasm_h
+#define cglm_affine_mat_wasm_h
+#if defined(__wasm__) && defined(__wasm_simd128__)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mul_wasm(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+ glmm_128 l, r0, r1, r2, r3, v0, v1, v2, v3;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+ r3 = glmm_load(m2[3]);
+
+ v0 = wasm_f32x4_mul(glmm_splat_x(r0), l);
+ v1 = wasm_f32x4_mul(glmm_splat_x(r1), l);
+ v2 = wasm_f32x4_mul(glmm_splat_x(r2), l);
+ v3 = wasm_f32x4_mul(glmm_splat_x(r3), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_y(r3), l, v3);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_z(r3), l, v3);
+
+ l = glmm_load(m1[3]);
+ v3 = glmm_fmadd(glmm_splat_w(r3), l, v3);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], v3);
+}
+
+CGLM_INLINE
+void
+glm_mul_rot_wasm(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ glmm_128 l, r0, r1, r2, v0, v1, v2;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+
+ v0 = wasm_f32x4_mul(glmm_splat_x(r0), l);
+ v1 = wasm_f32x4_mul(glmm_splat_x(r1), l);
+ v2 = wasm_f32x4_mul(glmm_splat_x(r2), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], glmm_load(m1[3]));
+}
+
+CGLM_INLINE
+void
+glm_inv_tr_wasm(mat4 mat) {
+ glmm_128 r0, r1, r2, r3, x0, x1, x2, x3, x4, x5;
+
+ r0 = glmm_load(mat[0]);
+ r1 = glmm_load(mat[1]);
+ r2 = glmm_load(mat[2]);
+ r3 = glmm_load(mat[3]);
+ x1 = wasm_f32x4_const(0.0f, 0.0f, 0.0f, 1.0f);
+
+ /* _MM_TRANSPOSE4_PS(r0, r1, r2, x1); */
+ x2 = wasm_i32x4_shuffle(r0, r1, 0, 4, 1, 5);
+ x3 = wasm_i32x4_shuffle(r0, r1, 2, 6, 3, 7);
+ x4 = wasm_i32x4_shuffle(r2, x1, 0, 4, 1, 5);
+ x5 = wasm_i32x4_shuffle(r2, x1, 2, 6, 3, 7);
+ /* r0 = _mm_movelh_ps(x2, x4); */
+ r0 = wasm_i32x4_shuffle(x2, x4, 0, 1, 4, 5);
+ /* r1 = _mm_movehl_ps(x4, x2); */
+ r1 = wasm_i32x4_shuffle(x4, x2, 6, 7, 2, 3);
+ /* r2 = _mm_movelh_ps(x3, x5); */
+ r2 = wasm_i32x4_shuffle(x3, x5, 0, 1, 4, 5);
+ /* x1 = _mm_movehl_ps(x5, x3); */
+ x1 = wasm_i32x4_shuffle(x5, x3, 6, 7, 2, 3);
+
+ x2 = glmm_shuff1(r3, 0, 0, 0, 0);
+ x3 = glmm_shuff1(r3, 1, 1, 1, 1);
+ x4 = glmm_shuff1(r3, 2, 2, 2, 2);
+
+ x0 = glmm_fmadd(r0, x2,
+ glmm_fmadd(r1, x3, wasm_f32x4_mul(r2, x4)));
+ x0 = wasm_f32x4_neg(x0);
+
+ x0 = wasm_f32x4_add(x0, x1);
+
+ glmm_store(mat[0], r0);
+ glmm_store(mat[1], r1);
+ glmm_store(mat[2], r2);
+ glmm_store(mat[3], x0);
+}
+
+#endif
+#endif /* cglm_affine_mat_wasm_h */
diff --git a/include/cglm/simd/wasm/mat2.h b/include/cglm/simd/wasm/mat2.h
new file mode 100644
index 0000000..80ce0fb
--- /dev/null
+++ b/include/cglm/simd/wasm/mat2.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat2_wasm_h
+#define cglm_mat2_wasm_h
+#if defined(__wasm__) && defined(__wasm_simd128__)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mat2_mul_wasm(mat2 m1, mat2 m2, mat2 dest) {
+ glmm_128 x0, x1, x2, x3, x4;
+
+ x1 = glmm_load(m1[0]); /* d c b a */
+ x2 = glmm_load(m2[0]); /* h g f e */
+
+ x3 = glmm_shuff1(x2, 2, 2, 0, 0);
+ x4 = glmm_shuff1(x2, 3, 3, 1, 1);
+ /* x0 = _mm_movelh_ps(x1, x1); */
+ x0 = wasm_i32x4_shuffle(x1, x1, 0, 1, 4, 5);
+ /* x2 = _mm_movehl_ps(x1, x1); */
+ x2 = wasm_i32x4_shuffle(x1, x1, 6, 7, 2, 3);
+
+ /*
+ dest[0][0] = a * e + c * f;
+ dest[0][1] = b * e + d * f;
+ dest[1][0] = a * g + c * h;
+ dest[1][1] = b * g + d * h;
+ */
+ x0 = glmm_fmadd(x0, x3, wasm_f32x4_mul(x2, x4));
+
+ glmm_store(dest[0], x0);
+}
+
+CGLM_INLINE
+void
+glm_mat2_transp_wasm(mat2 m, mat2 dest) {
+ /* d c b a */
+ /* d b c a */
+ glmm_store(dest[0], glmm_shuff1(glmm_load(m[0]), 3, 1, 2, 0));
+}
+
+#endif
+#endif /* cglm_mat2_wasm_h */
diff --git a/include/cglm/simd/wasm/mat3.h b/include/cglm/simd/wasm/mat3.h
new file mode 100644
index 0000000..dfe192d
--- /dev/null
+++ b/include/cglm/simd/wasm/mat3.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat3_wasm_h
+#define cglm_mat3_wasm_h
+#if defined(__wasm__) && defined(__wasm_simd128__)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_mat3_mul_wasm(mat3 m1, mat3 m2, mat3 dest) {
+ glmm_128 l0, l1, l2, r0, r1, r2, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
+
+ l0 = wasm_v128_load(m1[0]);
+ l1 = wasm_v128_load(&m1[1][1]);
+
+ r0 = wasm_v128_load(m2[0]);
+ r1 = wasm_v128_load(&m2[1][1]);
+
+ x8 = glmm_shuff1(l0, 0, 2, 1, 0); /* a00 a02 a01 a00 */
+ x1 = glmm_shuff1(r0, 3, 0, 0, 0); /* b10 b00 b00 b00 */
+ x2 = wasm_i32x4_shuffle(l0, l1, 3, 3, 4, 5); /* a12 a11 a10 a10 */
+ x3 = wasm_i32x4_shuffle(r0, r1, 1, 3, 4, 6); /* b20 b11 b10 b01 */
+ x0 = wasm_f32x4_mul(x8, x1);
+
+ x6 = glmm_shuff1(l0, 1, 0, 2, 1); /* a01 a00 a02 a01 */
+ x7 = glmm_shuff1(x3, 3, 3, 1, 1); /* b20 b20 b10 b10 */
+ l2 = wasm_v128_load32_zero(&m1[2][2]);
+ r2 = wasm_v128_load32_zero(&m2[2][2]);
+ x1 = wasm_f32x4_mul(x6, x7);
+ l2 = glmm_shuff1(l2, 0, 0, 1, 0); /* a22 a22 0.f a22 */
+ r2 = glmm_shuff1(r2, 0, 0, 1, 0); /* b22 b22 0.f b22 */
+
+ x4 = glmm_shuff1(x2, 0, 3, 2, 0); /* a10 a12 a11 a10 */
+ x5 = glmm_shuff1(x2, 2, 0, 3, 2); /* a11 a10 a12 a11 */
+ x6 = glmm_shuff1(x3, 2, 0, 0, 0); /* b11 b01 b01 b01 */
+ x2 = glmm_shuff1(r1, 3, 3, 0, 0); /* b21 b21 b11 b11 */
+
+ /* x8 = _mm_unpackhi_ps(x8, x4); */
+ /* x9 = _mm_unpackhi_ps(x7, x2); */
+ x8 = wasm_i32x4_shuffle(x8, x4, 2, 6, 3, 7); /* a10 a00 a12 a02 */
+ x9 = wasm_i32x4_shuffle(x7, x2, 2, 6, 3, 7); /* b21 b20 b21 b20 */
+
+ x0 = glmm_fmadd(x4, x6, x0);
+ x1 = glmm_fmadd(x5, x2, x1);
+
+ /* x2 = _mm_movehl_ps(l2, l1); */
+ x2 = wasm_i32x4_shuffle(l2, l1, 6, 7, 2, 3); /* a22 a22 a21 a20 */
+ x3 = glmm_shuff1(x2, 0, 2, 1, 0); /* a20 a22 a21 a20 */
+ x2 = glmm_shuff1(x2, 1, 0, 2, 1); /* a21 a20 a22 a21 */
+ x4 = wasm_i32x4_shuffle(r0, r1, 2, 2, 5, 5); /* b12 b12 b02 b02 */
+
+ x5 = glmm_shuff1(x4, 3, 0, 0, 0); /* b12 b02 b02 b02 */
+ /* x4 = _mm_movehl_ps(r2, x4); */
+ x4 = wasm_i32x4_shuffle(r2, x4, 6, 7, 2, 3); /* b22 b22 b12 b12 */
+ x0 = glmm_fmadd(x3, x5, x0);
+ x1 = glmm_fmadd(x2, x4, x1);
+
+ /*
+ Dot Product : dest[2][2] = a02 * b20 +
+ a12 * b21 +
+ a22 * b22 +
+ 0 * 00 */
+ /* x2 = _mm_movelh_ps(x8, l2); */
+ /* x3 = _mm_movelh_ps(x9, r2); */
+ x2 = wasm_i32x4_shuffle(x8, l2, 0, 1, 4, 5); /* 0.f a22 a12 a02 */
+ x3 = wasm_i32x4_shuffle(x9, r2, 0, 1, 4, 5); /* 0.f b22 b21 b20 */
+ x2 = glmm_vdots(x2, x3);
+
+ /* _mm_storeu_ps(&dest[0][0], x0); */
+ wasm_v128_store(&dest[0][0], x0);
+ /* _mm_storeu_ps(&dest[1][1], x1); */
+ wasm_v128_store(&dest[1][1], x1);
+ /* _mm_store_ss (&dest[2][2], x2); */
+ wasm_v128_store32_lane(&dest[2][2], x2, 0);
+}
+
+#endif
+#endif /* cglm_mat3_wasm_h */
diff --git a/include/cglm/simd/wasm/mat4.h b/include/cglm/simd/wasm/mat4.h
new file mode 100644
index 0000000..79ed688
--- /dev/null
+++ b/include/cglm/simd/wasm/mat4.h
@@ -0,0 +1,454 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_mat_wasm_h
+#define cglm_mat_wasm_h
+#if defined(__wasm__) && defined(__wasm_simd128__)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+#define glm_mat4_inv_precise_wasm(mat, dest) glm_mat4_inv_wasm(mat, dest)
+
+CGLM_INLINE
+void
+glm_mat4_scale_wasm(mat4 m, float s) {
+ glmm_128 x0;
+ x0 = wasm_f32x4_splat(s);
+
+ glmm_store(m[0], wasm_f32x4_mul(glmm_load(m[0]), x0));
+ glmm_store(m[1], wasm_f32x4_mul(glmm_load(m[1]), x0));
+ glmm_store(m[2], wasm_f32x4_mul(glmm_load(m[2]), x0));
+ glmm_store(m[3], wasm_f32x4_mul(glmm_load(m[3]), x0));
+}
+
+CGLM_INLINE
+void
+glm_mat4_transp_wasm(mat4 m, mat4 dest) {
+ glmm_128 r0, r1, r2, r3, tmp0, tmp1, tmp2, tmp3;
+
+ r0 = glmm_load(m[0]);
+ r1 = glmm_load(m[1]);
+ r2 = glmm_load(m[2]);
+ r3 = glmm_load(m[3]);
+
+ /* _MM_TRANSPOSE4_PS(r0, r1, r2, r3); */
+ tmp0 = wasm_i32x4_shuffle(r0, r1, 0, 4, 1, 5);
+ tmp1 = wasm_i32x4_shuffle(r0, r1, 2, 6, 3, 7);
+ tmp2 = wasm_i32x4_shuffle(r2, r3, 0, 4, 1, 5);
+ tmp3 = wasm_i32x4_shuffle(r2, r3, 2, 6, 3, 7);
+ /* r0 = _mm_movelh_ps(tmp0, tmp2); */
+ r0 = wasm_i32x4_shuffle(tmp0, tmp2, 0, 1, 4, 5);
+ /* r1 = _mm_movehl_ps(tmp2, tmp0); */
+ r1 = wasm_i32x4_shuffle(tmp2, tmp0, 6, 7, 2, 3);
+ /* r2 = _mm_movelh_ps(tmp1, tmp3); */
+ r2 = wasm_i32x4_shuffle(tmp1, tmp3, 0, 1, 4, 5);
+ /* r3 = _mm_movehl_ps(tmp3, tmp1); */
+ r3 = wasm_i32x4_shuffle(tmp3, tmp1, 6, 7, 2, 3);
+
+ glmm_store(dest[0], r0);
+ glmm_store(dest[1], r1);
+ glmm_store(dest[2], r2);
+ glmm_store(dest[3], r3);
+}
+
+CGLM_INLINE
+void
+glm_mat4_mul_wasm(mat4 m1, mat4 m2, mat4 dest) {
+ /* D = R * L (Column-Major) */
+
+ glmm_128 l, r0, r1, r2, r3, v0, v1, v2, v3;
+
+ l = glmm_load(m1[0]);
+ r0 = glmm_load(m2[0]);
+ r1 = glmm_load(m2[1]);
+ r2 = glmm_load(m2[2]);
+ r3 = glmm_load(m2[3]);
+
+ v0 = wasm_f32x4_mul(glmm_splat_x(r0), l);
+ v1 = wasm_f32x4_mul(glmm_splat_x(r1), l);
+ v2 = wasm_f32x4_mul(glmm_splat_x(r2), l);
+ v3 = wasm_f32x4_mul(glmm_splat_x(r3), l);
+
+ l = glmm_load(m1[1]);
+ v0 = glmm_fmadd(glmm_splat_y(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_y(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_y(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_y(r3), l, v3);
+
+ l = glmm_load(m1[2]);
+ v0 = glmm_fmadd(glmm_splat_z(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_z(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_z(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_z(r3), l, v3);
+
+ l = glmm_load(m1[3]);
+ v0 = glmm_fmadd(glmm_splat_w(r0), l, v0);
+ v1 = glmm_fmadd(glmm_splat_w(r1), l, v1);
+ v2 = glmm_fmadd(glmm_splat_w(r2), l, v2);
+ v3 = glmm_fmadd(glmm_splat_w(r3), l, v3);
+
+ glmm_store(dest[0], v0);
+ glmm_store(dest[1], v1);
+ glmm_store(dest[2], v2);
+ glmm_store(dest[3], v3);
+}
+
+CGLM_INLINE
+void
+glm_mat4_mulv_wasm(mat4 m, vec4 v, vec4 dest) {
+ glmm_128 x0, x1, m0, m1, m2, m3, v0, v1, v2, v3;
+
+ m0 = glmm_load(m[0]);
+ m1 = glmm_load(m[1]);
+ m2 = glmm_load(m[2]);
+ m3 = glmm_load(m[3]);
+
+ x0 = glmm_load(v);
+ v0 = glmm_splat_x(x0);
+ v1 = glmm_splat_y(x0);
+ v2 = glmm_splat_z(x0);
+ v3 = glmm_splat_w(x0);
+
+ x1 = wasm_f32x4_mul(m3, v3);
+ x1 = glmm_fmadd(m2, v2, x1);
+ x1 = glmm_fmadd(m1, v1, x1);
+ x1 = glmm_fmadd(m0, v0, x1);
+
+ glmm_store(dest, x1);
+}
+
+CGLM_INLINE
+float
+glm_mat4_det_wasm(mat4 mat) {
+ glmm_128 r0, r1, r2, r3, x0, x1, x2;
+
+ /* 127 <- 0, [square] det(A) = det(At) */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r2 = glmm_load(mat[2]); /* l k j i */
+ r3 = glmm_load(mat[3]); /* p o n m */
+
+ /*
+ t[1] = j * p - n * l;
+ t[2] = j * o - n * k;
+ t[3] = i * p - m * l;
+ t[4] = i * o - m * k;
+ */
+ x0 = glmm_fnmadd(glmm_shuff1(r3, 0, 0, 1, 1), glmm_shuff1(r2, 2, 3, 2, 3),
+ wasm_f32x4_mul(glmm_shuff1(r2, 0, 0, 1, 1),
+ glmm_shuff1(r3, 2, 3, 2, 3)));
+ /*
+ t[0] = k * p - o * l;
+ t[0] = k * p - o * l;
+ t[5] = i * n - m * j;
+ t[5] = i * n - m * j;
+ */
+ x1 = glmm_fnmadd(glmm_shuff1(r3, 0, 0, 2, 2), glmm_shuff1(r2, 1, 1, 3, 3),
+ wasm_f32x4_mul(glmm_shuff1(r2, 0, 0, 2, 2),
+ glmm_shuff1(r3, 1, 1, 3, 3)));
+
+ /*
+ a * (f * t[0] - g * t[1] + h * t[2])
+ - b * (e * t[0] - g * t[3] + h * t[4])
+ + c * (e * t[1] - f * t[3] + h * t[5])
+ - d * (e * t[2] - f * t[4] + g * t[5])
+ */
+ x2 = glmm_fnmadd(glmm_shuff1(r1, 1, 1, 2, 2), glmm_shuff1(x0, 3, 2, 2, 0),
+ wasm_f32x4_mul(glmm_shuff1(r1, 0, 0, 0, 1),
+ wasm_i32x4_shuffle(x1, x0, 0, 0, 4, 5)));
+ x2 = glmm_fmadd(glmm_shuff1(r1, 2, 3, 3, 3),
+ wasm_i32x4_shuffle(x0, x1, 1, 3, 6, 6),
+ x2);
+ /* x2 = wasm_v128_xor(x2, wasm_f32x4_const(0.f, -0.f, 0.f, -0.f)); */
+ x2 = wasm_v128_xor(x2, glmm_float32x4_SIGNMASK_PNPN);
+
+ return glmm_hadd(wasm_f32x4_mul(x2, r0));
+}
+
+CGLM_INLINE
+void
+glm_mat4_inv_fast_wasm(mat4 mat, mat4 dest) {
+ glmm_128 r0, r1, r2, r3,
+ v0, v1, v2, v3,
+ t0, t1, t2, t3, t4, t5,
+ x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
+
+ /* x8 = wasm_f32x4_const(0.f, -0.f, 0.f, -0.f); */
+ x8 = glmm_float32x4_SIGNMASK_PNPN;
+ x9 = glmm_shuff1(x8, 2, 1, 2, 1);
+
+ /* 127 <- 0 */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r2 = glmm_load(mat[2]); /* l k j i */
+ r3 = glmm_load(mat[3]); /* p o n m */
+ /* x0 = _mm_movehl_ps(r3, r2); */
+ x0 = wasm_i32x4_shuffle(r3, r2, 6, 7, 2, 3); /* p o l k */
+ /* x3 = _mm_movelh_ps(r2, r3); */
+ x3 = wasm_i32x4_shuffle(r2, r3, 0, 1, 4, 5); /* n m j i */
+ x1 = glmm_shuff1(x0, 1, 3, 3 ,3); /* l p p p */
+ x2 = glmm_shuff1(x0, 0, 2, 2, 2); /* k o o o */
+ x4 = glmm_shuff1(x3, 1, 3, 3, 3); /* j n n n */
+ x7 = glmm_shuff1(x3, 0, 2, 2, 2); /* i m m m */
+
+ x6 = wasm_i32x4_shuffle(r2, r1, 0, 0, 4, 4); /* e e i i */
+ x5 = wasm_i32x4_shuffle(r2, r1, 1, 1, 5, 5); /* f f j j */
+ x3 = wasm_i32x4_shuffle(r2, r1, 2, 2, 6, 6); /* g g k k */
+ x0 = wasm_i32x4_shuffle(r2, r1, 3, 3, 7, 7); /* h h l l */
+
+ t0 = wasm_f32x4_mul(x3, x1);
+ t1 = wasm_f32x4_mul(x5, x1);
+ t2 = wasm_f32x4_mul(x5, x2);
+ t3 = wasm_f32x4_mul(x6, x1);
+ t4 = wasm_f32x4_mul(x6, x2);
+ t5 = wasm_f32x4_mul(x6, x4);
+
+ /* t1[0] = k * p - o * l;
+ t1[0] = k * p - o * l;
+ t2[0] = g * p - o * h;
+ t3[0] = g * l - k * h; */
+ t0 = glmm_fnmadd(x2, x0, t0);
+
+ /* t1[1] = j * p - n * l;
+ t1[1] = j * p - n * l;
+ t2[1] = f * p - n * h;
+ t3[1] = f * l - j * h; */
+ t1 = glmm_fnmadd(x4, x0, t1);
+
+ /* t1[2] = j * o - n * k
+ t1[2] = j * o - n * k;
+ t2[2] = f * o - n * g;
+ t3[2] = f * k - j * g; */
+ t2 = glmm_fnmadd(x4, x3, t2);
+
+ /* t1[3] = i * p - m * l;
+ t1[3] = i * p - m * l;
+ t2[3] = e * p - m * h;
+ t3[3] = e * l - i * h; */
+ t3 = glmm_fnmadd(x7, x0, t3);
+
+ /* t1[4] = i * o - m * k;
+ t1[4] = i * o - m * k;
+ t2[4] = e * o - m * g;
+ t3[4] = e * k - i * g; */
+ t4 = glmm_fnmadd(x7, x3, t4);
+
+ /* t1[5] = i * n - m * j;
+ t1[5] = i * n - m * j;
+ t2[5] = e * n - m * f;
+ t3[5] = e * j - i * f; */
+ t5 = glmm_fnmadd(x7, x5, t5);
+ /* x4 = _mm_movelh_ps(r0, r1); */
+ x4 = wasm_i32x4_shuffle(r0, r1, 0, 1, 4, 5); /* f e b a */
+ /* x5 = _mm_movehl_ps(r1, r0); */
+ x5 = wasm_i32x4_shuffle(r1, r0, 6, 7, 2, 3); /* h g d c */
+
+ x0 = glmm_shuff1(x4, 0, 0, 0, 2); /* a a a e */
+ x1 = glmm_shuff1(x4, 1, 1, 1, 3); /* b b b f */
+ x2 = glmm_shuff1(x5, 0, 0, 0, 2); /* c c c g */
+ x3 = glmm_shuff1(x5, 1, 1, 1, 3); /* d d d h */
+
+ v2 = wasm_f32x4_mul(x0, t1);
+ v1 = wasm_f32x4_mul(x0, t0);
+ v3 = wasm_f32x4_mul(x0, t2);
+ v0 = wasm_f32x4_mul(x1, t0);
+
+ v2 = glmm_fnmadd(x1, t3, v2);
+ v3 = glmm_fnmadd(x1, t4, v3);
+ v0 = glmm_fnmadd(x2, t1, v0);
+ v1 = glmm_fnmadd(x2, t3, v1);
+
+ v3 = glmm_fmadd(x2, t5, v3);
+ v0 = glmm_fmadd(x3, t2, v0);
+ v2 = glmm_fmadd(x3, t5, v2);
+ v1 = glmm_fmadd(x3, t4, v1);
+
+ /*
+ dest[0][0] = f * t1[0] - g * t1[1] + h * t1[2];
+ dest[0][1] =-(b * t1[0] - c * t1[1] + d * t1[2]);
+ dest[0][2] = b * t2[0] - c * t2[1] + d * t2[2];
+ dest[0][3] =-(b * t3[0] - c * t3[1] + d * t3[2]); */
+ v0 = wasm_v128_xor(v0, x8);
+
+ /*
+ dest[2][0] = e * t1[1] - f * t1[3] + h * t1[5];
+ dest[2][1] =-(a * t1[1] - b * t1[3] + d * t1[5]);
+ dest[2][2] = a * t2[1] - b * t2[3] + d * t2[5];
+ dest[2][3] =-(a * t3[1] - b * t3[3] + d * t3[5]);*/
+ v2 = wasm_v128_xor(v2, x8);
+
+ /*
+ dest[1][0] =-(e * t1[0] - g * t1[3] + h * t1[4]);
+ dest[1][1] = a * t1[0] - c * t1[3] + d * t1[4];
+ dest[1][2] =-(a * t2[0] - c * t2[3] + d * t2[4]);
+ dest[1][3] = a * t3[0] - c * t3[3] + d * t3[4]; */
+ v1 = wasm_v128_xor(v1, x9);
+
+ /*
+ dest[3][0] =-(e * t1[2] - f * t1[4] + g * t1[5]);
+ dest[3][1] = a * t1[2] - b * t1[4] + c * t1[5];
+ dest[3][2] =-(a * t2[2] - b * t2[4] + c * t2[5]);
+ dest[3][3] = a * t3[2] - b * t3[4] + c * t3[5]; */
+ v3 = wasm_v128_xor(v3, x9);
+
+ /* determinant */
+ x0 = wasm_i32x4_shuffle(v0, v1, 0, 0, 4, 4);
+ x1 = wasm_i32x4_shuffle(v2, v3, 0, 0, 4, 4);
+ x0 = wasm_i32x4_shuffle(x0, x1, 0, 2, 4, 6);
+
+ /* x0 = _mm_rcp_ps(glmm_vhadd(wasm_f32x4_mul(x0, r0))); */
+ x0 = wasm_f32x4_div(wasm_f32x4_const_splat(1.0f),
+ glmm_vhadd(wasm_f32x4_mul(x0, r0)));
+
+ glmm_store(dest[0], wasm_f32x4_mul(v0, x0));
+ glmm_store(dest[1], wasm_f32x4_mul(v1, x0));
+ glmm_store(dest[2], wasm_f32x4_mul(v2, x0));
+ glmm_store(dest[3], wasm_f32x4_mul(v3, x0));
+}
+
+CGLM_INLINE
+void
+glm_mat4_inv_wasm(mat4 mat, mat4 dest) {
+ glmm_128 r0, r1, r2, r3,
+ v0, v1, v2, v3,
+ t0, t1, t2, t3, t4, t5,
+ x0, x1, x2, x3, x4, x5, x6, x7, x8, x9;
+
+ /* x8 = wasm_f32x4_const(0.f, -0.f, 0.f, -0.f); */
+ x8 = glmm_float32x4_SIGNMASK_PNPN;
+ x9 = glmm_shuff1(x8, 2, 1, 2, 1);
+
+ /* 127 <- 0 */
+ r0 = glmm_load(mat[0]); /* d c b a */
+ r1 = glmm_load(mat[1]); /* h g f e */
+ r2 = glmm_load(mat[2]); /* l k j i */
+ r3 = glmm_load(mat[3]); /* p o n m */
+ /* x0 = _mm_movehl_ps(r3, r2); */
+ x0 = wasm_i32x4_shuffle(r3, r2, 6, 7, 2, 3); /* p o l k */
+ /* x3 = _mm_movelh_ps(r2, r3); */
+ x3 = wasm_i32x4_shuffle(r2, r3, 0, 1, 4, 5); /* n m j i */
+ x1 = glmm_shuff1(x0, 1, 3, 3 ,3); /* l p p p */
+ x2 = glmm_shuff1(x0, 0, 2, 2, 2); /* k o o o */
+ x4 = glmm_shuff1(x3, 1, 3, 3, 3); /* j n n n */
+ x7 = glmm_shuff1(x3, 0, 2, 2, 2); /* i m m m */
+
+ x6 = wasm_i32x4_shuffle(r2, r1, 0, 0, 4, 4); /* e e i i */
+ x5 = wasm_i32x4_shuffle(r2, r1, 1, 1, 5, 5); /* f f j j */
+ x3 = wasm_i32x4_shuffle(r2, r1, 2, 2, 6, 6); /* g g k k */
+ x0 = wasm_i32x4_shuffle(r2, r1, 3, 3, 7, 7); /* h h l l */
+
+ t0 = wasm_f32x4_mul(x3, x1);
+ t1 = wasm_f32x4_mul(x5, x1);
+ t2 = wasm_f32x4_mul(x5, x2);
+ t3 = wasm_f32x4_mul(x6, x1);
+ t4 = wasm_f32x4_mul(x6, x2);
+ t5 = wasm_f32x4_mul(x6, x4);
+
+ /* t1[0] = k * p - o * l;
+ t1[0] = k * p - o * l;
+ t2[0] = g * p - o * h;
+ t3[0] = g * l - k * h; */
+ t0 = glmm_fnmadd(x2, x0, t0);
+
+ /* t1[1] = j * p - n * l;
+ t1[1] = j * p - n * l;
+ t2[1] = f * p - n * h;
+ t3[1] = f * l - j * h; */
+ t1 = glmm_fnmadd(x4, x0, t1);
+
+ /* t1[2] = j * o - n * k
+ t1[2] = j * o - n * k;
+ t2[2] = f * o - n * g;
+ t3[2] = f * k - j * g; */
+ t2 = glmm_fnmadd(x4, x3, t2);
+
+ /* t1[3] = i * p - m * l;
+ t1[3] = i * p - m * l;
+ t2[3] = e * p - m * h;
+ t3[3] = e * l - i * h; */
+ t3 = glmm_fnmadd(x7, x0, t3);
+
+ /* t1[4] = i * o - m * k;
+ t1[4] = i * o - m * k;
+ t2[4] = e * o - m * g;
+ t3[4] = e * k - i * g; */
+ t4 = glmm_fnmadd(x7, x3, t4);
+
+ /* t1[5] = i * n - m * j;
+ t1[5] = i * n - m * j;
+ t2[5] = e * n - m * f;
+ t3[5] = e * j - i * f; */
+ t5 = glmm_fnmadd(x7, x5, t5);
+ /* x4 = _mm_movelh_ps(r0, r1); */
+ x4 = wasm_i32x4_shuffle(r0, r1, 0, 1, 4, 5); /* f e b a */
+ /* x5 = _mm_movehl_ps(r1, r0); */
+ x5 = wasm_i32x4_shuffle(r1, r0, 6, 7, 2, 3); /* h g d c */
+
+ x0 = glmm_shuff1(x4, 0, 0, 0, 2); /* a a a e */
+ x1 = glmm_shuff1(x4, 1, 1, 1, 3); /* b b b f */
+ x2 = glmm_shuff1(x5, 0, 0, 0, 2); /* c c c g */
+ x3 = glmm_shuff1(x5, 1, 1, 1, 3); /* d d d h */
+
+ v2 = wasm_f32x4_mul(x0, t1);
+ v1 = wasm_f32x4_mul(x0, t0);
+ v3 = wasm_f32x4_mul(x0, t2);
+ v0 = wasm_f32x4_mul(x1, t0);
+
+ v2 = glmm_fnmadd(x1, t3, v2);
+ v3 = glmm_fnmadd(x1, t4, v3);
+ v0 = glmm_fnmadd(x2, t1, v0);
+ v1 = glmm_fnmadd(x2, t3, v1);
+
+ v3 = glmm_fmadd(x2, t5, v3);
+ v0 = glmm_fmadd(x3, t2, v0);
+ v2 = glmm_fmadd(x3, t5, v2);
+ v1 = glmm_fmadd(x3, t4, v1);
+
+ /*
+ dest[0][0] = f * t1[0] - g * t1[1] + h * t1[2];
+ dest[0][1] =-(b * t1[0] - c * t1[1] + d * t1[2]);
+ dest[0][2] = b * t2[0] - c * t2[1] + d * t2[2];
+ dest[0][3] =-(b * t3[0] - c * t3[1] + d * t3[2]); */
+ v0 = wasm_v128_xor(v0, x8);
+
+ /*
+ dest[2][0] = e * t1[1] - f * t1[3] + h * t1[5];
+ dest[2][1] =-(a * t1[1] - b * t1[3] + d * t1[5]);
+ dest[2][2] = a * t2[1] - b * t2[3] + d * t2[5];
+ dest[2][3] =-(a * t3[1] - b * t3[3] + d * t3[5]);*/
+ v2 = wasm_v128_xor(v2, x8);
+
+ /*
+ dest[1][0] =-(e * t1[0] - g * t1[3] + h * t1[4]);
+ dest[1][1] = a * t1[0] - c * t1[3] + d * t1[4];
+ dest[1][2] =-(a * t2[0] - c * t2[3] + d * t2[4]);
+ dest[1][3] = a * t3[0] - c * t3[3] + d * t3[4]; */
+ v1 = wasm_v128_xor(v1, x9);
+
+ /*
+ dest[3][0] =-(e * t1[2] - f * t1[4] + g * t1[5]);
+ dest[3][1] = a * t1[2] - b * t1[4] + c * t1[5];
+ dest[3][2] =-(a * t2[2] - b * t2[4] + c * t2[5]);
+ dest[3][3] = a * t3[2] - b * t3[4] + c * t3[5]; */
+ v3 = wasm_v128_xor(v3, x9);
+
+ /* determinant */
+ x0 = wasm_i32x4_shuffle(v0, v1, 0, 0, 4, 4);
+ x1 = wasm_i32x4_shuffle(v2, v3, 0, 0, 4, 4);
+ x0 = wasm_i32x4_shuffle(x0, x1, 0, 2, 4, 6);
+
+ x0 = wasm_f32x4_div(wasm_f32x4_splat(1.0f), glmm_vhadd(wasm_f32x4_mul(x0, r0)));
+
+ glmm_store(dest[0], wasm_f32x4_mul(v0, x0));
+ glmm_store(dest[1], wasm_f32x4_mul(v1, x0));
+ glmm_store(dest[2], wasm_f32x4_mul(v2, x0));
+ glmm_store(dest[3], wasm_f32x4_mul(v3, x0));
+}
+
+#endif
+#endif /* cglm_mat_wasm_h */
diff --git a/include/cglm/simd/wasm/quat.h b/include/cglm/simd/wasm/quat.h
new file mode 100644
index 0000000..8d72546
--- /dev/null
+++ b/include/cglm/simd/wasm/quat.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_quat_wasm_h
+#define cglm_quat_wasm_h
+#if defined(__wasm__) && defined(__wasm_simd128__)
+
+#include "../../common.h"
+#include "../intrin.h"
+
+CGLM_INLINE
+void
+glm_quat_mul_wasm(versor p, versor q, versor dest) {
+ /*
+ + (a1 b2 + b1 a2 + c1 d2 − d1 c2)i
+ + (a1 c2 − b1 d2 + c1 a2 + d1 b2)j
+ + (a1 d2 + b1 c2 − c1 b2 + d1 a2)k
+ a1 a2 − b1 b2 − c1 c2 − d1 d2
+ */
+
+ glmm_128 xp, xq, x1, x2, x3, r, x, y, z;
+
+ xp = glmm_load(p); /* 3 2 1 0 */
+ xq = glmm_load(q);
+ /* x1 = wasm_f32x4_const(0.f, -0.f, 0.f, -0.f); */
+ x1 = glmm_float32x4_SIGNMASK_PNPN; /* TODO: _mm_set1_ss() + shuff ? */
+ r = wasm_f32x4_mul(glmm_splat_w(xp), xq);
+ /* x2 = _mm_unpackhi_ps(x1, x1); */
+ x2 = wasm_i32x4_shuffle(x1, x1, 2, 6, 3, 7);
+ x3 = glmm_shuff1(x1, 3, 2, 0, 1);
+ x = glmm_splat_x(xp);
+ y = glmm_splat_y(xp);
+ z = glmm_splat_z(xp);
+
+ x = wasm_v128_xor(x, x1);
+ y = wasm_v128_xor(y, x2);
+ z = wasm_v128_xor(z, x3);
+
+ x1 = glmm_shuff1(xq, 0, 1, 2, 3);
+ x2 = glmm_shuff1(xq, 1, 0, 3, 2);
+ x3 = glmm_shuff1(xq, 2, 3, 0, 1);
+
+ r = glmm_fmadd(x, x1, r);
+ r = glmm_fmadd(y, x2, r);
+ r = glmm_fmadd(z, x3, r);
+
+ glmm_store(dest, r);
+}
+
+#endif
+#endif /* cglm_quat_wasm_h */
diff --git a/include/cglm/simd/x86.h b/include/cglm/simd/x86.h
new file mode 100644
index 0000000..2410d0f
--- /dev/null
+++ b/include/cglm/simd/x86.h
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c), Recep Aslantas.
+ *
+ * MIT License (MIT), http://opensource.org/licenses/MIT
+ * Full license can be found in the LICENSE file
+ */
+
+#ifndef cglm_simd_x86_h
+#define cglm_simd_x86_h
+#include "intrin.h"
+#ifdef CGLM_SIMD_x86
+
+#ifdef CGLM_ALL_UNALIGNED
+# define glmm_load(p) _mm_loadu_ps(p)
+# define glmm_store(p, a) _mm_storeu_ps(p, a)
+#else
+# define glmm_load(p) _mm_load_ps(p)
+# define glmm_store(p, a) _mm_store_ps(p, a)
+#endif
+
+#define glmm_128 __m128
+
+#ifdef __AVX__
+# define glmm_shuff1(xmm, z, y, x, w) \
+ _mm_permute_ps((xmm), _MM_SHUFFLE(z, y, x, w))
+#else
+# if !defined(CGLM_NO_INT_DOMAIN) && defined(__SSE2__)
+# define glmm_shuff1(xmm, z, y, x, w) \
+ _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(xmm), \
+ _MM_SHUFFLE(z, y, x, w)))
+# else
+# define glmm_shuff1(xmm, z, y, x, w) \
+ _mm_shuffle_ps(xmm, xmm, _MM_SHUFFLE(z, y, x, w))
+# endif
+#endif
+
+#define glmm_splat(x, lane) glmm_shuff1(x, lane, lane, lane, lane)
+
+#ifdef __AVX__
+# define glmm_set1(x) _mm_broadcast_ss(&x)
+# define glmm_set1_ptr(x) _mm_broadcast_ss(x)
+# define glmm_set1_rval(x) _mm_set1_ps(x)
+# ifdef __AVX2__
+# define glmm_splat_x(x) _mm_broadcastss_ps(x)
+# else
+# define glmm_splat_x(x) _mm_permute_ps(x, _MM_SHUFFLE(0, 0, 0, 0))
+# endif
+# define glmm_splat_y(x) _mm_permute_ps(x, _MM_SHUFFLE(1, 1, 1, 1))
+# define glmm_splat_z(x) _mm_permute_ps(x, _MM_SHUFFLE(2, 2, 2, 2))
+# define glmm_splat_w(x) _mm_permute_ps(x, _MM_SHUFFLE(3, 3, 3, 3))
+#else
+# define glmm_set1(x) _mm_set1_ps(x)
+# define glmm_set1_ptr(x) _mm_set1_ps(*x)
+# define glmm_set1_rval(x) _mm_set1_ps(x)
+
+# define glmm_splat_x(x) glmm_splat(x, 0)
+# define glmm_splat_y(x) glmm_splat(x, 1)
+# define glmm_splat_z(x) glmm_splat(x, 2)
+# define glmm_splat_w(x) glmm_splat(x, 3)
+#endif
+
+#ifdef __AVX__
+# ifdef CGLM_ALL_UNALIGNED
+# define glmm_load256(p) _mm256_loadu_ps(p)
+# define glmm_store256(p, a) _mm256_storeu_ps(p, a)
+# else
+# define glmm_load256(p) _mm256_load_ps(p)
+# define glmm_store256(p, a) _mm256_store_ps(p, a)
+# endif
+#endif
+
+/* Note that `0x80000000` corresponds to `INT_MIN` for a 32-bit int. */
+
+#if defined(__SSE2__)
+# define GLMM_NEGZEROf ((int)0x80000000) /* 0x80000000 ---> -0.0f */
+# define GLMM_POSZEROf ((int)0x00000000) /* 0x00000000 ---> +0.0f */
+#else
+# ifdef CGLM_FAST_MATH
+ union { int i; float f; } static GLMM_NEGZEROf_TU = { .i = (int)0x80000000 };
+# define GLMM_NEGZEROf GLMM_NEGZEROf_TU.f
+# define GLMM_POSZEROf 0.0f
+# else
+# define GLMM_NEGZEROf -0.0f
+# define GLMM_POSZEROf 0.0f
+# endif
+#endif
+
+#if defined(__SSE2__)
+# define GLMM__SIGNMASKf(X, Y, Z, W) \
+ _mm_castsi128_ps(_mm_set_epi32(X, Y, Z, W))
+ /* _mm_set_ps(X, Y, Z, W); */
+#else
+# define GLMM__SIGNMASKf(X, Y, Z, W) _mm_set_ps(X, Y, Z, W)
+#endif
+
+#define glmm_float32x4_SIGNMASK_PNPN GLMM__SIGNMASKf(GLMM_POSZEROf, GLMM_NEGZEROf, GLMM_POSZEROf, GLMM_NEGZEROf)
+#define glmm_float32x4_SIGNMASK_NPNP GLMM__SIGNMASKf(GLMM_NEGZEROf, GLMM_POSZEROf, GLMM_NEGZEROf, GLMM_POSZEROf)
+#define glmm_float32x4_SIGNMASK_NPPN GLMM__SIGNMASKf(GLMM_NEGZEROf, GLMM_POSZEROf, GLMM_POSZEROf, GLMM_NEGZEROf)
+
+/* fasth math prevents -0.0f to work */
+#if defined(__SSE2__)
+# define glmm_float32x4_SIGNMASK_NEG _mm_castsi128_ps(_mm_set1_epi32(GLMM_NEGZEROf)) /* _mm_set1_ps(-0.0f) */
+#else
+# define glmm_float32x4_SIGNMASK_NEG glmm_set1(GLMM_NEGZEROf)
+#endif
+
+#define glmm_float32x8_SIGNMASK_NEG _mm256_castsi256_ps(_mm256_set1_epi32(GLMM_NEGZEROf))
+
+static inline
+__m128
+glmm_abs(__m128 x) {
+ return _mm_andnot_ps(glmm_float32x4_SIGNMASK_NEG, x);
+}
+
+static inline __m128 glmm_min(__m128 a, __m128 b) { return _mm_min_ps(a, b); }
+static inline __m128 glmm_max(__m128 a, __m128 b) { return _mm_max_ps(a, b); }
+
+static inline
+__m128
+glmm_vhadd(__m128 v) {
+ __m128 x0;
+ x0 = _mm_add_ps(v, glmm_shuff1(v, 0, 1, 2, 3));
+ x0 = _mm_add_ps(x0, glmm_shuff1(x0, 1, 0, 0, 1));
+ return x0;
+}
+
+static inline
+__m128
+glmm_vhadds(__m128 v) {
+#if defined(__SSE3__)
+ __m128 shuf, sums;
+ shuf = _mm_movehdup_ps(v);
+ sums = _mm_add_ps(v, shuf);
+ shuf = _mm_movehl_ps(shuf, sums);
+ sums = _mm_add_ss(sums, shuf);
+ return sums;
+#else
+ __m128 shuf, sums;
+ shuf = glmm_shuff1(v, 2, 3, 0, 1);
+ sums = _mm_add_ps(v, shuf);
+ shuf = _mm_movehl_ps(shuf, sums);
+ sums = _mm_add_ss(sums, shuf);
+ return sums;
+#endif
+}
+
+static inline
+float
+glmm_hadd(__m128 v) {
+ return _mm_cvtss_f32(glmm_vhadds(v));
+}
+
+static inline
+__m128
+glmm_vhmin(__m128 v) {
+ __m128 x0, x1, x2;
+ x0 = _mm_movehl_ps(v, v); /* [2, 3, 2, 3] */
+ x1 = _mm_min_ps(x0, v); /* [0|2, 1|3, 2|2, 3|3] */
+ x2 = glmm_splat(x1, 1); /* [1|3, 1|3, 1|3, 1|3] */
+ return _mm_min_ss(x1, x2);
+}
+
+static inline
+float
+glmm_hmin(__m128 v) {
+ return _mm_cvtss_f32(glmm_vhmin(v));
+}
+
+static inline
+__m128
+glmm_vhmax(__m128 v) {
+ __m128 x0, x1, x2;
+ x0 = _mm_movehl_ps(v, v); /* [2, 3, 2, 3] */
+ x1 = _mm_max_ps(x0, v); /* [0|2, 1|3, 2|2, 3|3] */
+ x2 = glmm_splat(x1, 1); /* [1|3, 1|3, 1|3, 1|3] */
+ return _mm_max_ss(x1, x2);
+}
+
+static inline
+float
+glmm_hmax(__m128 v) {
+ return _mm_cvtss_f32(glmm_vhmax(v));
+}
+
+static inline
+__m128
+glmm_vdots(__m128 a, __m128 b) {
+#if (defined(__SSE4_1__) || defined(__SSE4_2__)) && defined(CGLM_SSE4_DOT)
+ return _mm_dp_ps(a, b, 0xFF);
+#elif defined(__SSE3__) && defined(CGLM_SSE3_DOT)
+ __m128 x0, x1;
+ x0 = _mm_mul_ps(a, b);
+ x1 = _mm_hadd_ps(x0, x0);
+ return _mm_hadd_ps(x1, x1);
+#else
+ return glmm_vhadds(_mm_mul_ps(a, b));
+#endif
+}
+
+static inline
+__m128
+glmm_vdot(__m128 a, __m128 b) {
+#if (defined(__SSE4_1__) || defined(__SSE4_2__)) && defined(CGLM_SSE4_DOT)
+ return _mm_dp_ps(a, b, 0xFF);
+#elif defined(__SSE3__) && defined(CGLM_SSE3_DOT)
+ __m128 x0, x1;
+ x0 = _mm_mul_ps(a, b);
+ x1 = _mm_hadd_ps(x0, x0);
+ return _mm_hadd_ps(x1, x1);
+#else
+ __m128 x0;
+ x0 = _mm_mul_ps(a, b);
+ x0 = _mm_add_ps(x0, glmm_shuff1(x0, 1, 0, 3, 2));
+ return _mm_add_ps(x0, glmm_shuff1(x0, 0, 1, 0, 1));
+#endif
+}
+
+static inline
+float
+glmm_dot(__m128 a, __m128 b) {
+ return _mm_cvtss_f32(glmm_vdots(a, b));
+}
+
+static inline
+float
+glmm_norm(__m128 a) {
+ return _mm_cvtss_f32(_mm_sqrt_ss(glmm_vhadds(_mm_mul_ps(a, a))));
+}
+
+static inline
+float
+glmm_norm2(__m128 a) {
+ return _mm_cvtss_f32(glmm_vhadds(_mm_mul_ps(a, a)));
+}
+
+static inline
+float
+glmm_norm_one(__m128 a) {
+ return _mm_cvtss_f32(glmm_vhadds(glmm_abs(a)));
+}
+
+static inline
+float
+glmm_norm_inf(__m128 a) {
+ return _mm_cvtss_f32(glmm_vhmax(glmm_abs(a)));
+}
+
+#if defined(__SSE2__)
+static inline
+__m128
+glmm_load3(float v[3]) {
+ __m128i xy;
+ __m128 z;
+
+ xy = _mm_loadl_epi64(CGLM_CASTPTR_ASSUME_ALIGNED(v, const __m128i));
+ z = _mm_load_ss(&v[2]);
+
+ return _mm_movelh_ps(_mm_castsi128_ps(xy), z);
+}
+
+static inline
+void
+glmm_store3(float v[3], __m128 vx) {
+ _mm_storel_pi(CGLM_CASTPTR_ASSUME_ALIGNED(v, __m64), vx);
+ _mm_store_ss(&v[2], glmm_shuff1(vx, 2, 2, 2, 2));
+}
+#endif
+
+static inline
+__m128
+glmm_div(__m128 a, __m128 b) {
+ return _mm_div_ps(a, b);
+}
+
+/* enable FMA macro for MSVC? */
+#if defined(_MSC_VER) && !defined(__FMA__) && defined(__AVX2__)
+# define __FMA__ 1
+#endif
+
+static inline
+__m128
+glmm_fmadd(__m128 a, __m128 b, __m128 c) {
+#ifdef __FMA__
+ return _mm_fmadd_ps(a, b, c);
+#else
+ return _mm_add_ps(c, _mm_mul_ps(a, b));
+#endif
+}
+
+static inline
+__m128
+glmm_fnmadd(__m128 a, __m128 b, __m128 c) {
+#ifdef __FMA__
+ return _mm_fnmadd_ps(a, b, c);
+#else
+ return _mm_sub_ps(c, _mm_mul_ps(a, b));
+#endif
+}
+
+static inline
+__m128
+glmm_fmsub(__m128 a, __m128 b, __m128 c) {
+#ifdef __FMA__
+ return _mm_fmsub_ps(a, b, c);
+#else
+ return _mm_sub_ps(_mm_mul_ps(a, b), c);
+#endif
+}
+
+static inline
+__m128
+glmm_fnmsub(__m128 a, __m128 b, __m128 c) {
+#ifdef __FMA__
+ return _mm_fnmsub_ps(a, b, c);
+#else
+ return _mm_xor_ps(_mm_add_ps(_mm_mul_ps(a, b), c),
+ glmm_float32x4_SIGNMASK_NEG);
+#endif
+}
+
+#if defined(__AVX__)
+static inline
+__m256
+glmm256_fmadd(__m256 a, __m256 b, __m256 c) {
+#ifdef __FMA__
+ return _mm256_fmadd_ps(a, b, c);
+#else
+ return _mm256_add_ps(c, _mm256_mul_ps(a, b));
+#endif
+}
+
+static inline
+__m256
+glmm256_fnmadd(__m256 a, __m256 b, __m256 c) {
+#ifdef __FMA__
+ return _mm256_fnmadd_ps(a, b, c);
+#else
+ return _mm256_sub_ps(c, _mm256_mul_ps(a, b));
+#endif
+}
+
+static inline
+__m256
+glmm256_fmsub(__m256 a, __m256 b, __m256 c) {
+#ifdef __FMA__
+ return _mm256_fmsub_ps(a, b, c);
+#else
+ return _mm256_sub_ps(_mm256_mul_ps(a, b), c);
+#endif
+}
+
+static inline
+__m256
+glmm256_fnmsub(__m256 a, __m256 b, __m256 c) {
+#ifdef __FMA__
+ return _mm256_fmsub_ps(a, b, c);
+#else
+ return _mm256_xor_ps(_mm256_sub_ps(_mm256_mul_ps(a, b), c),
+ glmm_float32x8_SIGNMASK_NEG);
+#endif
+}
+#endif
+
+#endif
+#endif /* cglm_simd_x86_h */