# OgreSIMDHelper.h

Go to the documentation of this file.

    00001 /*
    00002 -----------------------------------------------------------------------------
    00003 This source file is part of OGRE
    00004     (Object-oriented Graphics Rendering Engine)
    00005 For the latest info, see http://www.ogre3d.org/
    00006
    00007 Copyright (c) 2000-2006 Torus Knot Software Ltd
    00008 Also see acknowledgements in Readme.html
    00009
    00010 This program is free software; you can redistribute it and/or modify it under
    00011 the terms of the GNU Lesser General Public License as published by the Free Software
    00012 Foundation; either version 2 of the License, or (at your option) any later
    00013 version.
    00014
    00015 This program is distributed in the hope that it will be useful, but WITHOUT
    00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    00017 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
    00018
    00019 You should have received a copy of the GNU Lesser General Public License along with
    00020 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
    00021 Place - Suite 330, Boston, MA 02111-1307, USA, or go to
    00022 http://www.gnu.org/copyleft/lesser.txt.
    00023
    00024 You may alternatively use this source under the terms of a specific version of
    00025 the OGRE Unrestricted License provided you have obtained such a license from
    00026 Torus Knot Software Ltd.
    00027 -----------------------------------------------------------------------------
    00028 */
    00029 #ifndef __SIMDHelper_H__
    00030 #define __SIMDHelper_H__
    00031
    00032 #include "OgrePrerequisites.h"
    00033 #include "OgrePlatformInformation.h"
    00034
    00035 // Stack-alignment hackery.
    00036 //
    00037 // If macro __OGRE_SIMD_ALIGN_STACK defined, means there requests
    00038 // special code to ensure stack align to a 16-bytes boundary.
    00039 //
    00040 // Note:
    00041 //   This macro can only guarantee callee stack pointer (esp) align
    00042 // to a 16-bytes boundary, but not that for frame pointer (ebp).
    00043 // Because most compiler might use frame pointer to access to stack
    00044 // variables, so you need to wrap those alignment required functions
    00045 // with extra function call.
    00046 //
    00047 #if defined(__INTEL_COMPILER)
    00048 // For intel's compiler, simply calling alloca seems to do the right
    00049 // thing. The size of the allocated block seems to be irrelevant.
    00050 #define __OGRE_SIMD_ALIGN_STACK()   _alloca(16)
    00051
    00052 #elif OGRE_CPU == OGRE_CPU_X86 && OGRE_COMPILER == OGRE_COMPILER_GNUC
    00053 //
    00054 // Horrible hack to align the stack to a 16-bytes boundary for gcc.
    00055 //
    00056 // We assume a gcc version >= 2.95 so that
    00057 // -mpreferred-stack-boundary works.  Otherwise, all bets are
    00058 // off.  However, -mpreferred-stack-boundary does not create a
    00059 // stack alignment, but it only preserves it.  Unfortunately,
    00060 // since Ogre are designed as a flexibility library, user might
    00061 // compile their application with wrong stack alignment, even
    00062 // if user taken care with stack alignment, but many versions
    00063 // of libc on linux call main() with the wrong initial stack
    00064 // alignment the result that the code is now pessimally aligned
    00065 // instead of having a 50% chance of being correct.
    00066 //
    00067 #define __OGRE_SIMD_ALIGN_STACK()                                   \
    00068     {                                                               \
    00069         /* Use alloca to allocate some memory on the stack.  */     \
    00070         /* This alerts gcc that something funny is going on, */     \
    00071         /* so that it does not omit the frame pointer etc.   */     \
    00072         (void)__builtin_alloca(16);                                 \
    00073         /* Now align the stack pointer */                           \
    00074         __asm__ __volatile__ ("andl $-16, %esp");                   \
    00075     }
    00076
    00077 #elif defined(_MSC_VER)
    00078 // Fortunately, MSVC will align the stack automatically
    00079
    00080 #endif
    00081
    00082
    00083 // Additional platform-dependent header files and declares.
    00084 //
    00085 // NOTE: Should be sync with __OGRE_HAVE_SSE macro.
    00086 //
    00087
    00088 #if OGRE_DOUBLE_PRECISION == 0 && OGRE_CPU == OGRE_CPU_X86
    00089
    00090 #if OGRE_COMPILER == OGRE_COMPILER_MSVC || defined(__INTEL_COMPILER)
    00091 #include "OgreNoMemoryMacros.h"
    00092 #include <xmmintrin.h>
    00093 #include "OgreMemoryMacros.h"
    00094
    00095 #elif OGRE_COMPILER == OGRE_COMPILER_GNUC
    00096 // Don't define ourself version SSE intrinsics if "xmmintrin.h" already included.
    00097 //
    00098 // Note: gcc in some platform already included "xmmintrin.h" for some reason.
    00099 // I pick up macro _XMMINTRIN_H_INCLUDED here which based on the "xmmintrin.h"
    00100 // comes with cygwin gcc 3.4.4, guess it should be solved duplicate definition
    00101 // problem on gcc for x86.
    00102 //
    00103 #if !defined(_XMMINTRIN_H_INCLUDED)
    00104
    00105 // Simulate VC/ICC intrinsics. Only used intrinsics are declared here.
    00106
    00107 typedef float __m128 __attribute__ ((mode(V4SF),aligned(16)));
    00108 typedef int __m64 __attribute__ ((mode(V2SI)));
    00109
    00110 // Macro for declare intrinsic routines always inline even if in debug build
    00111 #define __ALWAYS_INLINE    FORCEINLINE __attribute__ ((__always_inline__))
    00112
    00113 // Shuffle instruction must be declare as macro
    00114
    00115 #define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \
    00116     (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | ((fp0)))
    00117
    00118 #define _mm_shuffle_ps(a, b, imm8) __extension__                                        \
    00119     ({                                                                                  \
    00120         __m128 result;                                                                  \
    00121         __asm__("shufps %3, %2, %0" : "=x" (result) : "0" (a), "xm" (b), "N" (imm8));   \
    00122         result;                                                                         \
    00123     })
    00124
    00125
    00126 // Load/store instructions
    00127
    00128 #define __MM_DECL_LD(name, instruction, type)                               \
    00129     static __ALWAYS_INLINE __m128 _mm_##name(const type *addr)              \
    00130     {                                                                       \
    00131         __m128 result;                                                      \
    00132         __asm__( #instruction " %1, %0" : "=x" (result) : "m" (*addr));     \
    00133         return result;                                                      \
    00134     }
    00135
    00136 #define __MM_DECL_LD2(name, instruction, type)                                      \
    00137     static __ALWAYS_INLINE __m128 _mm_##name(__m128 val, const type *addr)          \
    00138     {                                                                               \
    00139         __m128 result;                                                              \
    00140         __asm__( #instruction " %2, %0" : "=x" (result) : "0"(val), "m" (*addr));   \
    00141         return result;                                                              \
    00142     }
    00143
    00144 #define __MM_DECL_ST(name, instruction, type)                               \
    00145     static __ALWAYS_INLINE void _mm_##name(type *addr, __m128 val)          \
    00146     {                                                                       \
    00147         __asm__( #instruction " %1, %0" : "=m" (*addr) : "x" (val));        \
    00148     }
    00149
    00150 __MM_DECL_LD(loadu_ps, movups, float)
    00151 __MM_DECL_ST(storeu_ps, movups, float)
    00152
    00153 __MM_DECL_LD(load_ss, movss, float)
    00154 __MM_DECL_ST(store_ss, movss, float)
    00155
    00156 __MM_DECL_ST(storel_pi, movlps, __m64)
    00157 __MM_DECL_ST(storeh_pi, movhps, __m64)
    00158 __MM_DECL_LD2(loadl_pi, movlps, __m64)
    00159 __MM_DECL_LD2(loadh_pi, movhps, __m64)
    00160
    00161 #undef __MM_DECL_LD
    00162 #undef __MM_DECL_LD2
    00163 #undef __MM_DECL_ST
    00164
    00165 // Two operand instructions
    00166
    00167 #define __MM_DECL_OP2(name, instruction, constraint)                                    \
    00168     static __ALWAYS_INLINE __m128 _mm_##name(__m128 a, __m128 b)                        \
    00169     {                                                                                   \
    00170         __m128 result;                                                                  \
    00171         __asm__( #instruction " %2, %0" : "=x" (result) : "0" (a), #constraint (b));    \
    00172         return result;                                                                  \
    00173     }
    00174
    00175 __MM_DECL_OP2(add_ps, addps, xm)
    00176 __MM_DECL_OP2(add_ss, addss, xm)
    00177 __MM_DECL_OP2(sub_ps, subps, xm)
    00178 __MM_DECL_OP2(sub_ss, subss, xm)
    00179 __MM_DECL_OP2(mul_ps, mulps, xm)
    00180 __MM_DECL_OP2(mul_ss, mulss, xm)
    00181
    00182 __MM_DECL_OP2(xor_ps, xorps, xm)
    00183
    00184 __MM_DECL_OP2(unpacklo_ps, unpcklps, xm)
    00185 __MM_DECL_OP2(unpackhi_ps, unpckhps, xm)
    00186
    00187 __MM_DECL_OP2(movehl_ps, movhlps, x)
    00188 __MM_DECL_OP2(movelh_ps, movlhps, x)
    00189
    00190 __MM_DECL_OP2(cmpnle_ps, cmpnleps, xm)
    00191
    00192 #undef __MM_DECL_OP2
    00193
    00194 // Other used instructions
    00195
    00196     static __ALWAYS_INLINE __m128 _mm_load_ps1(const float *addr)
    00197     {
    00198         __m128 tmp = _mm_load_ss(addr);
    00199         return _mm_shuffle_ps(tmp, tmp, 0);
    00200     }
    00201
    00202     static __ALWAYS_INLINE __m128 _mm_setzero_ps(void)
    00203     {
    00204         __m128 result;
    00205         __asm__("xorps %0, %0" : "=x" (result));
    00206         return result;
    00207     }
    00208
    00209     static __ALWAYS_INLINE __m128 _mm_rsqrt_ps(__m128 val)
    00210     {
    00211         __m128 result;
    00212         __asm__("rsqrtps %1, %0" : "=x" (result) : "xm" (val));
    00213         //__asm__("rsqrtps %0, %0" : "=x" (result) : "0" (val));
    00214         return result;
    00215     }
    00216
    00217     static __ALWAYS_INLINE int _mm_movemask_ps(__m128 val)
    00218     {
    00219         int result;
    00220         __asm__("movmskps %1, %0" : "=r" (result) : "x" (val));
    00221         return result;
    00222     }
    00223
    00224 #endif // !defined(_XMMINTRIN_H_INCLUDED)
    00225
    00226 #endif // OGRE_COMPILER == OGRE_COMPILER_GNUC
    00227
    00228 #endif // OGRE_DOUBLE_PRECISION == 0 && OGRE_CPU == OGRE_CPU_X86
    00229
    00230
    00231
    00232 //---------------------------------------------------------------------
    00233 // SIMD macros and helpers
    00234 //---------------------------------------------------------------------
    00235
    00236
    00237 namespace Ogre {
    00238
    00239 #if __OGRE_HAVE_SSE
    00240
    00251 #if 1
    00252 #define __MM_RSQRT_PS(x)    _mm_rsqrt_ps(x)
    00253 #else
    00254 #define __MM_RSQRT_PS(x)    __mm_rsqrt_nr_ps(x) // Implemented below
    00255 #endif
    00256
    00265 #define __MM_TRANSPOSE4x4_PS(r0, r1, r2, r3)                                        \
    00266     {                                                                               \
    00267         __m128 t3, t2, t1, t0;                                                      \
    00268                                                                                     \
    00269                                                             /* r00 r01 r02 r03 */   \
    00270                                                             /* r10 r11 r12 r13 */   \
    00271                                                             /* r20 r21 r22 r23 */   \
    00272                                                             /* r30 r31 r32 r33 */   \
    00273                                                                                     \
    00274         t0 = _mm_unpacklo_ps(r0, r1);                       /* r00 r10 r01 r11 */   \
    00275         t2 = _mm_unpackhi_ps(r0, r1);                       /* r02 r12 r03 r13 */   \
    00276         t1 = _mm_unpacklo_ps(r2, r3);                       /* r20 r30 r21 r31 */   \
    00277         t3 = _mm_unpackhi_ps(r2, r3);                       /* r22 r32 r23 r33 */   \
    00278                                                                                     \
    00279         r0 = _mm_movelh_ps(t0, t1);                         /* r00 r10 r20 r30 */   \
    00280         r1 = _mm_movehl_ps(t1, t0);                         /* r01 r11 r21 r31 */   \
    00281         r2 = _mm_movelh_ps(t2, t3);                         /* r02 r12 r22 r32 */   \
    00282         r3 = _mm_movehl_ps(t3, t2);                         /* r03 r13 r23 r33 */   \
    00283     }
    00284
    00293 #define __MM_TRANSPOSE4x3_PS(v0, v1, v2)                                            \
    00294     {                                                                               \
    00295         __m128 t0, t1, t2;                                                          \
    00296                                                                                     \
    00297                                                             /* r00 r01 r02 r10 */   \
    00298                                                             /* r11 r12 r20 r21 */   \
    00299                                                             /* r22 r30 r31 r32 */   \
    00300                                                                                     \
    00301         t0 = _mm_shuffle_ps(v0, v2, _MM_SHUFFLE(3,0,3,0));  /* r00 r10 r22 r32 */   \
    00302         t1 = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(1,0,2,1));  /* r01 r02 r11 r12 */   \
    00303         t2 = _mm_shuffle_ps(v1, v2, _MM_SHUFFLE(2,1,3,2));  /* r20 r21 r30 r31 */   \
    00304                                                                                     \
    00305         v0 = _mm_shuffle_ps(t0, t2, _MM_SHUFFLE(2,0,1,0));  /* r00 r10 r20 r30 */   \
    00306         v1 = _mm_shuffle_ps(t1, t2, _MM_SHUFFLE(3,1,2,0));  /* r01 r11 r21 r31 */   \
    00307         v2 = _mm_shuffle_ps(t1, t0, _MM_SHUFFLE(3,2,3,1));  /* r02 r12 r22 r32 */   \
    00308     }
    00309
    00317 #define __MM_TRANSPOSE3x4_PS(v0, v1, v2)                                            \
    00318     {                                                                               \
    00319         __m128 t0, t1, t2;                                                          \
    00320                                                                                     \
    00321                                                             /* r00 r10 r20 r30 */   \
    00322                                                             /* r01 r11 r21 r31 */   \
    00323                                                             /* r02 r12 r22 r32 */   \
    00324                                                                                     \
    00325         t0 = _mm_shuffle_ps(v0, v2, _MM_SHUFFLE(2,0,3,1));  /* r10 r30 r02 r22 */   \
    00326         t1 = _mm_shuffle_ps(v1, v2, _MM_SHUFFLE(3,1,3,1));  /* r11 r31 r12 r32 */   \
    00327         t2 = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(2,0,2,0));  /* r00 r20 r01 r21 */   \
    00328                                                                                     \
    00329         v0 = _mm_shuffle_ps(t2, t0, _MM_SHUFFLE(0,2,2,0));  /* r00 r01 r02 r10 */   \
    00330         v1 = _mm_shuffle_ps(t1, t2, _MM_SHUFFLE(3,1,2,0));  /* r11 r12 r20 r21 */   \
    00331         v2 = _mm_shuffle_ps(t0, t1, _MM_SHUFFLE(3,1,1,3));  /* r22 r30 r31 r32 */   \
    00332     }
    00333
    00337 #define __MM_SELECT(v, fp)                                                          \
    00338     _mm_shuffle_ps((v), (v), _MM_SHUFFLE((fp),(fp),(fp),(fp)))
    00339
    00341 #define __MM_ACCUM4_PS(a, b, c, d)                                                  \
    00342     _mm_add_ps(_mm_add_ps(a, b), _mm_add_ps(c, d))
    00343
    00347 #define __MM_DOT4x4_PS(a0, a1, a2, a3, b0, b1, b2, b3)                              \
    00348     __MM_ACCUM4_PS(_mm_mul_ps(a0, b0), _mm_mul_ps(a1, b1), _mm_mul_ps(a2, b2), _mm_mul_ps(a3, b3))
    00349
    00353 #define __MM_DOT4x3_PS(r0, r1, r2, r3, v0, v1, v2)                                  \
    00354     __MM_ACCUM4_PS(_mm_mul_ps(r0, v0), _mm_mul_ps(r1, v1), _mm_mul_ps(r2, v2), r3)
    00355
    00357 #define __MM_ACCUM3_PS(a, b, c)                                                     \
    00358     _mm_add_ps(_mm_add_ps(a, b), c)
    00359
    00363 #define __MM_DOT3x3_PS(r0, r1, r2, v0, v1, v2)                                      \
    00364     __MM_ACCUM3_PS(_mm_mul_ps(r0, v0), _mm_mul_ps(r1, v1), _mm_mul_ps(r2, v2))
    00365
    00367 #define __MM_MADD_PS(a, b, c)                                                       \
    00368     _mm_add_ps(_mm_mul_ps(a, b), c)
    00369
    00371 #define __MM_LERP_PS(t, a, b)                                                       \
    00372     __MM_MADD_PS(_mm_sub_ps(b, a), t, a)
    00373
    00375 #define __MM_MADD_SS(a, b, c)                                                       \
    00376     _mm_add_ss(_mm_mul_ss(a, b), c)
    00377
    00379 #define __MM_LERP_SS(t, a, b)                                                       \
    00380     __MM_MADD_SS(_mm_sub_ss(b, a), t, a)
    00381
    00383 #define __MM_LOAD_PS(p)                                                             \
    00384     (*(__m128*)(p))
    00385
    00387 #define __MM_STORE_PS(p, v)                                                         \
    00388     (*(__m128*)(p) = (v))
    00389
    00390
    00393     template <bool aligned = false>
    00394     struct SSEMemoryAccessor
    00395     {
    00396         static FORCEINLINE __m128 load(const float *p)
    00397         {
    00398             return _mm_loadu_ps(p);
    00399         }
    00400         static FORCEINLINE void store(float *p, const __m128& v)
    00401         {
    00402             _mm_storeu_ps(p, v);
    00403         }
    00404     };
    00405     // Special aligned accessor
    00406     template <>
    00407     struct SSEMemoryAccessor<true>
    00408     {
    00409         static FORCEINLINE const __m128& load(const float *p)
    00410         {
    00411             return __MM_LOAD_PS(p);
    00412         }
    00413         static FORCEINLINE void store(float *p, const __m128& v)
    00414         {
    00415             __MM_STORE_PS(p, v);
    00416         }
    00417     };
    00418
    00421     static FORCEINLINE bool _isAlignedForSSE(const void *p)
    00422     {
    00423         return (((size_t)p) & 15) == 0;
    00424     }
    00425
    00429     static FORCEINLINE __m128 __mm_rsqrt_nr_ps(const __m128& x)
    00430     {
    00431         static const __m128 v0pt5 = { 0.5f, 0.5f, 0.5f, 0.5f };
    00432         static const __m128 v3pt0 = { 3.0f, 3.0f, 3.0f, 3.0f };
    00433         __m128 t = _mm_rsqrt_ps(x);
    00434         return _mm_mul_ps(_mm_mul_ps(v0pt5, t),
    00435             _mm_sub_ps(v3pt0, _mm_mul_ps(_mm_mul_ps(x, t), t)));
    00436     }
    00437
    00438 // Macro to check the stack aligned for SSE
    00439 #if OGRE_DEBUG_MODE
    00440 #define __OGRE_CHECK_STACK_ALIGNED_FOR_SSE()        \
    00441     {                                               \
    00442         __m128 test;                                \
    00443         assert(_isAlignedForSSE(&test));            \
    00444     }
    00445
    00446 #else   // !OGRE_DEBUG_MODE
    00447 #define __OGRE_CHECK_STACK_ALIGNED_FOR_SSE()
    00448
    00449 #endif  // OGRE_DEBUG_MODE
    00450
    00451
    00452 #endif  // __OGRE_HAVE_SSE
    00453
    00454 }
    00455
    00456 #endif // __SIMDHelper_H__

Copyright © 2000-2005 by The OGRE Team
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.
Last modified Sat May 10 16:25:03 2008