packages feed

geomancy-0.2.4.2: cbits/mat4.c

#if defined(__i386__) || defined(__x86_64__)

#include <xmmintrin.h>

void Mat4xMat4_SIMD(float *A, float *B, float *O) {
  __m128 row0 = _mm_load_ps(&B[0]);
  __m128 row1 = _mm_load_ps(&B[4]);
  __m128 row2 = _mm_load_ps(&B[8]);
  __m128 row3 = _mm_load_ps(&B[12]);
  for(int i=0; i<4; i++) {
    __m128 brod0 = _mm_set1_ps(A[4*i + 0]);
    __m128 brod1 = _mm_set1_ps(A[4*i + 1]);
    __m128 brod2 = _mm_set1_ps(A[4*i + 2]);
    __m128 brod3 = _mm_set1_ps(A[4*i + 3]);
    __m128 row = _mm_add_ps(
      _mm_add_ps(
        _mm_mul_ps(brod0, row0),
        _mm_mul_ps(brod1, row1)),
      _mm_add_ps(
        _mm_mul_ps(brod2, row2),
        _mm_mul_ps(brod3, row3)));
    _mm_store_ps(&O[4*i], row);
  }
}

void Mat4xVec4_SIMD(float *M, float *V, float *O) {
  __m128 row0 = _mm_load_ps(&M[0]);
  __m128 row1 = _mm_load_ps(&M[4]);
  __m128 row2 = _mm_load_ps(&M[8]);
  __m128 row3 = _mm_load_ps(&M[12]);

  __m128 vec0 = _mm_load_ps(V);

  __m128 vec00 = _mm_shuffle_ps(vec0, vec0, _MM_SHUFFLE(0, 0, 0, 0));
  __m128 vec01 = _mm_shuffle_ps(vec0, vec0, _MM_SHUFFLE(1, 1, 1, 1));
  __m128 vec02 = _mm_shuffle_ps(vec0, vec0, _MM_SHUFFLE(2, 2, 2, 2));
  __m128 vec03 = _mm_shuffle_ps(vec0, vec0, _MM_SHUFFLE(3, 3, 3, 3));

  __m128 res = _mm_mul_ps(row0, vec00);
  res = _mm_add_ps(res, _mm_mul_ps(row1, vec01));
  res = _mm_add_ps(res, _mm_mul_ps(row2, vec02));
  res = _mm_add_ps(res, _mm_mul_ps(row3, vec03));

  _mm_storeu_ps(O, res);
}

#elif defined(__arm__) || defined(__aarch64__)

#include <arm_neon.h>
// Assumes 32bit floats

void Mat4xMat4_SIMD(float *A, float *B, float *O) {

    // Based on the column major ARM example:
    // https://developer.arm.com/documentation/102467/0100/Example---matrix-multiplication
    //
    // NEON intrinsics mapping:
    // https://arm-software.github.io/acle/neon_intrinsics/advsimd.html

    // vfmaq_laneq_f32 -> FMLA
    // Floating-point fused Multiply-Add to accumulator (by element). This
    // instruction multiplies the vector elements in the first source SIMD and
    // FP register by the specified value in the second source SIMD and FP
    // register, and accumulates the results in the vector elements of the
    // destination SIMD and FP register. All the values in this instruction are
    // floating-point values.

    // Contrary to the SSE function above we unroll the loop manually

    // these are the rows of A
    float32x4_t A0 = vld1q_f32(A);
    float32x4_t A1 = vld1q_f32(&A[4]);
    float32x4_t A2 = vld1q_f32(&A[8]);
    float32x4_t A3 = vld1q_f32(&A[12]);

    // these are the rows B
    float32x4_t B0 = vld1q_f32(B);
    float32x4_t B1 = vld1q_f32(&B[4]);
    float32x4_t B2 = vld1q_f32(&B[8]);
    float32x4_t B3 = vld1q_f32(&B[12]);

    // Zero accumulators for output rows
    float32x4_t C0 = vmovq_n_f32(0);
    float32x4_t C1 = vmovq_n_f32(0);
    float32x4_t C2 = vmovq_n_f32(0);
    float32x4_t C3 = vmovq_n_f32(0);

    // Multiply accumulate in 4x1 blocks to output row
    C0 = vfmaq_laneq_f32(C0, B0, A0, 0);
    C0 = vfmaq_laneq_f32(C0, B1, A0, 1);
    C0 = vfmaq_laneq_f32(C0, B2, A0, 2);
    C0 = vfmaq_laneq_f32(C0, B3, A0, 3);
    vst1q_f32(O, C0);

    C1 = vfmaq_laneq_f32(C1, B0, A1, 0);
    C1 = vfmaq_laneq_f32(C1, B1, A1, 1);
    C1 = vfmaq_laneq_f32(C1, B2, A1, 2);
    C1 = vfmaq_laneq_f32(C1, B3, A1, 3);
    vst1q_f32(&O[4], C1);

    C2 = vfmaq_laneq_f32(C2, B0, A2, 0);
    C2 = vfmaq_laneq_f32(C2, B1, A2, 1);
    C2 = vfmaq_laneq_f32(C2, B2, A2, 2);
    C2 = vfmaq_laneq_f32(C2, B3, A2, 3);
    vst1q_f32(&O[8], C2);

    C3 = vfmaq_laneq_f32(C3, B0, A3, 0);
    C3 = vfmaq_laneq_f32(C3, B1, A3, 1);
    C3 = vfmaq_laneq_f32(C3, B2, A3, 2);
    C3 = vfmaq_laneq_f32(C3, B3, A3, 3);
    vst1q_f32(&O[12], C3);

}

void Mat4xVec4_SIMD(float *M, float *V, float *O) {

    // Rows
    float32x4_t M0 = vld1q_f32(M);
    float32x4_t M1 = vld1q_f32(&M[4]);
    float32x4_t M2 = vld1q_f32(&M[8]);
    float32x4_t M3 = vld1q_f32(&M[12]);

    // Col
    float32x4_t VC = vld1q_f32(V);

    // Local output vector
    float32x4_t L0 = vmovq_n_f32(0);

    L0 = vfmaq_laneq_f32(L0, M0, VC, 0);
    L0 = vfmaq_laneq_f32(L0, M1, VC, 1);
    L0 = vfmaq_laneq_f32(L0, M2, VC, 2);
    L0 = vfmaq_laneq_f32(L0, M3, VC, 3);

    vst1q_f32(O, L0);
}

#else

#error "Architecture not supported! Please submit an issue."

#endif