Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

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 OGRE_CPU == OGRE_CPU_X86 && OGRE_COMPILER == OGRE_COMPILER_GNUC
00048 //
00049 // Horrible hack to align the stack to a 16-bytes boundary for gcc.
00050 //
00051 // We assume a gcc version >= 2.95 so that
00052 // -mpreferred-stack-boundary works.  Otherwise, all bets are
00053 // off.  However, -mpreferred-stack-boundary does not create a
00054 // stack alignment, but it only preserves it.  Unfortunately,
00055 // since Ogre are designed as a flexibility library, user might
00056 // compile their application with wrong stack alignment, even
00057 // if user taken care with stack alignment, but many versions
00058 // of libc on linux call main() with the wrong initial stack
00059 // alignment the result that the code is now pessimally aligned
00060 // instead of having a 50% chance of being correct.
00061 //
00062 #define __OGRE_SIMD_ALIGN_STACK()                                   \
00063     {                                                               \
00064         /* Use alloca to allocate some memory on the stack.  */     \
00065         /* This alerts gcc that something funny is going on, */     \
00066         /* so that it does not omit the frame pointer etc.   */     \
00067         (void)__builtin_alloca(16);                                 \
00068         /* Now align the stack pointer */                           \
00069         __asm__ __volatile__ ("andl $-16, %esp");                   \
00070     }
00071 
00072 #elif defined(__ICC)
00073 // For intel's compiler, simply calling alloca seems to do the right
00074 // thing. The size of the allocated block seems to be irrelevant.
00075 #define __OGRE_SIMD_ALIGN_STACK()   _alloca(16)
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 && OGRE_COMPILER == OGRE_COMPILER_MSVC
00089 #include "OgreNoMemoryMacros.h"
00090 #include <xmmintrin.h>
00091 #include "OgreMemoryMacros.h"
00092 
00093 #elif OGRE_DOUBLE_PRECISION == 0 && OGRE_CPU == OGRE_CPU_X86 && OGRE_COMPILER == OGRE_COMPILER_GNUC
00094 
00095 // Don't define ourself version SSE intrinsics if "xmmintrin.h" already included.
00096 //
00097 // Note: gcc in some platform already included "xmmintrin.h" for some reason.
00098 // I pick up macro _XMMINTRIN_H_INCLUDED here which based on the "xmmintrin.h"
00099 // comes with cygwin gcc 3.4.4, guess it should be solved duplicate definition
00100 // problem on gcc for x86.
00101 //
00102 #if !defined(_XMMINTRIN_H_INCLUDED)
00103 
00104 // Simulate VC/ICC intrinsics. Only used intrinsics are declared here.
00105 
00106 typedef float __m128 __attribute__ ((mode(V4SF),aligned(16)));
00107 typedef int __m64 __attribute__ ((mode(V2SI)));
00108 
00109 // Macro for declare intrinsic routines always inline even if in debug build
00110 #define __ALWAYS_INLINE    FORCEINLINE __attribute__ ((__always_inline__))
00111 
00112 // Shuffle instruction must be declare as macro
00113 
00114 #define _MM_SHUFFLE(fp3,fp2,fp1,fp0) \
00115     (((fp3) << 6) | ((fp2) << 4) | ((fp1) << 2) | ((fp0)))
00116 
00117 #define _mm_shuffle_ps(a, b, imm8) __extension__                                        \
00118     ({                                                                                  \
00119         __m128 result;                                                                  \
00120         __asm__("shufps %3, %2, %0" : "=x" (result) : "0" (a), "xm" (b), "N" (imm8));   \
00121         result;                                                                         \
00122     })
00123 
00124 
00125 // Load/store instructions
00126 
00127 #define __MM_DECL_LD(name, instruction, type)                               \
00128     static __ALWAYS_INLINE __m128 _mm_##name(const type *addr)              \
00129     {                                                                       \
00130         __m128 result;                                                      \
00131         __asm__( #instruction " %1, %0" : "=x" (result) : "m" (*addr));     \
00132         return result;                                                      \
00133     }
00134 
00135 #define __MM_DECL_LD2(name, instruction, type)                                      \
00136     static __ALWAYS_INLINE __m128 _mm_##name(__m128 val, const type *addr)          \
00137     {                                                                               \
00138         __m128 result;                                                              \
00139         __asm__( #instruction " %2, %0" : "=x" (result) : "0"(val), "m" (*addr));   \
00140         return result;                                                              \
00141     }
00142 
00143 #define __MM_DECL_ST(name, instruction, type)                               \
00144     static __ALWAYS_INLINE void _mm_##name(type *addr, __m128 val)          \
00145     {                                                                       \
00146         __asm__( #instruction " %1, %0" : "=m" (*addr) : "x" (val));        \
00147     }
00148 
00149 __MM_DECL_LD(loadu_ps, movups, float)
00150 __MM_DECL_ST(storeu_ps, movups, float)
00151 
00152 __MM_DECL_LD(load_ss, movss, float)
00153 __MM_DECL_ST(store_ss, movss, float)
00154 
00155 __MM_DECL_ST(storel_pi, movlps, __m64)
00156 __MM_DECL_ST(storeh_pi, movhps, __m64)
00157 __MM_DECL_LD2(loadl_pi, movlps, __m64)
00158 __MM_DECL_LD2(loadh_pi, movhps, __m64)
00159 
00160 #undef __MM_DECL_LD
00161 #undef __MM_DECL_LD2
00162 #undef __MM_DECL_ST
00163 
00164 // Two operand instructions
00165 
00166 #define __MM_DECL_OP2(name, instruction, constraint)                                    \
00167     static __ALWAYS_INLINE __m128 _mm_##name(__m128 a, __m128 b)                        \
00168     {                                                                                   \
00169         __m128 result;                                                                  \
00170         __asm__( #instruction " %2, %0" : "=x" (result) : "0" (a), #constraint (b));    \
00171         return result;                                                                  \
00172     }
00173 
00174 __MM_DECL_OP2(add_ps, addps, xm)
00175 __MM_DECL_OP2(add_ss, addss, xm)
00176 __MM_DECL_OP2(sub_ps, subps, xm)
00177 __MM_DECL_OP2(sub_ss, subss, xm)
00178 __MM_DECL_OP2(mul_ps, mulps, xm)
00179 __MM_DECL_OP2(mul_ss, mulss, xm)
00180 
00181 __MM_DECL_OP2(xor_ps, xorps, xm)
00182 
00183 __MM_DECL_OP2(unpacklo_ps, unpcklps, xm)
00184 __MM_DECL_OP2(unpackhi_ps, unpckhps, xm)
00185 
00186 __MM_DECL_OP2(movehl_ps, movhlps, x)
00187 __MM_DECL_OP2(movelh_ps, movlhps, x)
00188 
00189 __MM_DECL_OP2(cmpnle_ps, cmpnleps, xm)
00190 
00191 #undef __MM_DECL_OP2
00192 
00193 // Other used instructions
00194 
00195     static __ALWAYS_INLINE __m128 _mm_load_ps1(const float *addr)
00196     {
00197         __m128 tmp = _mm_load_ss(addr);
00198         return _mm_shuffle_ps(tmp, tmp, 0);
00199     }
00200 
00201     static __ALWAYS_INLINE __m128 _mm_setzero_ps(void)
00202     {
00203         __m128 result;
00204         __asm__("xorps %0, %0" : "=x" (result));
00205         return result;
00206     }
00207 
00208     static __ALWAYS_INLINE __m128 _mm_rsqrt_ps(__m128 val)
00209     {
00210         __m128 result;
00211         __asm__("rsqrtps %1, %0" : "=x" (result) : "xm" (val));
00212         //__asm__("rsqrtps %0, %0" : "=x" (result) : "0" (val));
00213         return result;
00214     }
00215 
00216     static __ALWAYS_INLINE int _mm_movemask_ps(__m128 val)
00217     {
00218         int result;
00219         __asm__("movmskps %1, %0" : "=r" (result) : "x" (val));
00220         return result;
00221     }
00222 
00223 #endif // !defined(_XMMINTRIN_H_INCLUDED)
00224 
00225 #endif // OGRE_DOUBLE_PRECISION == 0 && OGRE_CPU == OGRE_CPU_X86 && OGRE_COMPILER == OGRE_COMPILER_MSVC
00226 
00227 
00228 
00229 //---------------------------------------------------------------------
00230 // SIMD macros and helpers
00231 //---------------------------------------------------------------------
00232 
00233 
00234 namespace Ogre {
00235 
00236 #if __OGRE_HAVE_SSE
00237 
00248 #if 1
00249 #define __MM_RSQRT_PS(x)    _mm_rsqrt_ps(x)
00250 #else
00251 #define __MM_RSQRT_PS(x)    __mm_rsqrt_nr_ps(x) // Implemented below
00252 #endif
00253 
00262 #define __MM_TRANSPOSE4x4_PS(r0, r1, r2, r3)                                        \
00263     {                                                                               \
00264         __m128 t3, t2, t1, t0;                                                      \
00265                                                                                     \
00266                                                             /* r00 r01 r02 r03 */   \
00267                                                             /* r10 r11 r12 r13 */   \
00268                                                             /* r20 r21 r22 r23 */   \
00269                                                             /* r30 r31 r32 r33 */   \
00270                                                                                     \
00271         t0 = _mm_unpacklo_ps(r0, r1);                       /* r00 r10 r01 r11 */   \
00272         t2 = _mm_unpackhi_ps(r0, r1);                       /* r02 r12 r03 r13 */   \
00273         t1 = _mm_unpacklo_ps(r2, r3);                       /* r20 r30 r21 r31 */   \
00274         t3 = _mm_unpackhi_ps(r2, r3);                       /* r22 r32 r23 r33 */   \
00275                                                                                     \
00276         r0 = _mm_movelh_ps(t0, t1);                         /* r00 r10 r20 r30 */   \
00277         r1 = _mm_movehl_ps(t1, t0);                         /* r01 r11 r21 r31 */   \
00278         r2 = _mm_movelh_ps(t2, t3);                         /* r02 r12 r22 r32 */   \
00279         r3 = _mm_movehl_ps(t3, t2);                         /* r03 r13 r23 r33 */   \
00280     }
00281 
00290 #define __MM_TRANSPOSE4x3_PS(v0, v1, v2)                                            \
00291     {                                                                               \
00292         __m128 t0, t1, t2;                                                          \
00293                                                                                     \
00294                                                             /* r00 r01 r02 r10 */   \
00295                                                             /* r11 r12 r20 r21 */   \
00296                                                             /* r22 r30 r31 r32 */   \
00297                                                                                     \
00298         t0 = _mm_shuffle_ps(v0, v2, _MM_SHUFFLE(3,0,3,0));  /* r00 r10 r22 r32 */   \
00299         t1 = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(1,0,2,1));  /* r01 r02 r11 r12 */   \
00300         t2 = _mm_shuffle_ps(v1, v2, _MM_SHUFFLE(2,1,3,2));  /* r20 r21 r30 r31 */   \
00301                                                                                     \
00302         v0 = _mm_shuffle_ps(t0, t2, _MM_SHUFFLE(2,0,1,0));  /* r00 r10 r20 r30 */   \
00303         v1 = _mm_shuffle_ps(t1, t2, _MM_SHUFFLE(3,1,2,0));  /* r01 r11 r21 r31 */   \
00304         v2 = _mm_shuffle_ps(t1, t0, _MM_SHUFFLE(3,2,3,1));  /* r02 r12 r22 r32 */   \
00305     }
00306 
00314 #define __MM_TRANSPOSE3x4_PS(v0, v1, v2)                                            \
00315     {                                                                               \
00316         __m128 t0, t1, t2;                                                          \
00317                                                                                     \
00318                                                             /* r00 r10 r20 r30 */   \
00319                                                             /* r01 r11 r21 r31 */   \
00320                                                             /* r02 r12 r22 r32 */   \
00321                                                                                     \
00322         t0 = _mm_shuffle_ps(v0, v2, _MM_SHUFFLE(2,0,3,1));  /* r10 r30 r02 r22 */   \
00323         t1 = _mm_shuffle_ps(v1, v2, _MM_SHUFFLE(3,1,3,1));  /* r11 r31 r12 r32 */   \
00324         t2 = _mm_shuffle_ps(v0, v1, _MM_SHUFFLE(2,0,2,0));  /* r00 r20 r01 r21 */   \
00325                                                                                     \
00326         v0 = _mm_shuffle_ps(t2, t0, _MM_SHUFFLE(0,2,2,0));  /* r00 r01 r02 r10 */   \
00327         v1 = _mm_shuffle_ps(t1, t2, _MM_SHUFFLE(3,1,2,0));  /* r11 r12 r20 r21 */   \
00328         v2 = _mm_shuffle_ps(t0, t1, _MM_SHUFFLE(3,1,1,3));  /* r22 r30 r31 r32 */   \
00329     }
00330 
00334 #define __MM_SELECT(v, fp)                                                          \
00335     _mm_shuffle_ps((v), (v), _MM_SHUFFLE((fp),(fp),(fp),(fp)))
00336 
00338 #define __MM_ACCUM4_PS(a, b, c, d)                                                  \
00339     _mm_add_ps(_mm_add_ps(a, b), _mm_add_ps(c, d))
00340 
00344 #define __MM_DOT4x4_PS(a0, a1, a2, a3, b0, b1, b2, b3)                              \
00345     __MM_ACCUM4_PS(_mm_mul_ps(a0, b0), _mm_mul_ps(a1, b1), _mm_mul_ps(a2, b2), _mm_mul_ps(a3, b3))
00346 
00350 #define __MM_DOT4x3_PS(r0, r1, r2, r3, v0, v1, v2)                                  \
00351     __MM_ACCUM4_PS(_mm_mul_ps(r0, v0), _mm_mul_ps(r1, v1), _mm_mul_ps(r2, v2), r3)
00352 
00354 #define __MM_ACCUM3_PS(a, b, c)                                                     \
00355     _mm_add_ps(_mm_add_ps(a, b), c)
00356 
00360 #define __MM_DOT3x3_PS(r0, r1, r2, v0, v1, v2)                                      \
00361     __MM_ACCUM3_PS(_mm_mul_ps(r0, v0), _mm_mul_ps(r1, v1), _mm_mul_ps(r2, v2))
00362 
00364 #define __MM_MADD_PS(a, b, c)                                                       \
00365     _mm_add_ps(_mm_mul_ps(a, b), c)
00366 
00368 #define __MM_LERP_PS(t, a, b)                                                       \
00369     __MM_MADD_PS(_mm_sub_ps(b, a), t, a)
00370 
00372 #define __MM_MADD_SS(a, b, c)                                                       \
00373     _mm_add_ss(_mm_mul_ss(a, b), c)
00374 
00376 #define __MM_LERP_SS(t, a, b)                                                       \
00377     __MM_MADD_SS(_mm_sub_ss(b, a), t, a)
00378 
00380 #define __MM_LOAD_PS(p)                                                             \
00381     (*(__m128*)(p))
00382 
00384 #define __MM_STORE_PS(p, v)                                                         \
00385     (*(__m128*)(p) = (v))
00386 
00387 
00390     template <bool aligned = false>
00391     struct SSEMemoryAccessor
00392     {
00393         static FORCEINLINE __m128 load(const float *p)
00394         {
00395             return _mm_loadu_ps(p);
00396         }
00397         static FORCEINLINE void store(float *p, const __m128& v)
00398         {
00399             _mm_storeu_ps(p, v);
00400         }
00401     };
00402     // Special aligned accessor
00403     template <>
00404     struct SSEMemoryAccessor<true>
00405     {
00406         static FORCEINLINE const __m128& load(const float *p)
00407         {
00408             return __MM_LOAD_PS(p);
00409         }
00410         static FORCEINLINE void store(float *p, const __m128& v)
00411         {
00412             __MM_STORE_PS(p, v);
00413         }
00414     };
00415 
00418     static FORCEINLINE bool _isAlignedForSSE(const void *p)
00419     {
00420         return (((size_t)p) & 15) == 0;
00421     }
00422 
00426     static FORCEINLINE __m128 __mm_rsqrt_nr_ps(const __m128& x)
00427     {
00428         static const __m128 v0pt5 = { 0.5f, 0.5f, 0.5f, 0.5f };
00429         static const __m128 v3pt0 = { 3.0f, 3.0f, 3.0f, 3.0f };
00430         __m128 t = _mm_rsqrt_ps(x);
00431         return _mm_mul_ps(_mm_mul_ps(v0pt5, t),
00432             _mm_sub_ps(v3pt0, _mm_mul_ps(_mm_mul_ps(x, t), t)));
00433     }
00434 
00435 // Macro to check the stack aligned for SSE
00436 #if OGRE_DEBUG_MODE
00437 #define __OGRE_CHECK_STACK_ALIGNED_FOR_SSE()        \
00438     {                                               \
00439         __m128 test;                                \
00440         assert(_isAlignedForSSE(&test));            \
00441     }
00442 
00443 #else   // !OGRE_DEBUG_MODE
00444 #define __OGRE_CHECK_STACK_ALIGNED_FOR_SSE()
00445 
00446 #endif  // OGRE_DEBUG_MODE
00447 
00448 
00449 #endif  // __OGRE_HAVE_SSE
00450 
00451 }
00452 
00453 #endif // __SIMDHelper_H__

Copyright © 2000-2005 by The OGRE Team
Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License.
Last modified Sun Jun 10 10:35:49 2007