Files
watrbx-game-engine/Base/include/simd/simd_neon.inl
T
2025-09-18 17:55:52 -04:00

1546 lines
38 KiB
C++

#pragma once
// Do not include this file directly. Include simd/simd.h
#include "simd/simd_types.h"
#include <arm_neon.h>
namespace RBX
{
namespace simd
{
//
// Vector Loading
//
namespace details
{
RBX_SIMD_INLINE v4f::pod_t load( const float* s )
{
return vld1q_f32( s );
}
RBX_SIMD_INLINE v4i::pod_t load( const int32_t* s )
{
return vld1q_s32( s );
}
RBX_SIMD_INLINE v4u::pod_t load( const uint32_t* s )
{
return vld1q_u32( s );
}
template< int Lane >
RBX_SIMD_INLINE v4f::pod_t loadLane( const float* s, v4f::pod_t vec )
{
return vld1q_lane_f32( s, vec, Lane );
}
template< int Lane >
RBX_SIMD_INLINE v4u::pod_t loadLane( const uint32_t* s, v4u::pod_t vec )
{
return vld1q_lane_u32( s, vec, Lane );
}
template< int Lane >
RBX_SIMD_INLINE v4i::pod_t loadLane( const int32_t* s, v4i::pod_t vec )
{
return vld1q_lane_s32( s, vec, Lane );
}
RBX_SIMD_INLINE v4f::pod_t loadSplat( const float* s )
{
return vld1q_dup_f32( s );
}
RBX_SIMD_INLINE v4u::pod_t loadSplat( const uint32_t* s )
{
return vld1q_dup_u32( s );
}
RBX_SIMD_INLINE v4i::pod_t loadSplat( const int32_t* s )
{
return vld1q_dup_s32( s );
}
RBX_SIMD_INLINE float32x2_t loadD( const float* s )
{
return vld1_f32( s );
}
RBX_SIMD_INLINE int32x2_t loadD( const int32_t* s )
{
return vld1_s32( s );
}
RBX_SIMD_INLINE uint32x2_t loadD( const uint32_t* s )
{
return vld1_u32( s );
}
RBX_SIMD_INLINE float32x2_t loadSplatD( const float* s )
{
return vld1_dup_f32( s );
}
RBX_SIMD_INLINE int32x2_t loadSplatD( const int32_t* s )
{
return vld1_dup_s32( s );
}
RBX_SIMD_INLINE uint32x2_t loadSplatD( const uint32_t* s )
{
return vld1_dup_u32( s );
}
RBX_SIMD_INLINE float32x4_t combine( float32x2_t a, float32x2_t b )
{
return vcombine_f32( a, b );
}
RBX_SIMD_INLINE int32x4_t combine( int32x2_t a, int32x2_t b )
{
return vcombine_s32( a, b );
}
RBX_SIMD_INLINE uint32x4_t combine( uint32x2_t a, uint32x2_t b )
{
return vcombine_u32( a, b );
}
template< class Type >
RBX_SIMD_INLINE typename Type::pod_t zero();
template< >
RBX_SIMD_INLINE v4f::pod_t zero< v4f >()
{
float32x2_t z = vcreate_f32( 0 );
return vcombine_f32( z, z );
}
template< >
RBX_SIMD_INLINE v4i::pod_t zero< v4i >()
{
int32x2_t z = vcreate_s32( 0 );
return vcombine_s32( z, z );
}
template< >
RBX_SIMD_INLINE v4u::pod_t zero< v4u >()
{
uint32x2_t z = vcreate_u32( 0 );
return vcombine_u32( z, z );
}
template< class ScalarType >
struct v2;
template< >
struct v2< float >
{
typedef float32x2_t pod_t;
pod_t v;
};
template< >
struct v2< int32_t >
{
typedef int32x2_t pod_t;
pod_t v;
};
template< >
struct v2< uint32_t >
{
typedef uint32x2_t pod_t;
pod_t v;
};
template< class ScalarType >
struct v2x2;
template< >
struct v2x2< float >
{
typedef float32x2x2_t pod_t;
pod_t v;
};
template< >
struct v2x2< int32_t >
{
typedef int32x2x2_t pod_t;
pod_t v;
};
template< >
struct v2x2< uint32_t >
{
typedef uint32x2x2_t pod_t;
pod_t v;
};
}
template< class ScalarType >
RBX_SIMD_INLINE v4< ScalarType > load( const ScalarType* s )
{
RBX_SIMD_ALIGN_ASSERT( s, 16 );
v4< ScalarType > r;
r.v = details::load( s );
return r;
}
template< class ScalarType >
RBX_SIMD_INLINE v4< ScalarType > loadUnaligned( const ScalarType* s )
{
v4< ScalarType > r;
r.v = details::load( s );
return r;
}
template< class ScalarType >
RBX_SIMD_INLINE v4< ScalarType > loadSingle( const ScalarType* s )
{
v4< ScalarType > r;
r.v = details::loadLane< 0 >( s, details::zero< v4< ScalarType > >() );
return r;
}
template< class ScalarType >
RBX_SIMD_INLINE v4< ScalarType > loadSplat( const ScalarType* s )
{
v4< ScalarType > r;
r.v = details::loadSplat( s );
return r;
}
RBX_SIMD_INLINE v4f load3( const float* value )
{
float32x2_t xy = details::loadD(value);
float32x2_t z = details::loadSplatD((value + 2));
v4f r;
r.v = details::combine(xy, z);
return r;
}
//
// Storing
//
namespace details
{
RBX_SIMD_INLINE void store( float* s, float32x4_t v )
{
vst1q_f32( s, v );
}
RBX_SIMD_INLINE void store( int32_t* s, int32x4_t v )
{
vst1q_s32( s, v );
}
RBX_SIMD_INLINE void store( uint32_t* s, uint32x4_t v )
{
vst1q_u32( s, v );
}
template< int lane >
RBX_SIMD_INLINE void storeLane( float* s, float32x4_t v )
{
vst1q_lane_f32( s, v, lane );
}
template< int lane >
RBX_SIMD_INLINE void storeLane( int32_t* s, int32x4_t v )
{
vst1q_lane_s32( s, v, lane );
}
template< int lane >
RBX_SIMD_INLINE void storeLane( uint32_t* s, uint32x4_t v )
{
vst1q_lane_u32( s, v, lane );
}
}
template< class ScalarType >
RBX_SIMD_INLINE void store( ScalarType* dst, const v4< ScalarType >& v )
{
RBX_SIMD_ALIGN_ASSERT( dst, 16 );
details::store( dst, v.v );
}
template< class ScalarType >
RBX_SIMD_INLINE void storeUnaligned( ScalarType* dst, const v4< ScalarType >& v )
{
details::store( dst, v.v );
}
template< class ScalarType >
RBX_SIMD_INLINE void storeSingle( ScalarType* dst, const v4< ScalarType >& v )
{
details::storeLane< 0 >( dst, v.v );
}
//
// Vector Forming
//
RBX_SIMD_INLINE v4f zerof()
{
v4f r;
r.v = details::zero< v4f >();
return r;
}
RBX_SIMD_INLINE v4i zeroi()
{
v4i r;
r.v = details::zero< v4i >();
return r;
}
RBX_SIMD_INLINE v4u zerou()
{
v4u r;
r.v = details::zero< v4u >();
return r;
}
namespace details
{
RBX_SIMD_INLINE float32x4_t splat( float s )
{
return vdupq_n_f32( s );
}
RBX_SIMD_INLINE int32x4_t splat( int32_t s )
{
return vdupq_n_s32( s );
}
RBX_SIMD_INLINE uint32x4_t splat( uint32_t s )
{
return vdupq_n_u32( s );
}
}
template< class ScalarType >
RBX_SIMD_INLINE v4< ScalarType > splat( ScalarType s )
{
v4< ScalarType > r;
r.v = details::splat( s );
return r;
}
namespace details
{
RBX_SIMD_INLINE float32x2_t splatD( float s )
{
return vmov_n_f32( (s) );
}
RBX_SIMD_INLINE int32x2_t splatD( int32_t s )
{
return vmov_n_s32( (s) );
}
RBX_SIMD_INLINE uint32x2_t splatD( uint32_t s )
{
return vmov_n_u32( (s) );
}
RBX_SIMD_INLINE float32x2_t zip1( float32x2_t a, float32x2_t b )
{
return vzip_f32( a, b ).val[ 0 ];
}
RBX_SIMD_INLINE int32x2_t zip1( int32x2_t a, int32x2_t b )
{
return vzip_s32( a, b ).val[ 0 ];
}
RBX_SIMD_INLINE uint32x2_t zip1( uint32x2_t a, uint32x2_t b )
{
return vzip_u32( a, b ).val[ 0 ];
}
}
template< class ScalarType >
RBX_SIMD_INLINE v4< ScalarType > form( ScalarType x, ScalarType y, ScalarType z, ScalarType w )
{
typename details::v2< ScalarType >::pod_t vx = details::splatD( x );
typename details::v2< ScalarType >::pod_t vy = details::splatD( y );
typename details::v2< ScalarType >::pod_t vxy = details::zip1( vx, vy );
typename details::v2< ScalarType >::pod_t vz = details::splatD( z );
typename details::v2< ScalarType >::pod_t vw = details::splatD( w );
typename details::v2< ScalarType >::pod_t vzw = details::zip1( vz, vw );
v4< ScalarType > r;
r.v = details::combine( vxy, vzw );
return r;
}
template< class ScalarType >
RBX_SIMD_INLINE v4< ScalarType > form( ScalarType x, ScalarType y, ScalarType z )
{
typename details::v2< ScalarType >::pod_t vx = details::splatD( x );
typename details::v2< ScalarType >::pod_t vy = details::splatD( y );
typename details::v2< ScalarType >::pod_t vxy = details::zip1( vx, vy );
typename details::v2< ScalarType >::pod_t vz = details::splatD( z );
v4< ScalarType > r;
r.v = details::combine( vxy, vz );
return r;
}
template< class ScalarType >
RBX_SIMD_INLINE v4< ScalarType > form( ScalarType x, ScalarType y )
{
typename details::v2< ScalarType >::pod_t vx = details::splatD( x );
typename details::v2< ScalarType >::pod_t vy = details::splatD( y );
typename details::v2< ScalarType >::pod_t vxy = details::zip1( vx, vy );
v4< ScalarType > r;
r.v = details::combine( vxy, vxy );
return r;
}
//
// Insert / extract
//
template< class VectorType >
RBX_SIMD_INLINE typename VectorType::elem_t extractSlow( const VectorType& _v, uint32_t _i )
{
union
{
typename VectorType::pod_t v;
typename VectorType::elem_t el[ 4 ];
};
v = _v;
return el[ _i ];
}
//
// Casts: cast one type to the other preserving the bit representation
//
RBX_SIMD_INLINE v4u reinterpretAsUInt( v4fArg v )
{
v4u r;
r.v = vreinterpretq_u32_f32( (v.v) );
return r;
}
RBX_SIMD_INLINE v4i reinterpretAsInt( v4fArg v )
{
v4i r;
r.v = vreinterpretq_s32_f32( (v.v) );
return r;
}
RBX_SIMD_INLINE v4f reinterpretAsFloat( v4uArg v )
{
v4f r;
r.v = vreinterpretq_f32_u32( (v.v) );
return r;
}
RBX_SIMD_INLINE v4f reinterpretAsFloat( v4iArg v )
{
v4f r;
r.v = vreinterpretq_f32_s32( (v.v) );
return r;
}
RBX_SIMD_INLINE v4u reinterpretAsUInt( v4iArg v )
{
v4u r;
r.v = vreinterpretq_u32_s32( (v.v) );
return r;
}
RBX_SIMD_INLINE v4i reinterpretAsInt( v4uArg v )
{
v4i r;
r.v = vreinterpretq_s32_u32( (v.v) );
return r;
}
//
// Conversions: convert float to int and int to float
//
// Convert using rounding to closest
// vcvtq_s32_f32 uses truncate to zero so we need to do more...
RBX_SIMD_INLINE v4i convertFloat2IntNearest( v4fArg v )
{
uint32x4_t sign = vcgeq_f32( v.v, vdupq_n_f32( 0.0f ) );
int32x4_t t = vcvtq_s32_f32( vabsq_f32( v.v ) + vdupq_n_f32( 0.5f ) );
v4i r;
r.v = vbslq_s32( sign, t, -t );
return r;
}
RBX_SIMD_INLINE v4i convertFloat2IntTruncate( v4fArg v )
{
v4i r;
r.v = vcvtq_s32_f32( v.v );
return r;
}
RBX_SIMD_INLINE v4f convertIntToFloat( v4iArg v )
{
v4f r;
r.v = vcvtq_f32_s32( v.v );
return r;
}
//
// Splat
//
namespace details
{
RBX_SIMD_INLINE float32x2_t low( float32x4_t t )
{
return vget_low_f32( t );
}
RBX_SIMD_INLINE int32x2_t low( int32x4_t t )
{
return vget_low_s32( t );
}
RBX_SIMD_INLINE uint32x2_t low( uint32x4_t t )
{
return vget_low_u32( t );
}
RBX_SIMD_INLINE float32x2_t high( float32x4_t t )
{
return vget_high_f32( t );
}
RBX_SIMD_INLINE int32x2_t high( int32x4_t t )
{
return vget_high_s32( t );
}
RBX_SIMD_INLINE uint32x2_t high( uint32x4_t t )
{
return vget_high_u32( t );
}
template< int lane >
RBX_SIMD_INLINE float32x4_t splat( float32x2_t a )
{
return vdupq_lane_f32( a, lane );
}
template< int lane >
RBX_SIMD_INLINE int32x4_t splat( int32x2_t a )
{
return vdupq_lane_s32( a, lane );
}
template< int lane >
RBX_SIMD_INLINE uint32x4_t splat( uint32x2_t a )
{
return vdupq_lane_u32( a, lane );
}
template< int lane >
RBX_SIMD_INLINE float32x2_t splatD( float32x2_t a )
{
return vdup_lane_f32( a, lane );
}
template< int lane >
RBX_SIMD_INLINE int32x2_t splatD( int32x2_t a )
{
return vdup_lane_s32( a, lane );
}
template< int lane >
RBX_SIMD_INLINE uint32x2_t splatD( uint32x2_t a )
{
return vdup_lane_u32( a, lane );
}
template< class VectorType, unsigned i >
struct SplatHelper;
template< class VectorType >
struct SplatHelper< VectorType, 0 >
{
static RBX_SIMD_INLINE VectorType splat( const VectorType& v )
{
typename v2< typename VectorType::elem_t >::pod_t h = low( v.v );
VectorType r;
r.v = details::splat< 0 >( h );
return r;
}
static RBX_SIMD_INLINE v2< typename VectorType::elem_t > splatD( const VectorType& v )
{
v2< typename VectorType::elem_t > r;
r.v = details::splatD< 0 >( low( v.v ) );
return r;
}
};
template< class VectorType >
struct SplatHelper< VectorType, 1 >
{
static RBX_SIMD_INLINE VectorType splat( const VectorType& v )
{
typename v2< typename VectorType::elem_t >::pod_t h = low( v.v );
VectorType r;
r.v = details::splat< 1 >( h );
return r;
}
static RBX_SIMD_INLINE v2< typename VectorType::elem_t > splatD( const VectorType& v )
{
v2< typename VectorType::elem_t > r;
r.v = splatD< 1 >( low( v.v ) );
return r;
}
};
template< class VectorType >
struct SplatHelper< VectorType, 2 >
{
static RBX_SIMD_INLINE VectorType splat( const VectorType& v )
{
typename v2< typename VectorType::elem_t >::pod_t h = high( v.v );
VectorType r;
r.v = details::splat< 0 >( h );
return r;
}
static RBX_SIMD_INLINE v2< typename VectorType::elem_t > splatD( const VectorType& v )
{
v2< typename VectorType::elem_t > r;
r.v = splatD< 0 >( high( v.v ) );
return r;
}
};
template< class VectorType >
struct SplatHelper< VectorType, 3 >
{
static RBX_SIMD_INLINE VectorType splat( const VectorType& v )
{
typename v2< typename VectorType::elem_t >::pod_t h = high( v.v );
VectorType r;
r.v = details::splat< 1 >( h );
return r;
}
static RBX_SIMD_INLINE v2< typename VectorType::elem_t > splatD( const VectorType& v )
{
v2< typename VectorType::elem_t > r;
r.v = splatD< 1 >( high( v.v ) );
return r;
}
};
}
template< unsigned i, class VectorType >
RBX_SIMD_INLINE VectorType splat( const VectorType& v )
{
return details::SplatHelper< VectorType, i >::splat( v );
}
//
// Selects
//
namespace details
{
RBX_SIMD_INLINE float32x2_t selectD( float32x2_t u, float32x2_t v, uint32x2_t s )
{
return vbsl_f32( s, v, u );
}
RBX_SIMD_INLINE int32x2_t selectD( int32x2_t u, int32x2_t v, uint32x2_t s )
{
return vbsl_s32( s, v, u );
}
RBX_SIMD_INLINE uint32x2_t selectD( uint32x2_t u, uint32x2_t v, uint32x2_t s )
{
return vbsl_u32( s, v, u );
}
RBX_SIMD_INLINE uint8x8_t castToUInt8( float32x2_t v )
{
return vreinterpret_u8_f32( v );
}
RBX_SIMD_INLINE uint8x8_t castToUInt8( uint32x2_t v )
{
return vreinterpret_u8_u32( v );
}
RBX_SIMD_INLINE uint8x8_t castToUInt8( int32x2_t v )
{
return vreinterpret_u8_s32( v );
}
template< class From >
RBX_SIMD_INLINE uint8x8x2_t castToUInt8x2( From v )
{
uint8x8x2_t r;
r.val[ 0 ] = details::castToUInt8( details::low( v ) );
r.val[ 1 ] = details::castToUInt8( details::high( v ) );
return r;
}
template< class From >
RBX_SIMD_INLINE uint8x8x4_t castToUInt8x4( From u, From v )
{
uint8x8x4_t r;
r.val[ 0 ] = details::castToUInt8( details::low( u ) );
r.val[ 1 ] = details::castToUInt8( details::high( u ) );
r.val[ 2 ] = details::castToUInt8( details::low( v ) );
r.val[ 3 ] = details::castToUInt8( details::high( v ) );
return r;
}
template< class To >
struct CastFromUInt8;
template<>
struct CastFromUInt8< float32x4_t >
{
static RBX_SIMD_INLINE float32x4_t cast( uint8x8x2_t v )
{
float32x2_t r0 = vreinterpret_f32_u8( v.val[0] );
float32x2_t r1 = vreinterpret_f32_u8( v.val[1] );
return details::combine(r0, r1);
}
static RBX_SIMD_INLINE float32x2_t cast( uint8x8_t v )
{
return vreinterpret_f32_u8( v );
}
};
template<>
struct CastFromUInt8< uint32x4_t >
{
static RBX_SIMD_INLINE uint32x4_t cast( uint8x8x2_t v )
{
uint32x2_t r0 = vreinterpret_u32_u8( v.val[0] );
uint32x2_t r1 = vreinterpret_u32_u8( v.val[1] );
return details::combine(r0, r1);
}
static RBX_SIMD_INLINE uint32x2_t cast( uint8x8_t v )
{
return vreinterpret_u32_u8( v );
}
};
template<>
struct CastFromUInt8< int32x4_t >
{
static RBX_SIMD_INLINE int32x4_t cast( uint8x8x2_t v )
{
int32x2_t r0 = vreinterpret_s32_u8( v.val[0] );
int32x2_t r1 = vreinterpret_s32_u8( v.val[1] );
return details::combine(r0, r1);
}
static RBX_SIMD_INLINE int32x2_t cast( uint8x8_t v )
{
return vreinterpret_s32_u8( v );
}
};
template< int a, int b >
RBX_SIMD_INLINE uint32x2_t selectMaskD()
{
boost::uint64_t mask64 = ( boost::uint64_t( boost::uint32_t( -( boost::int32_t(b) & 0x1 ) ) ) << 32 )
| boost::uint64_t( boost::uint32_t( -(boost::int32_t)( a & 0x1 ) ) );
return vcreate_u32( mask64 );
}
}
template< uint32_t a, uint32_t b, uint32_t c, uint32_t d >
RBX_SIMD_INLINE v4u selectMask()
{
uint32x2_t low_u = details::selectMaskD< a, b >();
uint32x2_t high_u = details::selectMaskD< c, d >();
v4u r;
r.v = vcombine_u32( low_u, high_u );
return r;
}
template< class VectorType >
RBX_SIMD_INLINE VectorType select( const VectorType& a, const VectorType& b, const v4u& mask )
{
VectorType r;
typename details::v2< typename VectorType::elem_t >::pod_t rlow = details::selectD( details::low( a.v ), details::low( b.v ), details::low( mask.v ) );
typename details::v2< typename VectorType::elem_t >::pod_t rhigh = details::selectD( details::high( a.v ), details::high( b.v ), details::high( mask.v ) );
r.v = details::combine( rlow, rhigh );
return r;
}
#define RBX_SIMD_NEON_TBL_MASK( a ) ( ( 4 * ( a ) ) | ( ( 4 * ( a ) + 1 ) << 8 ) | ( ( 4 * ( a ) + 2 ) << 16 ) | ( ( 4 * ( a ) + 3 ) << 24 ) )
namespace details
{
template< uint32_t a, uint32_t b, class VectorType >
RBX_SIMD_INLINE typename details::v2< typename VectorType::elem_t >::pod_t chooseTwoElements( const VectorType& u )
{
uint8x8x2_t u8 = details::castToUInt8x2( u.v );
uint64_t lowMask = uint64_t( RBX_SIMD_NEON_TBL_MASK( a ) ) | ( uint64_t( RBX_SIMD_NEON_TBL_MASK( b ) ) << 32 );
return details::CastFromUInt8< typename VectorType::pod_t >::cast( vtbl2_u8( u8, vcreate_u8(lowMask) ) );
}
template< uint32_t a, uint32_t b, class VectorType >
RBX_SIMD_INLINE typename details::v2< typename VectorType::elem_t >::pod_t chooseTwoElements( const VectorType& u, const VectorType& v )
{
uint8x8x4_t u8 = details::castToUInt8x4( u.v, v.v );
uint64_t lowMask = uint64_t( RBX_SIMD_NEON_TBL_MASK( a ) ) | ( uint64_t( RBX_SIMD_NEON_TBL_MASK( b ) ) << 32 );
return details::CastFromUInt8< typename VectorType::pod_t >::cast( vtbl4_u8( u8, vcreate_u8(lowMask) ) );
}
}
template< uint32_t a, uint32_t b, uint32_t c, uint32_t d, class VectorType >
RBX_SIMD_INLINE VectorType shuffle( const VectorType& u, const VectorType& v )
{
VectorType r;
r.v = details::combine( details::chooseTwoElements<a, b>( u ), details::chooseTwoElements<c, d>( v ) );
return r;
}
namespace details
{
RBX_SIMD_INLINE float32x4_t zipLow( float32x4_t a, float32x4_t b )
{
float32x2x2_t t = vzip_f32( low(a), low(b) );
return combine( t.val[0], t.val[1] );
}
RBX_SIMD_INLINE uint32x4_t zipLow( uint32x4_t a, uint32x4_t b )
{
uint32x2x2_t t = vzip_u32( low(a), low(b) );
return combine( t.val[0], t.val[1] );
}
RBX_SIMD_INLINE int32x4_t zipLow( int32x4_t a, int32x4_t b )
{
int32x2x2_t t = vzip_s32( low(a), low(b) );
return combine( t.val[0], t.val[1] );
}
RBX_SIMD_INLINE float32x4_t zipHigh( float32x4_t a, float32x4_t b )
{
float32x2x2_t t = vzip_f32( high(a), high(b) );
return combine( t.val[0], t.val[1] );
}
RBX_SIMD_INLINE uint32x4_t zipHigh( uint32x4_t a, uint32x4_t b )
{
uint32x2x2_t t = vzip_u32( high(a), high(b) );
return combine( t.val[0], t.val[1] );
}
RBX_SIMD_INLINE int32x4_t zipHigh( int32x4_t a, int32x4_t b )
{
int32x2x2_t t = vzip_s32( high(a), high(b) );
return combine( t.val[0], t.val[1] );
}
RBX_SIMD_INLINE float32x4x2_t zip( float32x4_t a, float32x4_t b )
{
return vzipq_f32( a, b );
}
RBX_SIMD_INLINE uint32x4x2_t zip( uint32x4_t a, uint32x4_t b )
{
return vzipq_u32( a, b );
}
RBX_SIMD_INLINE int32x4x2_t zip( int32x4_t a, int32x4_t b )
{
return vzipq_s32( a, b );
}
}
template< class VectorType >
RBX_SIMD_INLINE VectorType zipLow( const VectorType& u, const VectorType& v )
{
return VectorType( details::zipLow( u.v, v.v ) );
}
template< class VectorType >
RBX_SIMD_INLINE VectorType zipHigh( const VectorType& u, const VectorType& v )
{
return VectorType( details::zipHigh( u.v, v.v ) );
}
template< class VectorType >
RBX_SIMD_INLINE void zip( VectorType& r0, VectorType& r1, const VectorType& u, const VectorType& v )
{
auto r = details::zip( u.v, v.v );
r0.v = r.val[0];
r1.v = r.val[1];
}
template< class VectorType >
RBX_SIMD_INLINE VectorType moveHighLow( const VectorType& u, const VectorType& v )
{
VectorType r;
r.v = details::combine( details::high( v ), details::high( u ) );
return r;
}
template< class VectorType >
RBX_SIMD_INLINE VectorType moveLowHigh( const VectorType& u, const VectorType& v )
{
VectorType r;
r.v = details::combine( details::low( u ), details::low( v ) );
return r;
}
namespace details
{
template< class T, uint32_t a, uint32_t b, uint32_t c, uint32_t d >
struct SelectHelper
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
v4u mask = selectMask<a, b, c, d>();
return simd::select( u, v, mask );
}
};
template< class T >
struct SelectHelper< T, 0,0,0,0 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return u;
}
};
template< class T >
struct SelectHelper< T, 1,1,1,1 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return v;
}
};
template< class T >
struct SelectHelper< T, 1,0,0,0 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
uint32x2_t mask = details::selectMaskD< 1, 0 >();
typename details::v2< typename T::elem_t >::pod_t lowr = details::selectD( details::low( u ), details::low( v ), mask );
T r;
r.v = details::combine( lowr, details::high( u ) );
return r;
}
};
template< class T >
struct SelectHelper< T, 0,1,1,1 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return SelectHelper< T, 1, 0, 0, 0 >::select( v, u );
}
};
template< class T >
struct SelectHelper< T, 0,1,0,0 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
uint32x2_t mask = details::selectMaskD< 0, 1 >();
typename details::v2< typename T::elem_t >::pod_t lowr = details::selectD( details::low( u ), details::low( v ), mask );
T r;
r.v = details::combine( lowr, details::high( u ) );
return r;
}
};
template< class T >
struct SelectHelper< T, 1,0,1,1 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return SelectHelper<T, 0, 1, 0, 0 >::select( v, u );
}
};
template< class T >
struct SelectHelper< T, 0,0,1,0 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
uint32x2_t mask = details::selectMaskD< 1, 0 >();
typename details::v2< typename T::elem_t >::pod_t highr = details::selectD( details::high( u ), details::high( v ), mask );
T r;
r.v = details::combine( details::low( u ), highr );
return r;
}
};
template< class T >
struct SelectHelper< T, 1,1,0,1 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return SelectHelper<T, 0, 0, 1, 0 >::select( v, u );
}
};
template< class T >
struct SelectHelper< T, 0,0,0,1 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
uint32x2_t mask = details::selectMaskD< 0, 1 >();
typename details::v2< typename T::elem_t >::pod_t highr = details::selectD( details::high( u ), details::high( v ), mask );
T r;
r.v = details::combine( details::low( u ), highr );
return r;
}
};
template< class T >
struct SelectHelper< T, 1,1,1,0 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return SelectHelper<T, 0, 0, 0, 1 >::select( v, u );
}
};
template< class T >
struct SelectHelper< T, 0,0,1,1 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return details::combine( details::low(u), details::high(v) );
}
};
template< class T >
struct SelectHelper< T, 1,1,0,0 >
{
static RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return SelectHelper<T, 0, 0, 1, 1 >::select( v, u );
}
};
}
template< uint32_t a, uint32_t b, uint32_t c, uint32_t d, class T >
RBX_SIMD_INLINE T select( const T& u, const T& v )
{
return details::SelectHelper< T, a, b, c, d >::select( u, v );
}
template< uint32_t index, class T >
RBX_SIMD_INLINE T replace( const T& a, const T& b )
{
BOOST_STATIC_ASSERT_MSG( ( index & 0x3 ) == index, "index must be between 0 and 3" );
return select< index == 0, index == 1, index == 2, index == 3 >( a, b );
}
//
// Permutes
//
template< uint32_t a, uint32_t b, uint32_t c, uint32_t d, class VectorType >
RBX_SIMD_INLINE VectorType permute( const VectorType& u )
{
VectorType r;
r.v = details::combine( details::chooseTwoElements<a, b>(u), details::chooseTwoElements<c, d>(u) );
return r;
}
template< uint32_t offset, class T >
RBX_SIMD_INLINE T rotateLeft( const T& v )
{
return shuffle< offset % 4, ( offset + 1 ) % 4, ( offset + 2 ) % 4, ( offset + 3 ) % 4 >( v, v );
}
//
// Compares
//
RBX_SIMD_INLINE v4u compareGreater( v4fArg a, v4fArg b )
{
v4u r;
r.v = vcgtq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator>( v4fArg a, v4fArg b )
{
v4u r;
r.v = vcgtq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u compareGreaterEqual( v4fArg a, v4fArg b )
{
v4u r;
r.v = vcgeq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator>=( v4fArg a, v4fArg b )
{
v4u r;
r.v = vcgeq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u compareLess( v4fArg a, v4fArg b )
{
v4u r;
r.v = vcltq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator<( v4fArg a, v4fArg b )
{
v4u r;
r.v = vcltq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u compareLessEqual( v4fArg a, v4fArg b )
{
v4u r;
r.v = vcleq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator<=( v4fArg a, v4fArg b )
{
v4u r;
r.v = vcleq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u compare( v4fArg a, v4fArg b )
{
v4u r;
r.v = vceqq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator==( v4fArg a, v4fArg b )
{
v4u r;
r.v = vceqq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator!=( v4fArg a, v4fArg b )
{
v4u r;
r.v = vornq_u32( vdupq_n_u32( 0 ), vceqq_f32( a.v, b.v ) );
return r;
}
RBX_SIMD_INLINE v4u compare( v4iArg a, v4iArg b )
{
v4u r;
r.v = vceqq_s32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator==( v4iArg a, v4iArg b )
{
v4u r;
r.v = vceqq_s32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator!=( v4iArg a, v4iArg b )
{
v4u r;
r.v = vornq_u32( vdupq_n_u32( 0 ), vceqq_s32( a.v, b.v ) );
return r;
}
RBX_SIMD_INLINE v4u compare( v4uArg a, v4uArg b )
{
v4u r;
r.v = vceqq_u32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator==( v4uArg a, v4uArg b )
{
v4u r;
r.v = vceqq_u32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4u operator!=( v4uArg a, v4uArg b )
{
v4u r;
r.v = vornq_u32( vdupq_n_u32( 0 ), vceqq_u32( a.v, b.v ) );
return r;
}
//
// Set
//
template< uint32_t a >
RBX_SIMD_INLINE v4f replaceWithZero( v4fArg v )
{
BOOST_STATIC_ASSERT_MSG( ( a & 0x3 ) == a, "a must be between 0 and 3" );
v4f r;
r.v = vsetq_lane_f32(0.0f, v.v, a);
return r;
}
template< uint32_t a >
RBX_SIMD_INLINE v4i replaceWithZero( v4iArg v )
{
BOOST_STATIC_ASSERT_MSG( ( a & 0x3 ) == a, "a must be between 0 and 3" );
v4i r;
r.v = vsetq_lane_s32(0, v.v, a);
return r;
}
template< uint32_t a >
RBX_SIMD_INLINE v4u replaceWithZero( v4uArg v )
{
BOOST_STATIC_ASSERT_MSG( ( a & 0x3 ) == a, "a must be between 0 and 3" );
v4u r;
r.v = vsetq_lane_u32(0, v.v, a);
return r;
}
//
// Float Arithmetics
//
RBX_SIMD_INLINE v4f operator+( v4fArg a, v4fArg b )
{
v4f r;
r.v = vaddq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4f& operator+=( v4f& a, v4fArg b )
{
a.v = vaddq_f32( a.v, b.v );
return a;
}
RBX_SIMD_INLINE v4f operator-( v4fArg a, v4fArg b )
{
v4f r;
r.v = vsubq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4f& operator-=( v4f& a, v4fArg b )
{
a.v = vsubq_f32( a.v, b.v );
return a;
}
RBX_SIMD_INLINE v4f operator*( v4fArg a, v4fArg b )
{
v4f r;
r.v = vmulq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4f& operator*=( v4f& a, v4fArg b )
{
a.v = vmulq_f32( a.v, b.v );
return a;
}
RBX_SIMD_INLINE v4f mulAdd( v4fArg a, v4fArg b, v4fArg c )
{
v4f r;
r.v = vmlaq_f32( a.v, b.v, c.v );
return r;
}
RBX_SIMD_INLINE v4f operator-( v4fArg a )
{
v4f r;
r.v = vnegq_f32( a.v );
return r;
}
RBX_SIMD_INLINE v4f operator/( v4fArg a, v4fArg b )
{
return a * inverseEstimate1( b );
}
RBX_SIMD_INLINE v4f& operator/=( v4f& a, v4fArg b )
{
a = a * inverseEstimate1( b );
return a;
}
RBX_SIMD_INLINE v4f abs( v4fArg a )
{
v4f r;
r.v = vabsq_f32( a.v );
return r;
}
//
// Inverse estimates
//
namespace details
{
RBX_SIMD_INLINE v4f inverseEstimateIteration(const v4f& a, const v4f& estimate)
{
return estimate * v4f( vrecpsq_f32( a.v, estimate.v ) );
}
}
RBX_SIMD_INLINE v4f inverseEstimate0Fast( v4fArg a )
{
v4f estimate = v4f(vrecpeq_f32( a.v ));
v4f estimate1 = details::inverseEstimateIteration(a, estimate);
return estimate1;
}
RBX_SIMD_INLINE v4f inverseEstimate0( v4fArg a )
{
v4f estimate = v4f(vrecpeq_f32( a.v ));
v4f estimate1 = details::inverseEstimateIteration(a, estimate);
// Handle small inputs
estimate1 = select( estimate, estimate1, abs( estimate1 ) < splat( std::numeric_limits< float >::infinity() ) );
// Handle infinities
v4f signedZero = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32( vdupq_n_f32(-0.0f) ), vreinterpretq_u32_f32( a.v ) ) );
return select( estimate1, signedZero, abs( a ) == splat( std::numeric_limits< float >::infinity() ) );
}
RBX_SIMD_INLINE v4f inverseEstimate1Fast( v4fArg a )
{
v4f estimate = v4f(vrecpeq_f32( a.v ));
v4f estimate1 = details::inverseEstimateIteration(a, estimate);
estimate1 = details::inverseEstimateIteration(a, estimate1);
return estimate1;
}
RBX_SIMD_INLINE v4f inverseEstimate1( v4fArg a )
{
v4f estimate = v4f(vrecpeq_f32( a.v ));
v4f estimate1 = details::inverseEstimateIteration(a, estimate);
estimate1 = details::inverseEstimateIteration(a, estimate1);
// Handle denorms
estimate1 = select( estimate, estimate1, abs( estimate1 ) < splat( std::numeric_limits< float >::infinity() ) );
// Handle infinities
v4f signedZero = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32( vdupq_n_f32(-0.0f) ), vreinterpretq_u32_f32( a.v ) ) );
return select( estimate1, signedZero, abs( a ) == splat( std::numeric_limits< float >::infinity() ) );
}
RBX_SIMD_INLINE v4f smallestInvertible( )
{
// This is not the same as std::numeric_limints< float >::min().
// This number is denormalized
return splat( 2.9387359e-39f );
}
RBX_SIMD_INLINE v4f largestInvertible( )
{
return splat( 3.40282347e+38f );
}
//
// Inverse sqrt estimate
//
namespace details
{
RBX_SIMD_INLINE v4f inverseSqrtIteration(const v4f& v, const v4f& estimate)
{
v4f estimate2 = estimate * v;
return estimate * v4f( vrsqrtsq_f32(estimate2.v, estimate.v) );
}
}
RBX_SIMD_INLINE v4f inverseSqrtEstimate0( v4fArg a )
{
v4f estimate( vrsqrteq_f32( a.v ) );
v4f estimate1 = details::inverseSqrtIteration(a, estimate);
// Handle small numbers
estimate1 = select( estimate, estimate1, abs( estimate1 ) < splat( std::numeric_limits< float >::infinity() ) );
// Handle infinities
return select( estimate1, splat(0.0f), a == splat( std::numeric_limits< float >::infinity() ) );
}
RBX_SIMD_INLINE v4f inverseSqrtEstimate0Fast( v4fArg a )
{
v4f estimate( vrsqrteq_f32( a.v ) );
estimate = details::inverseSqrtIteration(a, estimate);
return estimate;
}
RBX_SIMD_INLINE v4f inverseSqrtEstimate1( v4fArg a )
{
v4f estimate( vrsqrteq_f32( a.v ) );
v4f estimate1 = details::inverseSqrtIteration(a, estimate);
estimate1 = details::inverseSqrtIteration(a, estimate1);
// Handle small numbers
estimate1 = select( estimate, estimate1, abs( estimate1 ) < splat( std::numeric_limits< float >::infinity() ) );
// Handle infinities
return select( estimate1, splat(0.0f), a == splat( std::numeric_limits< float >::infinity() ) );
}
RBX_SIMD_INLINE v4f inverseSqrtEstimate1Fast(const v4f& v)
{
v4f estimate( vrsqrteq_f32( v.v ) );
estimate = details::inverseSqrtIteration(v, estimate);
estimate = details::inverseSqrtIteration(v, estimate);
return estimate;
}
RBX_SIMD_INLINE v4f smallestSqrtInvertible( )
{
return splat( 1.4012985e-45f );
}
RBX_SIMD_INLINE v4f largestSqrtInvertible( )
{
// This is the same as std::numeric_limints< float >::max()
return splat( 3.40282347E+38f );
}
RBX_SIMD_INLINE v4f max( v4fArg a, v4fArg b )
{
v4f r;
r.v = vmaxq_f32( a.v, b.v );
return r;
}
RBX_SIMD_INLINE v4f min( v4fArg a, v4fArg b )
{
v4f r;
r.v = vminq_f32( a.v, b.v );
return r;
}
//
// Inter-element vector ops
//
RBX_SIMD_INLINE v4f dotProduct( v4fArg a, v4fArg b )
{
float32x4_t p0123 = vmulq_f32( a.v, b.v );
float32x4_t p1032 = vrev64q_f32( p0123 );
float32x4_t t = p0123 + p1032;
t = t + details::combine( details::high( t ), details::low( t ) );
return v4f( t );
}
RBX_SIMD_INLINE v4f dotProduct3( v4fArg a, v4fArg b )
{
float32x4_t p0123 = vmulq_f32( a.v, b.v );
p0123 = vsetq_lane_f32(0.0f, p0123, 3);
float32x4_t p1032 = vrev64q_f32( p0123 );
float32x4_t t = p0123 + p1032;
t = t + details::combine( details::high( t ), details::low( t ) );
return t;
}
RBX_SIMD_INLINE v4f sumAcross( v4fArg a )
{
float32x4_t p0123 = a.v;
float32x4_t p1032 = vrev64q_f32( p0123 );
float32x4_t t = p0123 + p1032;
t = t + details::combine( details::high( t ), details::low( t ) );
return v4f( t );
}
RBX_SIMD_INLINE v4f sumAcross2( v4fArg a, v4fArg b )
{
float32x2x2_t t = vtrn_f32( details::low( a.v ), details::low( b.v ) );
float32x2_t sum = t.val[0] + t.val[1];
return v4f( details::combine( sum, sum ) );
}
RBX_SIMD_INLINE v4f sumAcross3( v4fArg a, v4fArg b )
{
float32x2x2_t t01 = vtrn_f32( details::low( a.v ), details::low( b.v ) );
float32x2x2_t t2 = vtrn_f32( details::high( a.v ), details::high( b.v ) );
float32x2_t sum = t01.val[0] + t01.val[1] + t2.val[0];
return v4f( details::combine( sum, sum ) );
}
RBX_SIMD_INLINE v4f sumAcross4( v4fArg a, v4fArg b )
{
float32x2x2_t t01 = vtrn_f32( details::low( a.v ), details::low( b.v ) );
float32x2x2_t t23 = vtrn_f32( details::high( a.v ), details::high( b.v ) );
float32x2_t sum = ( t01.val[0] + t01.val[1] ) + ( t23.val[0] + t23.val[1] );
return v4f( details::combine( sum, sum ) );
}
//
// Packing / unpacking
//
namespace details
{
template< class T >
RBX_SIMD_INLINE T moveHigh( const T& a, const T& b )
{
return details::combine( details::low( a ), details::high( b ) );
}
template< class T >
RBX_SIMD_INLINE T moveLow( const T& a, const T& b )
{
return details::combine( details::low( b ), details::high( a ) );
}
}
template< class T >
RBX_SIMD_INLINE void pack3( typename T::pod_t* __restrict p, const T& a, const T& b, const T& c, const T& d )
{
p[0] = details::combine( details::low(a), details::zip1( details::high(a), details::low(b) ) );
p[1] = details::combine( details::chooseTwoElements<1, 2>(b), details::low(c) );
p[2] = details::combine( details::zip1( details::high(c), details::low(d) ), details::chooseTwoElements<1, 2>( d ) );
}
template< class T >
RBX_SIMD_INLINE void unpack3( T& a, T& b, T& c, T& d, const typename T::pod_t* p )
{
a = p[0];
b = details::combine( details::chooseTwoElements<3, 4>(T(p[0]),T(p[1])), details::chooseTwoElements<1, 2>(T(p[1])) );
c = details::combine( details::chooseTwoElements<2, 3>(T(p[1])), details::chooseTwoElements<0, 1>(T(p[2])));
d = details::combine( details::chooseTwoElements<1, 2>(T(p[2])), details::chooseTwoElements<3, 3>(T(p[2])));
}
}
}