/*
  Copyright (c) 2010-2025, Intel Corporation

  SPDX-License-Identifier: BSD-3-Clause
*/

/** @file xmmintrin.isph
    @brief Principal header file for Streaming SIMD Extensions intrinsics
*/

#define NOT_IMPLEMENTED(x)

#ifndef _INCLUDED_MM2
#define _INCLUDED_MM2

/*
 * the m64 type is required for the integer Streaming SIMD Extensions intrinsics
 */
typedef float<2> __m64;

typedef struct {
    uniform float<4> m128_f32;
} __m128;

#if DEBUG
typedef __m128 __m128_unaligned;
#else
typedef struct {
    uniform float m128_f32[4];
} __m128_unaligned;
#endif

#if 0
typedef union __declspec(intrin_type) __declspec(align(16)) __m128 {
     float               m128_f32[4];
     unsigned __int64    m128_u64[2];
     __int8              m128_i8[16];
     __int16             m128_i16[8];
     __int32             m128_i32[4];
     __int64             m128_i64[2];
     unsigned __int8     m128_u8[16];
     unsigned __int16    m128_u16[8];
     unsigned __int32    m128_u32[4];
 } __m128;
#endif

/*******************************************************/
/* MACRO for shuffle parameter for _mm_shuffle_ps().   */
/* Argument fp3 is a digit[0123] that represents the fp*/
/* from argument "b" of mm_shuffle_ps that will be     */
/* placed in fp3 of result. fp2 is the same for fp2 in */
/* result. fp1 is a digit[0123] that represents the fp */
/* from argument "a" of mm_shuffle_ps that will be     */
/* places in fp1 of result. fp0 is the same for fp0 of */
/* result                                              */
/*******************************************************/
#define _MM_SHUFFLE(fp3, fp2, fp1, fp0) (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | ((fp0)))

/*******************************************************/
/* MACRO for performing the transpose of a 4x4 matrix  */
/* of single precision floating point values.          */
/* Arguments row0, row1, row2, and row3 are __m128     */
/* values whose elements form the corresponding rows   */
/* of a 4x4 matrix.  The matrix transpose is returned  */
/* in arguments row0, row1, row2, and row3 where row0  */
/* now holds column 0 of the original matrix, row1 now */
/* holds column 1 of the original matrix, etc.         */
/*******************************************************/
#define _MM_TRANSPOSE4_PS(row0, row1, row2, row3)                                                                      \
    {                                                                                                                  \
        __m128 _Tmp3, _Tmp2, _Tmp1, _Tmp0;                                                                             \
                                                                                                                       \
        _Tmp0 = _mm_shuffle_ps((row0), (row1), 0x44);                                                                  \
        _Tmp2 = _mm_shuffle_ps((row0), (row1), 0xEE);                                                                  \
        _Tmp1 = _mm_shuffle_ps((row2), (row3), 0x44);                                                                  \
        _Tmp3 = _mm_shuffle_ps((row2), (row3), 0xEE);                                                                  \
                                                                                                                       \
        (row0) = _mm_shuffle_ps(_Tmp0, _Tmp1, 0x88);                                                                   \
        (row1) = _mm_shuffle_ps(_Tmp0, _Tmp1, 0xDD);                                                                   \
        (row2) = _mm_shuffle_ps(_Tmp2, _Tmp3, 0x88);                                                                   \
        (row3) = _mm_shuffle_ps(_Tmp2, _Tmp3, 0xDD);                                                                   \
    }

/* constants for use with _mm_prefetch */
#define _MM_HINT_NTA 0
#define _MM_HINT_T0 1
#define _MM_HINT_T1 2
#define _MM_HINT_T2 3
#define _MM_HINT_ENTA 4
// The values below are not yet supported.
// #define _MM_HINT_ET0    5
// #define _MM_HINT_ET1    6
// #define _MM_HINT_ET2    7

/* (this declspec not supported with 0.A or 0.B) */
#define _MM_ALIGN16 _VCRT_ALIGN(16)

/* MACRO functions for setting and reading the MXCSR */
#define _MM_EXCEPT_MASK 0x003f
#define _MM_EXCEPT_INVALID 0x0001
#define _MM_EXCEPT_DENORM 0x0002
#define _MM_EXCEPT_DIV_ZERO 0x0004
#define _MM_EXCEPT_OVERFLOW 0x0008
#define _MM_EXCEPT_UNDERFLOW 0x0010
#define _MM_EXCEPT_INEXACT 0x0020

#define _MM_MASK_MASK 0x1f80
#define _MM_MASK_INVALID 0x0080
#define _MM_MASK_DENORM 0x0100
#define _MM_MASK_DIV_ZERO 0x0200
#define _MM_MASK_OVERFLOW 0x0400
#define _MM_MASK_UNDERFLOW 0x0800
#define _MM_MASK_INEXACT 0x1000

#define _MM_ROUND_MASK 0x6000
#define _MM_ROUND_NEAREST 0x0000
#define _MM_ROUND_DOWN 0x2000
#define _MM_ROUND_UP 0x4000
#define _MM_ROUND_TOWARD_ZERO 0x6000

#define _MM_FLUSH_ZERO_MASK 0x8000
#define _MM_FLUSH_ZERO_ON 0x8000
#define _MM_FLUSH_ZERO_OFF 0x0000

#define _MM_SET_EXCEPTION_STATE(mask) _mm_setcsr((_mm_getcsr() & ~_MM_EXCEPT_MASK) | (mask))
#define _MM_GET_EXCEPTION_STATE() (_mm_getcsr() & _MM_EXCEPT_MASK)

#define _MM_SET_EXCEPTION_MASK(mask) _mm_setcsr((_mm_getcsr() & ~_MM_MASK_MASK) | (mask))
#define _MM_GET_EXCEPTION_MASK() (_mm_getcsr() & _MM_MASK_MASK)

#define _MM_SET_ROUNDING_MODE(mode) _mm_setcsr((_mm_getcsr() & ~_MM_ROUND_MASK) | (mode))
#define _MM_GET_ROUNDING_MODE() (_mm_getcsr() & _MM_ROUND_MASK)

#define _MM_SET_FLUSH_ZERO_MODE(mode) _mm_setcsr((_mm_getcsr() & ~_MM_FLUSH_ZERO_MASK) | (mode))
#define _MM_GET_FLUSH_ZERO_MODE() (_mm_getcsr() & _MM_FLUSH_ZERO_MASK)

/*****************************************************/
/*     INTRINSICS FUNCTION PROTOTYPES START HERE     */
/*****************************************************/

/*
 * FP, arithmetic
 */

static inline unmasked uniform __m128 _mm_add_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] += _B.m128_f32[0];
    return _A;
}

static inline unmasked uniform __m128 _mm_add_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] + _B.m128_f32[i];
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_sub_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] -= _B.m128_f32[0];
    return _A;
}

static inline unmasked uniform __m128 _mm_sub_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] - _B.m128_f32[i];
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_mul_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] *= _B.m128_f32[0];
    return _A;
}

static inline unmasked uniform __m128 _mm_mul_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] * _B.m128_f32[i];
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_div_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] /= _B.m128_f32[0];
    return _A;
}

static inline unmasked uniform __m128 _mm_div_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] / _B.m128_f32[i];
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_sqrt_ss(uniform __m128 _A) {
    _A.m128_f32[0] = sqrt(_A.m128_f32[0]);
    return _A;
}

static inline unmasked uniform __m128 _mm_sqrt_ps(uniform __m128 _A) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = sqrt(_A.m128_f32[i]);
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_rcp_ss(uniform __m128 _A) {
    _A.m128_f32[0] = rcp_fast(_A.m128_f32[0]);
    return _A;
}

static inline unmasked uniform __m128 _mm_rcp_ps(uniform __m128 _A) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = rcp_fast(_A.m128_f32[i]);
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_rsqrt_ss(uniform __m128 _A) {
    _A.m128_f32[0] = rsqrt_fast(_A.m128_f32[0]);
    return _A;
}

static inline unmasked uniform __m128 _mm_rsqrt_ps(const uniform __m128 _A) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = rsqrt_fast(_A.m128_f32[i]);
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_min_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = min(_A.m128_f32[0], _B.m128_f32[0]);
    return _A;
}

static inline unmasked uniform __m128 _mm_min_ps(const uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = min(_A.m128_f32[i], _B.m128_f32[i]);
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_max_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = max(_A.m128_f32[0], _B.m128_f32[0]);
    return _A;
}

static inline unmasked uniform __m128 _mm_max_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = max(_A.m128_f32[i], _B.m128_f32[i]);
    }
    return Result;
}

/*
 * FP, logical
 */

static inline unmasked uniform __m128 _mm_and_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = floatbits(intbits(_A.m128_f32[i]) & intbits(_B.m128_f32[i]));
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_andnot_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = floatbits((~(intbits(_A.m128_f32[i]))) & intbits(_B.m128_f32[i]));
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_or_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = floatbits(intbits(_A.m128_f32[i]) | intbits(_B.m128_f32[i]));
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_xor_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = floatbits(intbits(_A.m128_f32[i]) ^ intbits(_B.m128_f32[i]));
    }
    return Result;
}

/*
 * FP, comparison
 */

static inline unmasked uniform __m128 _mm_cmpeq_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = _A.m128_f32[0] == _B.m128_f32[0] ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpeq_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] == _B.m128_f32[i] ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmplt_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = _A.m128_f32[0] < _B.m128_f32[0] ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmplt_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] < _B.m128_f32[i] ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmple_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = _A.m128_f32[0] <= _B.m128_f32[0] ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmple_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] <= _B.m128_f32[i] ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpgt_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = _A.m128_f32[0] > _B.m128_f32[0] ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpgt_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] > _B.m128_f32[i] ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpge_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = _A.m128_f32[0] >= _B.m128_f32[0] ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpge_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] >= _B.m128_f32[i] ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpneq_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = _A.m128_f32[0] != _B.m128_f32[0] ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpneq_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = _A.m128_f32[i] != _B.m128_f32[i] ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpnlt_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = !(_A.m128_f32[0] < _B.m128_f32[0]) ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpnlt_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = !(_A.m128_f32[i] < _B.m128_f32[i]) ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpnle_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = !(_A.m128_f32[0] <= _B.m128_f32[0]) ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpnle_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = !(_A.m128_f32[i] <= _B.m128_f32[i]) ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpngt_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = !(_A.m128_f32[0] > _B.m128_f32[0]) ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpngt_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = !(_A.m128_f32[i] > _B.m128_f32[i]) ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpnge_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = !(_A.m128_f32[0] >= _B.m128_f32[0]) ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpnge_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = !(_A.m128_f32[i] >= _B.m128_f32[i]) ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpord_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0])) ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpord_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        Result.m128_f32[i] = (!isnan(_A.m128_f32[i]) && !isnan(_B.m128_f32[i])) ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

static inline unmasked uniform __m128 _mm_cmpunord_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = (isnan(_A.m128_f32[0]) || isnan(_B.m128_f32[0])) ? floatbits(0xFFFFFFFF) : 0.0f;
    return _A;
}

static inline unmasked uniform __m128 _mm_cmpunord_ps(uniform __m128 _A, uniform __m128 _B) {
    uniform __m128 Result;
    foreach (i = 0 ... 4) {
        bool isnanA = isnan(_A.m128_f32[i]);
        bool isnanB = isnan(_B.m128_f32[i]);
        Result.m128_f32[i] = (isnanA || isnanB) ? floatbits(0xFFFFFFFF) : 0.0f;
    }
    return Result;
}

// TODO: Check QNaN on non u versions
static inline unmasked uniform int _mm_comieq_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] == _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_comilt_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] < _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_comile_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] <= _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_comigt_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] > _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_comige_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] >= _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_comineq_ss(uniform __m128 _A, uniform __m128 _B) {
    return (isnan(_A.m128_f32[0]) || isnan(_B.m128_f32[0]) || (_A.m128_f32[0] != _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_ucomieq_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] == _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_ucomilt_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] < _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_ucomile_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] <= _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_ucomigt_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] > _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_ucomige_ss(uniform __m128 _A, uniform __m128 _B) {
    return (!isnan(_A.m128_f32[0]) && !isnan(_B.m128_f32[0]) && (_A.m128_f32[0] >= _B.m128_f32[0])) ? 1 : 0;
}

static inline unmasked uniform int _mm_ucomineq_ss(uniform __m128 _A, uniform __m128 _B) {
    return (isnan(_A.m128_f32[0]) || isnan(_B.m128_f32[0]) || (_A.m128_f32[0] != _B.m128_f32[0])) ? 1 : 0;
}

/*
 * FP, conversions
 */

static inline unmasked uniform int _mm_cvt_ss2si(uniform __m128 _A) { return (uniform int)(round(_A.m128_f32[0])); }

static inline unmasked uniform int _mm_cvtt_ss2si(uniform __m128 _A) { return (uniform int)_A.m128_f32[0]; }

static inline unmasked uniform __m128 _mm_cvt_si2ss(uniform __m128 _A, uniform int _B) {
    _A.m128_f32[0] = (uniform float)_B;
    return _A;
}

static inline unmasked uniform float _mm_cvtss_f32(uniform __m128 _A) { return _A.m128_f32[0]; }

#if defined(_M_IX86)
/*
 * Support for MMX extension intrinsics
 */
NOT_IMPLEMENTED(extern __m64 _mm_cvt_ps2pi(__m128 _A));
NOT_IMPLEMENTED(extern __m64 _mm_cvtt_ps2pi(__m128 _A));
NOT_IMPLEMENTED(extern __m128 _mm_cvt_pi2ps(__m128, __m64));
#endif

#if defined(_M_X64)
/*
 * Support for 64-bit intrinsics
 */
NOT_IMPLEMENTED(extern __int64 _mm_cvtss_si64(__m128 _A));
NOT_IMPLEMENTED(extern __int64 _mm_cvttss_si64(__m128 _A));
NOT_IMPLEMENTED(extern __m128 _mm_cvtsi64_ss(__m128 _A, __int64 _B));
#endif /* defined (_M_X64) */

/*
 * FP, misc
 */

static inline unmasked uniform __m128 _mm_shuffle_ps(uniform __m128 _A, uniform __m128 _B, uniform unsigned int _Imm8) {
    uniform float<4> _R;
    _R[0] = _A.m128_f32[_Imm8 & 0x3];
    _R[1] = _A.m128_f32[(_Imm8 >> 2) & 0x3];
    _R[2] = _B.m128_f32[(_Imm8 >> 4) & 0x3];
    _R[3] = _B.m128_f32[(_Imm8 >> 6) & 0x3];
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_unpackhi_ps(uniform __m128 _A, uniform __m128 _B) {
    const uniform float<4> _R = {_A.m128_f32[2], _B.m128_f32[2], _A.m128_f32[3], _B.m128_f32[3]};
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_unpacklo_ps(uniform __m128 _A, uniform __m128 _B) {
    const uniform float<4> _R = {_A.m128_f32[0], _B.m128_f32[0], _A.m128_f32[1], _B.m128_f32[1]};
    return *((uniform __m128 * uniform) & _R);
}

NOT_IMPLEMENTED(extern __m128 _mm_loadh_pi(__m128, __m64 const *));

static inline unmasked uniform __m128 _mm_movehl_ps(uniform __m128 _A, uniform __m128 _B) {
    const uniform float<4> _R = {_B.m128_f32[2], _B.m128_f32[3], _A.m128_f32[2], _A.m128_f32[3]};
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_movelh_ps(uniform __m128 _A, uniform __m128 _B) {
    const uniform float<4> _R = {_A.m128_f32[0], _A.m128_f32[1], _B.m128_f32[0], _B.m128_f32[1]};
    return *((uniform __m128 * uniform) & _R);
}

NOT_IMPLEMENTED(extern void _mm_storeh_pi(__m64 *, __m128));
NOT_IMPLEMENTED(extern __m128 _mm_loadl_pi(__m128, __m64 const *));
NOT_IMPLEMENTED(extern void _mm_storel_pi(__m64 *, __m128));

static inline unmasked uniform int _mm_movemask_ps(uniform __m128 _A) {
    varying float S0 = 0;
    *((uniform float<4> *uniform) & S0) = *((uniform float<4> *uniform) & _A);
    return packmask(signbits(S0));
}

#if defined(_M_IX86)
/*
 * Integer (MMX) extensions
 */
NOT_IMPLEMENTED(extern int _m_pextrw(__m64, int));
NOT_IMPLEMENTED(extern __m64 _m_pinsrw(__m64, int, int));
NOT_IMPLEMENTED(extern __m64 _m_pmaxsw(__m64, __m64));
NOT_IMPLEMENTED(extern __m64 _m_pmaxub(__m64, __m64));
NOT_IMPLEMENTED(extern __m64 _m_pminsw(__m64, __m64));
NOT_IMPLEMENTED(extern __m64 _m_pminub(__m64, __m64));
NOT_IMPLEMENTED(extern int _m_pmovmskb(__m64));
NOT_IMPLEMENTED(extern __m64 _m_pmulhuw(__m64, __m64));
NOT_IMPLEMENTED(extern __m64 _m_pshufw(__m64, int));
NOT_IMPLEMENTED(extern void _m_maskmovq(__m64, __m64, char *));
NOT_IMPLEMENTED(extern __m64 _m_pavgb(__m64, __m64));
NOT_IMPLEMENTED(extern __m64 _m_pavgw(__m64, __m64));
NOT_IMPLEMENTED(extern __m64 _m_psadbw(__m64, __m64));
#endif

/*
 * memory & initialization
 */

static inline unmasked uniform __m128 _mm_set_ss(uniform float _A) {
    const uniform float<4> _R = {_A, 0, 0, 0};
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_set_ps1(uniform float _A) {
    const uniform float<4> _R = {_A, _A, _A, _A};
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_set_ps(uniform float _A, uniform float _B, uniform float _C,
                                                 uniform float _D) {
    const uniform float<4> _R = {_D, _C, _B, _A};
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_setr_ps(uniform float _A, uniform float _B, uniform float _C,
                                                  uniform float _D) {
    const uniform float<4> _R = {_A, _B, _C, _D};
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_setzero_ps() {
    const uniform float<4> _R = {0, 0, 0, 0};
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_load_ss(uniform float const *uniform _A) {
    uniform __m128 _R;
    _R.m128_f32[0] = _A[0];
    return _R;
}

static inline unmasked uniform __m128 _mm_load_ps1(uniform float const *uniform _A) { return _mm_set_ps1(_A[0]); }

static inline unmasked uniform __m128 _mm_load_ps(uniform float const *uniform _A) {
    const uniform float<4> _R = *((uniform float<4> *uniform) & _A[0]);
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_loadr_ps(uniform float const *uniform _A) {
    const uniform float<4> _S0 = *((uniform float<4> *uniform) & _A[0]);
    const uniform float<4> _R = {_S0[3], _S0[2], _S0[1], _S0[0]};
    return *((uniform __m128 * uniform) & _R);
}

static inline unmasked uniform __m128 _mm_loadu_ps(uniform float const *uniform _A) {
    const uniform __m128_unaligned R = *((uniform __m128_unaligned * uniform) & _A[0]);
    return *((uniform __m128 * uniform) & R);
}

static inline unmasked void _mm_store_ss(uniform float *uniform _V, uniform __m128 _A) { _V[0] = _A.m128_f32[0]; }

static inline unmasked void _mm_store_ps1(uniform float *uniform _V, uniform __m128 _A) {
    const uniform float<4> _R = {_A.m128_f32[0], _A.m128_f32[0], _A.m128_f32[0], _A.m128_f32[0]};
    *((uniform float<4> *uniform) & _V[0]) = *((uniform float<4> *uniform) & _R[0]);
}

static inline unmasked void _mm_store_ps(uniform float *uniform _V, uniform __m128 _A) {
    *((uniform float<4> *uniform) & _V[0]) = *((uniform float<4> *uniform) & _A.m128_f32[0]);
}

static inline unmasked void _mm_storer_ps(uniform float *uniform _V, uniform __m128 _A) {
    const uniform float<4> _R = {_A.m128_f32[3], _A.m128_f32[2], _A.m128_f32[1], _A.m128_f32[0]};
    *((uniform float<4> *uniform) & _V[0]) = *((uniform float<4> *uniform) & _R[0]);
}

static inline unmasked void _mm_storeu_ps(uniform float *uniform _V, const uniform __m128 _A) {
    *((uniform __m128_unaligned * uniform) & _V[0]) = *((uniform __m128_unaligned * uniform) & _A.m128_f32[0]);
}

static inline unmasked void _mm_prefetch(uniform int8 const *uniform _A, uniform int _Sel) {
    switch (_Sel) {
    case _MM_HINT_NTA:
        prefetch_nt((void *uniform)_A);
        break;
    case _MM_HINT_T0:
        prefetch_l1((void *uniform)_A);
        break;
    case _MM_HINT_T1:
        prefetch_l2((void *uniform)_A);
        break;
    case _MM_HINT_T2:
        prefetch_l3((void *uniform)_A);
        break;
    }
}

#if defined(_M_IX86)
NOT_IMPLEMENTED(extern void _mm_stream_pi(__m64 *, __m64));
#endif

static inline unmasked void _mm_stream_ps(uniform float *uniform _V, uniform __m128 _A) {
#if TARGET_WIDTH == 4
    varying float S0;
    *((uniform float<4> *uniform) & S0) = *((uniform float<4> *uniform) & _A);
    streaming_store(_V, S0);
#else
    // Can't guarantee we won't store using wider regs so need to do uniform streaming stores
    streaming_store(&_V[0], _A.m128_f32[0]);
    streaming_store(&_V[1], _A.m128_f32[1]);
    streaming_store(&_V[2], _A.m128_f32[2]);
    streaming_store(&_V[3], _A.m128_f32[3]);
#endif
}

static inline unmasked uniform __m128 _mm_move_ss(uniform __m128 _A, uniform __m128 _B) {
    _A.m128_f32[0] = _B.m128_f32[0];
    return _A;
}

NOT_IMPLEMENTED(extern void _mm_sfence(void));
NOT_IMPLEMENTED(extern unsigned int _mm_getcsr(void));
NOT_IMPLEMENTED(extern void _mm_setcsr(unsigned int));

#ifdef __ICL
NOT_IMPLEMENTED(extern void *__cdecl _mm_malloc(size_t _Siz, size_t _Al));
NOT_IMPLEMENTED(extern void __cdecl _mm_free(void *_P));
#endif /* __ICL */

/* Alternate intrinsic names definition */
#if defined(_M_IX86)
#define _mm_cvtps_pi32 _mm_cvt_ps2pi
#define _mm_cvttps_pi32 _mm_cvtt_ps2pi
#define _mm_cvtpi32_ps _mm_cvt_pi2ps
#define _mm_extract_pi16 _m_pextrw
#define _mm_insert_pi16 _m_pinsrw
#define _mm_max_pi16 _m_pmaxsw
#define _mm_max_pu8 _m_pmaxub
#define _mm_min_pi16 _m_pminsw
#define _mm_min_pu8 _m_pminub
#define _mm_movemask_pi8 _m_pmovmskb
#define _mm_mulhi_pu16 _m_pmulhuw
#define _mm_shuffle_pi16 _m_pshufw
#define _mm_maskmove_si64 _m_maskmovq
#define _mm_avg_pu8 _m_pavgb
#define _mm_avg_pu16 _m_pavgw
#define _mm_sad_pu8 _m_psadbw
#endif
#define _mm_cvtss_si32 _mm_cvt_ss2si
#define _mm_cvttss_si32 _mm_cvtt_ss2si
#define _mm_cvtsi32_ss _mm_cvt_si2ss
#define _mm_set1_ps _mm_set_ps1
#define _mm_load1_ps _mm_load_ps1
#define _mm_store1_ps _mm_store_ps1

/******************************************************/
/* UTILITY INTRINSICS FUNCTION DEFINITIONS START HERE */
/******************************************************/

#if defined(_M_IX86)
/*********************************************************/
/*  NAME : _mm_cvtpi16_ps                                */
/*  DESCRIPTION : Convert 4 16-bit signed integer values */
/*                to 4 single-precision float values     */
/*  IN : __m64 _A                                         */
/*  OUT : none                                           */
/*  RETURN : __m128 : (float)_A                           */
/*********************************************************/
__inline __m128 _mm_cvtpi16_ps(__m64 _A) {
    __m128 _Tmp;
    __m64 _Ext_val = _mm_cmpgt_pi16(_mm_setzero_si64(), _A);

    _Tmp = _mm_cvtpi32_ps(_mm_setzero_ps(), _mm_unpackhi_pi16(_A, _Ext_val));
    return (_mm_cvtpi32_ps(_mm_movelh_ps(_Tmp, _Tmp), _mm_unpacklo_pi16(_A, _Ext_val)));
}

/***********************************************************/
/*  NAME : _mm_cvtpu16_ps                                  */
/*  DESCRIPTION : Convert 4 16-bit unsigned integer values */
/*                to 4 single-precision float values       */
/*  IN : __m64 _A                                           */
/*  OUT : none                                             */
/*  RETURN : __m128 : (float)_A                             */
/***********************************************************/
__inline __m128 _mm_cvtpu16_ps(__m64 _A) {
    __m128 _Tmp;
    __m64 _Ext_val = _mm_setzero_si64();

    _Tmp = _mm_cvtpi32_ps(_mm_setzero_ps(), _mm_unpackhi_pi16(_A, _Ext_val));
    return (_mm_cvtpi32_ps(_mm_movelh_ps(_Tmp, _Tmp), _mm_unpacklo_pi16(_A, _Ext_val)));
}

/******************************************************/
/*  NAME : _mm_cvtps_pi16                             */
/*  DESCRIPTION : Convert 4 single-precision float    */
/*                values to 4 16-bit integer values   */
/*  IN : __m128 a                                     */
/*  OUT : none                                        */
/*  RETURN : __m64 : (short)a                         */
/******************************************************/
__inline __m64 _mm_cvtps_pi16(__m128 _A) {
    return _mm_packs_pi32(_mm_cvtps_pi32(_A), _mm_cvtps_pi32(_mm_movehl_ps(_A, _A)));
}

/******************************************************/
/*  NAME : _mm_cvtpi8_ps                              */
/*  DESCRIPTION : Convert 4 8-bit integer values to 4 */
/*                single-precision float values       */
/*  IN : __m64 _A                                     */
/*  OUT : none                                        */
/*  RETURN : __m128 : (float)_A                        */
/******************************************************/
__inline __m128 _mm_cvtpi8_ps(__m64 _A) {
    __m64 _Ext_val = _mm_cmpgt_pi8(_mm_setzero_si64(), _A);

    return _mm_cvtpi16_ps(_mm_unpacklo_pi8(_A, _Ext_val));
}

/******************************************************/
/*  NAME : _mm_cvtpu8_ps                              */
/*  DESCRIPTION : Convert 4 8-bit unsigned integer    */
/*                values to 4 single-precision float  */
/*                values                              */
/*  IN : __m64 _A                                      */
/*  OUT : none                                        */
/*  RETURN : __m128 : (float)_A                        */
/******************************************************/
__inline __m128 _mm_cvtpu8_ps(__m64 _A) { return _mm_cvtpu16_ps(_mm_unpacklo_pi8(_A, _mm_setzero_si64())); }

/******************************************************/
/*  NAME : _mm_cvtps_pi8                              */
/*  DESCRIPTION : Convert 4 single-precision float    */
/*                values to 4 8-bit integer values    */
/*  IN : __m128 _A                                     */
/*  OUT : none                                        */
/*  RETURN : __m64 : (char)_A                          */
/******************************************************/
__inline __m64 _mm_cvtps_pi8(__m128 _A) { return _mm_packs_pi16(_mm_cvtps_pi16(_A), _mm_setzero_si64()); }

/******************************************************/
/*  NAME : _mm_cvtpi32x2_ps                           */
/*  DESCRIPTION : Convert 4 32-bit integer values     */
/*                to 4 single-precision float values  */
/*  IN : __m64 _A : operand 1                          */
/*       __m64 _B : operand 2                          */
/*  OUT : none                                        */
/*  RETURN : __m128 : (float)_A,(float)_B               */
/******************************************************/
__inline __m128 _mm_cvtpi32x2_ps(__m64 _A, __m64 _B) {
    return _mm_movelh_ps(_mm_cvt_pi2ps(_mm_setzero_ps(), _A), _mm_cvt_pi2ps(_mm_setzero_ps(), _B));
}
#endif
#endif /* _INCLUDED_MM2 */
