The SSE4 instruction set consists of two parts, referred as SSE4.1 and 4.2. The intrinsics are located in the smmintrin.h
header. The SSE4.1 instruction set is the most interesting for DirectXMath, while SSE 4.2 adds some more specialized instructions for CRC checks and string handling. The key new features are a flexible dot-product instruction, float4 vector rounding, a 2-vector ‘mux’ blend, and some specialized extract/insert operations.
A number of DirectXMath functions can be replaced with a single intrinsic when using SSE4.1.
XMVector2Dot(V1,V2) | _mm_dp_ps( V1, V2, 0x3f ) |
XMVector3Dot(V1,V2) | _mm_dp_ps( V1, V2, 0x7f ) |
XMVector4Dot(V1,V2) | _mm_dp_ps( V1, V2, 0xff ) |
XMVectorRound(V) | _mm_round_ps( V, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC ) |
XMVectorTruncate(V) | _mm_round_ps( V, _MM_FROUND_TO_ZERO | _MM_FROUND_NO_EXC ) |
XMVectorFloor(V) | _mm_floor_ps( V ) |
XMVectorCeiling(V) | _mm_ceil_ps( V ) |
The bit insert/extract instructions provide some specific optimization cases for vector accessors and setters. Here are the “Y” element versions, which can be extrapolated to the “Z” and “W” element versions very easily. Note that the standard scalar SSE/SSE2 mov already provides efficient support for the “X” element.
inline void XMVectorGetYPtr(float *y, FXMVECTOR V)
{
*((int*)y) = _mm_extract_ps( V, 1 );
}
inline uint32_t XMVectorGetIntY(FXMVECTOR V)
{
__m128i V1 = _mm_castps_si128( V );
return static_cast<uint32_t>( _mm_extract_epi32( V1, 1 ) );
}
inline void XMVectorGetIntYPtr(uint32_t *y, FXMVECTOR V)
{
__m128i V1 = _mm_castps_si128( V );
*y = static_cast<uint32_t>( _mm_extract_epi32( V1, 1 ) );
}
inline XMVECTOR XMVectorSetY(FXMVECTOR V, float y)
{
XMVECTOR vResult = _mm_set_ss(y);
vResult = _mm_insert_ps( V, vResult, 0x10 );
return vResult;
}
inline XMVECTOR XMVectorSetIntY(FXMVECTOR V, uint32_t y)
{
__m128i vResult = _mm_castps_si128( V );
vResult =
_mm_insert_epi32( vResult, static_cast<int>(y), 1 );
return _mm_castsi128_ps( vResult );
}
The _mm_blend_ps
instruction can be used as special-cases for the XMVectorPermute<>
template. We’ll make more use of these in a future installment.
XMVectorPermute<4,1,2,3>(V1,V2) | _mm_blend_ps(V1,V2,0x1) |
XMVectorPermute<0,5,2,3>(V1,V2) | _mm_blend_ps(V1,V2,0x2) |
XMVectorPermute<4,5,2,3>(V1,V2) | _mm_blend_ps(V1,V2,0x3) |
XMVectorPermute<0,1,6,3>(V1,V2) | _mm_blend_ps(V1,V2,0x4) |
XMVectorPermute<4,1,6,3>(V1,V2) | _mm_blend_ps(V1,V2,0x5) |
XMVectorPermute<0,5,6,3>(V1,V2) | _mm_blend_ps(V1,V2,0x6) |
XMVectorPermute<4,5,6,3>(V1,V2) | _mm_blend_ps(V1,V2,0x7) |
XMVectorPermute<0,1,2,7>(V1,V2) | _mm_blend_ps(V1,V2,0x8) |
XMVectorPermute<4,1,2,7>(V1,V2) | _mm_blend_ps(V1,V2,0x9) |
XMVectorPermute<0,5,2,7>(V1,V2) | _mm_blend_ps(V1,V2,0xA) |
XMVectorPermute<4,5,2,7>(V1,V2) | _mm_blend_ps(V1,V2,0xB) |
XMVectorPermute<0,1,6,7>(V1,V2) | _mm_blend_ps(V1,V2,0xC) |
XMVectorPermute<4,1,6,7>(V1,V2) | _mm_blend_ps(V1,V2,0xD) |
XMVectorPermute<0,5,6,7>(V1,V2) | _mm_blend_ps(V1,V2,0xE) |
Processor Support
SSE4.1 is supported on Intel Core 2 (“Penryn”), Intel Core i7 (“Nehalem”), AMD Bulldozer, and later processors.
SSE 4.1 and SSE4.2 are supported on Intel Core i7 (“Nehalem”), AMD Bulldozer, and later processors.
int CPUInfo[4] = {-1};
__cpuid( CPUInfo, 0 );
bool bSSE4_1 = false;
bool bSSE4_2 = false;
if ( CPUInfo[0] > 0 )
{
__cpuid(CPUInfo, 1 );
bSSE4_1 = (CPUInfo[2] & 0x80000) != 0;
bSSE4_2 = (CPUInfo[2] & 0x100000) != 0;
}
Compiler Support
Support for SSE4.1 and SSE4.2 intrinsics was added to Visual Studio 2010.
Utility Code
The source code attached to this blog post is bound to the Microsoft Public License (MS-PL).
See also: SSE, SSE2, and ARM-NEON; SSE3 and SSSE3; AVX; F16C and FMA