[C] zintrin.h: 智能引入intrinsic函數 V1.01版。改進對Mac OS X的支持,增加INTRIN_WORDSIZE宏


新版本——
http://www.cnblogs.com/zyl910/archive/2012/11/07/zintrin_v102.html
[C] zintrin.h: 智能引入intrinsic函數 V1.02版。支持VC2012,增加INTRIN_ALIGN、INTRIN_COMPILER_NAME宏

 

作者:zyl910

  之前的zintrin V1.00版對Mac OS X平台支持性不佳。現在的V1.01版改進對Mac OS X的支持,還做了這些改進——增加INTRIN_WORDSIZE宏 等。


一、更新說明

1.1 改進對Mac OS X的支持

  前幾天在使用Xcode時,發現mac下也有intrinsic頭文件,詳見http://www.cnblogs.com/zyl910/archive/2012/09/27/intrin_mac.html
  但是在終端中調用gcc編譯時,總是報告找不到cpuid.h、x86intrin.h等文件。
  后來才發現,“使用Xcode” 與 “終端中調用gcc”這兩種情況下,編譯器的include目錄是不同的——
使用Xcode:/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/4.0/include
終端中調用gcc:/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/include

  “/usr/llvm-gcc-4.2/lib/gcc/i686-apple-darwin11/4.2.1/include”文件夾下的intrinsic頭文件版本較老,沒有提供cpuid.h、x86intrin.h等新版本的文件。

  該怎么辦呢?
  方案一:用宏判斷gcc的版本,然后引入最高支持的intrinsic頭文件。
  方案二:強制要求用戶配置好include目錄。
  方案三:根據__MMX__等宏判斷當前編譯器是否啟用該指令集,然后引入相應文件。

  方案一不行。因為現在gcc的版本一樣,只是include目錄不同。
  方案二也不好。因為這樣給使用帶來了麻煩。
  方案三不錯。按需引入對應的文件。當發現啟用AVX等新指令集時,x86intrin.h應該是可用的。


1.2 增加INTRIN_WORDSIZE宏

  某些intrinsic函數僅在64位下可用,例如_mm_popcnt_u64。所以需要一種能判斷目標機器字長的辦法。

  一般情況下,指針變量的長度就是機器字長,但是sizeof運算符不能用於宏預處理。

  這是可以利用C99的stdint.h,它提供了一系列宏用於判斷各種數據類型的范圍。其中有PTRDIFF_MAX,我們可以利用它來判斷機器字長——

// INTRIN_WORDSIZE: 目標機器的字長.
#if PTRDIFF_MAX >= INT64_MAX
    #define INTRIN_WORDSIZE    64
#elif PTRDIFF_MAX >= INT32_MAX
    #define INTRIN_WORDSIZE    32
#else
    #define INTRIN_WORDSIZE    16
#endif

 

  有一點需要注意,根據C99標准——
對於純C程序來說,PTRDIFF_MAX等宏默認是啟用的。
但對於C++程序,PTRDIFF_MAX等宏默認不啟用。只有在引入stdint.h之前定義了__STDC_LIMIT_MACROS,才會啟用這些宏。

  當發現沒有PTRDIFF_MAX等宏時,可以這樣報告錯誤——

#include "stdint.h"
#if !defined(PTRDIFF_MAX) || !defined(INT32_MAX)
#error Need C99 marcos: __STDC_LIMIT_MACROS.
#endif

 


1.3 其他改進

  gcc不再引入cpuid.h。這是因為難以判斷cpuid.h是否存在。而使用ccpuid模塊能更方便的判斷cpu指令集。

  不再假定未來VC版本支持AVX2等指令集。這是為了避免潛在的錯誤,寧缺毋濫。

  調整了一下INTRIN_常數的順序。


二、全部代碼

2.1 zintrin.h

  全部代碼——

View Code
#ifndef __ZINTRIN_H_INCLUDED
#define __ZINTRIN_H_INCLUDED

#include "stdint.h"
#if !defined(PTRDIFF_MAX) || !defined(INT32_MAX)
#error Need C99 marcos: __STDC_LIMIT_MACROS.
#endif

// INTRIN_WORDSIZE: 目標機器的字長.
#if PTRDIFF_MAX >= INT64_MAX
    #define INTRIN_WORDSIZE    64
#elif PTRDIFF_MAX >= INT32_MAX
    #define INTRIN_WORDSIZE    32
#else
    #define INTRIN_WORDSIZE    16
#endif


// 根據不同的編譯器做不同的處理.
#if defined(__GNUC__)    // GCC
    #if (defined(__i386__) || defined(__x86_64__) )
        // header files
        //#include <cpuid.h>    // mac下有時找不到. 於是放棄, 使用ccpuid模塊會更方便.
        //#include <x86intrin.h>    // mac下有時找不到. 於是根據宏來加載頭文件.
        // macros
        #ifdef __MMX__
            #define INTRIN_MMX    1
            #include <mmintrin.h>
        #endif
        #ifdef __3dNOW__
            #define INTRIN_3dNOW    1
            #include <mm3dnow.h>
        #endif
        #ifdef __SSE__
            #define INTRIN_SSE    1
            #include <xmmintrin.h>
        #endif
        #ifdef __SSE2__
            #define INTRIN_SSE2    1
            #include <emmintrin.h>
        #endif
        #ifdef __SSE3__
            #define INTRIN_SSE3    1
            #include <pmmintrin.h>
        #endif
        #ifdef __SSSE3__
            #define INTRIN_SSSE3    1
            #include <tmmintrin.h>
        #endif
        #ifdef __SSE4_1__
            #define INTRIN_SSE4_1    1
            #include <smmintrin.h>
        #endif
        #ifdef __SSE4_2__
            #define INTRIN_SSE4_2    1
            #include <nmmintrin.h>
        #endif
        #ifdef __SSE4A__
            #define INTRIN_SSE4A    1
            #include <ammintrin.h>
        #endif
        #ifdef __AES__
            #define INTRIN_AES    1
            #include <x86intrin.h>
        #endif
        #ifdef __PCLMUL__
            #define INTRIN_PCLMUL    1
            #include <x86intrin.h>
        #endif
        #ifdef __AVX__
            #define INTRIN_AVX    1
            #include <x86intrin.h>
        #endif
        #ifdef __AVX2__
            #define INTRIN_AVX2    1
            #include <x86intrin.h>
        #endif
        #ifdef __F16C__
            #define INTRIN_F16C    1
            #include <x86intrin.h>
        #endif
        #ifdef __FMA__
            #define INTRIN_FMA    1
            #include <x86intrin.h>
        #endif
        #ifdef __FMA4__
            #define INTRIN_FMA4    1
            #include <x86intrin.h>
        #endif
        #ifdef __XOP__
            #define INTRIN_XOP    1
            #include <xopintrin.h>
        #endif
        #ifdef __LWP__
            #define INTRIN_LWP    1
            #include <x86intrin.h>
        #endif
        #ifdef __RDRND__
            #define INTRIN_RDRND    1
            #include <x86intrin.h>
        #endif
        #ifdef __FSGSBASE__
            #define INTRIN_FSGSBASE    1
            #include <x86intrin.h>
        #endif
        #ifdef __POPCNT__
            #define INTRIN_POPCNT    1
            #include <popcntintrin.h>
        #endif
        #ifdef __LZCNT__
            #define INTRIN_LZCNT    1
            #include <x86intrin.h>
        #endif
        #ifdef __TBM__
            #define INTRIN_TBM    1
            #include <x86intrin.h>
        #endif
        #ifdef __BMI__
            #define INTRIN_BMI    1
            #include <x86intrin.h>
        #endif
        #ifdef __BMI2__
            #define INTRIN_BMI2    1
            #include <x86intrin.h>
        #endif

    #endif    //#if (defined(__i386__) || defined(__x86_64__) )

#elif defined(_MSC_VER)    // MSVC
    // header files
    #if _MSC_VER >=1400    // VC2005
        #include <intrin.h>
    #elif _MSC_VER >=1200    // VC6
        #if (defined(_M_IX86) || defined(_M_X64))
            #include <emmintrin.h>    // MMX, SSE, SSE2
            #include <mm3dnow.h>    // 3DNow!
        #endif
    #endif    // #if _MSC_VER >=1400
    #include <malloc.h>    // _mm_malloc, _mm_free.

    // macros
    #if (defined(_M_IX86) || defined(_M_X64))
        #if _MSC_VER >=1200    // VC6
            #if defined(_M_X64) && !defined(__INTEL_COMPILER)
                // VC編譯器不支持64位下的MMX.
            #else
                #define INTRIN_MMX    1    // mmintrin.h
                #define INTRIN_3dNOW    1    // mm3dnow.h
            #endif
            #define INTRIN_SSE    1    // xmmintrin.h
            #define INTRIN_SSE2    1    // emmintrin.h
        #endif
        #if _MSC_VER >=1300    // VC2003
        #endif
        #if _MSC_VER >=1400    // VC2005
        #endif
        #if _MSC_VER >=1500    // VC2008
            #define INTRIN_SSE3    1    // pmmintrin.h
            #define INTRIN_SSSE3    1    // tmmintrin.h
            #define INTRIN_SSE4_1    1    // smmintrin.h
            #define INTRIN_SSE4_2    1    // nmmintrin.h
            #define INTRIN_POPCNT    1    // nmmintrin.h
            #define INTRIN_SSE4A    1    // intrin.h
            #define INTRIN_LZCNT    1    // intrin.h
        #endif
        #if _MSC_VER >=1600    // VC2010
            #define INTRIN_AES    1    // wmmintrin.h
            #define INTRIN_PCLMUL    1    // wmmintrin.h
            #define INTRIN_AVX    1    // immintrin.h
            #define INTRIN_FMA4    1    // ammintrin.h
            #define INTRIN_XOP    1    // ammintrin.h
            #define INTRIN_LWP    1    // ammintrin.h
        #endif
        #if _MSC_VER >=1700    // VC2012
            //#define INTRIN_AVX2    1    //TODO:待查證, 注釋掉.
            //#define INTRIN_FMA    1
            //#define INTRIN_F16C    1
            //#define INTRIN_RDRND    1
            //#define INTRIN_FSGSBASE    1
            //#define INTRIN_TBM    1
            //#define INTRIN_BMI    1
            //#define INTRIN_BMI2    1
        #endif
    #endif
    //TODO:待查證 VS配合intel C編譯器時intrin函數的支持性.

    // VC2008之前沒有_mm_cvtss_f32
    #if _MSC_VER <1500    // VC2008
        // float _mm_cvtss_f32(__m128 _A);
        #ifndef _mm_cvtss_f32
            #define _mm_cvtss_f32(__m128_A) ( *(float*)(void*)&(__m128_A) )
        #endif
    #endif

#else
//#error Only supports GCC or MSVC.
#endif    // #if defined(__GNUC__)


#endif    // #ifndef __ZINTRIN_H_INCLUDED

 


2.2 testzintrin.c

  全部代碼——

View Code
#define __STDC_LIMIT_MACROS    1    // C99整數范圍常量. [僅演示, 純C程序可以不用, 而C++程序必須定義該宏.]

#include <stdio.h>

#include "zintrin.h"

#define PT_MAKE_STR(x)    { #x, PT_MAKE_STR_ESC(x) }
#define PT_MAKE_STR_ESC(x)    #x

typedef struct tagMACRO_T
{
    const char *name;
    const char *value;
} MACRO_T;

/* Intrinsics */
const MACRO_T g_intrins[] =
{
    {"[Intrinsics]", ""},

#ifdef INTRIN_MMX
    PT_MAKE_STR(INTRIN_MMX),
#endif

#ifdef INTRIN_3dNOW
    PT_MAKE_STR(INTRIN_3dNOW),
#endif

#ifdef INTRIN_SSE
    PT_MAKE_STR(INTRIN_SSE),
#endif

#ifdef INTRIN_SSE2
    PT_MAKE_STR(INTRIN_SSE2),
#endif

#ifdef INTRIN_SSE3
    PT_MAKE_STR(INTRIN_SSE3),
#endif

#ifdef INTRIN_SSSE3
    PT_MAKE_STR(INTRIN_SSSE3),
#endif

#ifdef INTRIN_SSE4_1
    PT_MAKE_STR(INTRIN_SSE4_1),
#endif

#ifdef INTRIN_SSE4_2
    PT_MAKE_STR(INTRIN_SSE4_2),
#endif

#ifdef INTRIN_SSE4A
    PT_MAKE_STR(INTRIN_SSE4A),
#endif

#ifdef INTRIN_AES
    PT_MAKE_STR(INTRIN_AES),
#endif

#ifdef INTRIN_PCLMUL
    PT_MAKE_STR(INTRIN_PCLMUL),
#endif

#ifdef INTRIN_AVX
    PT_MAKE_STR(INTRIN_AVX),
#endif

#ifdef INTRIN_AVX2
    PT_MAKE_STR(INTRIN_AVX2),
#endif

#ifdef INTRIN_F16C
    PT_MAKE_STR(INTRIN_F16C),
#endif

#ifdef INTRIN_FMA
    PT_MAKE_STR(INTRIN_FMA),
#endif

#ifdef INTRIN_FMA4
    PT_MAKE_STR(INTRIN_FMA4),
#endif

#ifdef INTRIN_XOP
    PT_MAKE_STR(INTRIN_XOP),
#endif

#ifdef INTRIN_LWP
    PT_MAKE_STR(INTRIN_LWP),
#endif

#ifdef INTRIN_RDRND
    PT_MAKE_STR(INTRIN_RDRND),
#endif

#ifdef INTRIN_FSGSBASE
    PT_MAKE_STR(INTRIN_FSGSBASE),
#endif

#ifdef INTRIN_POPCNT
    PT_MAKE_STR(INTRIN_POPCNT),
#endif

#ifdef INTRIN_LZCNT
    PT_MAKE_STR(INTRIN_LZCNT),
#endif

#ifdef INTRIN_TBM
    PT_MAKE_STR(INTRIN_TBM),
#endif

#ifdef INTRIN_BMI
    PT_MAKE_STR(INTRIN_BMI),
#endif

#ifdef INTRIN_BMI2
    PT_MAKE_STR(INTRIN_BMI2),
#endif

};


//// 獲取程序位數(被編譯為多少位的代碼)
//int GetProgramBits(void)
//{
//    return sizeof(int*) * 8;
//}

void print_MACRO_T(const MACRO_T* pArray, int cnt)
{
    int i;
    for( i = 0; i < cnt; ++i )
    {
        printf( "%s\t%s\n", pArray[i].name, pArray[i].value );
    }
    printf( "\n" );
}


int main(int argc, char* argv[])
{

    //printf("testzintrin v1.00 (%dbit)\n\n", GetProgramBits());
    printf("testzintrin v1.01 (%dbit)\n\n", INTRIN_WORDSIZE);
    print_MACRO_T(g_intrins, sizeof(g_intrins)/sizeof(g_intrins[0]));

    // _mm_malloc
#ifdef INTRIN_SSE
    if(1)
    {
        void* p;
        p = _mm_malloc(0x10, 0x10);
        printf("_mm_malloc:\t%ph\n", p);
        _mm_free(p);
    }
#endif

    // mmx
#ifdef INTRIN_MMX
    _mm_empty();
#endif

    // 3DNow!
#ifdef INTRIN_3dNOW
    //_m_femms();    // AMD cpu only.
#endif

    // sse
#ifdef INTRIN_SSE
    if(1)
    {
        __m128 xmm1;
        float f;
        printf("&xmm1:\t%ph\n", &xmm1);
        xmm1 = _mm_setzero_ps();    // SSE instruction: xorps
        f = _mm_cvtss_f32(xmm1);
        printf("_mm_cvtss_f32:\t%f\n", f);
    }
#endif

    // popcnt
#ifdef INTRIN_POPCNT
    printf("popcnt(0xffffffffu):\t%u\n", _mm_popcnt_u32(0xffffffffu));
    #if INTRIN_WORDSIZE>=64
        printf("popcnt(0xffffffffffffffffull):\t%u\n", (int)_mm_popcnt_u64(0xffffffffffffffffull));
    #endif
#endif

    return 0;
}

 


2.3 makefile

  全部代碼——

View Code
# flags
CC = gcc
CFS = -Wall
LFS = 

# args
RELEASE =0
BITS =
CFLAGS = -msse

# [args] 生成模式. 0代表debug模式, 1代表release模式. make RELEASE=1.
ifeq ($(RELEASE),0)
    # debug
    CFS += -g
else
    # release
    CFS += -static -O3 -DNDEBUG
    LFS += -static
endif

# [args] 程序位數. 32代表32位程序, 64代表64位程序, 其他默認. make BITS=32.
ifeq ($(BITS),32)
    CFS += -m32
    LFS += -m32
else
    ifeq ($(BITS),64)
        CFS += -m64
        LFS += -m64
    else
    endif
endif

# [args] 使用 CFLAGS 添加新的參數. make CFLAGS="-mpopcnt -msse4a".
CFS += $(CFLAGS)


.PHONY : all clean

# files
TARGETS = testzintrin
OBJS = testzintrin.o

all : $(TARGETS)

testzintrin : $(OBJS)
    $(CC) $(LFS) -o $@ $^


testzintrin.o : testzintrin.c zintrin.h
    $(CC) $(CFS) -c $<


clean :
    rm -f $(OBJS) $(TARGETS) $(addsuffix .exe,$(TARGETS))

 


三、測試

  在以下編譯器中成功編譯——
VC6:x86版。
VC2003:x86版。
VC2005:x86版、x64版。
VC2010:x86版、x64版。
GCC 4.7.0(Fedora 17 x64):x86版、x64版。
GCC 4.6.2(MinGW(20120426)):x86版。
GCC 4.6.1(TDM-GCC(MinGW-w64)):x86版、x64版。
llvm-gcc-4.2(Mac OS X Lion 10.7.4, Xcode 4.4.1):x86版、x64版。

 

參考文獻——
《ISO/IEC 9899:1999 (C99)》。ISO/IEC,1999。www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
《Predefined Macros》. http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.110).aspx
《[GCC] The C Preprocessor》中的《3.7.2 Common Predefined Macros》. GNU, 2011. http://gcc.gnu.org/onlinedocs/cpp/Predefined-Macros.html
《兼容C99標准的stdint.h》. http://www.cnblogs.com/zyl910/archive/2012/08/08/c99int.html
《Intrinsics頭文件與SIMD指令集、Visual Studio版本對應表》. http://www.cnblogs.com/zyl910/archive/2012/02/28/vs_intrin_table.html
《GCC中的Intrinsics頭文件與SIMD指令集、宏、參數的對應表》. http://www.cnblogs.com/zyl910/archive/2012/08/27/intrin_table_gcc.html
《發現Mac OS X的llvm-gcc也是支持intrin函數的》. http://www.cnblogs.com/zyl910/archive/2012/09/27/intrin_mac.html
《[C] zintrin.h : 智能引入intrinsic函數。支持VC、GCC,兼容Windows、Linux、Mac OS X》. http://www.cnblogs.com/zyl910/archive/2012/09/23/zintrin.html

 

源碼下載——
http://files.cnblogs.com/zyl910/zintrin_v101.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM