作者:zyl910。

  现在很多编译器支持intrinsic函数,这给编写SSE等SIMD代码带来了方便。但是各个编译器略有差异,于是我编写了zintrin.h,智能引入intrinsic函数。

一、各种编译器的区别

1.1 Visual C++(Windows)

  最早支持intrinsic函数的VC编译器是VC 6.0。它在装上Visual Studio 6.0 Service Pack 5、Visual C++ 6.0 Processor Pack这两个补丁后,便提供了mmintrin.h、mm3dnow.h、xmmintrin.h、emmintrin.h,用于支持MMX、3DNow!、SSE、SSE2的intrinsic函数。
  从VC2005开始,提供了intrin.h,用于引入所有的intrinsic函数。
  详见——
http://www.cnblogs.com/zyl910/archive/2012/02/28/vs_intrin_table.html
Intrinsics头文件与SIMD指令集、Visual Studio版本对应表

  如果希望得知当前编译环境是否支持某种intrinsic函数,只能利用_MSC_VER判断VC的版本 来间接确认。
  而且实际过程中会发现一些兼容性小问题,例如——VC不支持x64下的MMX指令、VC2008之前没有_mm_cvtss_f32 等。

1.2 GCC(Linux下的GCC、Windows下的MinGW)

  gcc也支持intrinsic函数。例如在Fedora 17中,“/usr/lib/gcc/i686-redhat-linux/4.7.0/include/”目录下有Intrinsics头文件。而对于Windows中的MinGW,Intrinsics头文件是在MinGW的“\lib\gcc\mingw32\4.6.2\include”子目录中。
  详见——
http://www.cnblogs.com/zyl910/archive/2012/08/27/intrin_table_gcc.html
GCC中的Intrinsics头文件与SIMD指令集、宏、参数的对应表

  gcc允许通过命令行参数来控制是否打开某种指令集的支持,例如“-mmmx”用于打开MMX支持。可在终端中执行“gcc --target-help”,得到详细的参数列表。

  当通过命令行参数打开指令集支持后,gcc会自动定义对应的预处理宏。例如用“-mmmx”打开MMX支持后,gcc会自动定义“__MMX__”这个预定义宏。这一类的预定义宏有——
__MMX__
__3dNOW__
__SSE__
__SSE2__
__SSE3__
__SSSE3__
__SSE4_1__
__SSE4_2__
__SSE4A__
__AES__
__PCLMUL__
__AVX__
__AVX2__
__F16C__
__FMA__
__FMA4__
__XOP__
__LWP__
__RDRND__
__FSGSBASE__
__LZCNT__
__POPCNT__
__BMI__
__BMI2__
__TBM__

  gcc用于引入所有x86平台intrinsic函数的头文件是“x86intrin.h”,它会根据那些指令集预定义宏来引入相关的intrinsic函数。例如有“__MMX__”宏时,x86intrin.h会引入MMX的intrinsic函数。

1.3 Mac OS X 中的 llvm-gcc

  我在Mac OS X系统中找了很久,貌似它不支持intrinsic函数。详细版本是——
操作系统:Mac OS X Lion 10.7.4(11E53)
编程工具:Xcode 4.4.1(1448),并装好了它的“Command Line Tools”。

  __llvm__这个预定义宏可用来判断是不是llvm-gcc。

二、设计

2.1 思路

  不同的编译器引入intrinsic函数的办法——
对于VS2005之前的版本,只能手动逐个逐个的包含emmintrin.h、mm3dnow.h;
对于VS2005之后的版本,可以利用intrin.h引入所有intrinsic函数;
对于GCC,首先应该判断__llvm__宏来排除llvm-gcc,然后利用x86intrin.h引入所有intrinsic函数。

  这样做太麻烦了,我想编写一个头文件智能引入intrinsic函数。这就是zintrin.h。

  其次——
VC中,没有直接判断是否支持某种intrinsic函数的办法,只能利用_MSC_VER判断VC的版本 来间接确认。而且还有x64环境下不支持MMX等特殊情况。
对于GCC,虽然可以利用__MMX__等宏判断是否打开了指令集支持,但这并不代表支持intrinsic函数。例如Mac OS X 中的 llvm-gcc,默认打开了__MMX__、__SSE__、__SSE2__,但它不支持intrinsic函数。

  于是我想,如果有一种统一的方式判断当前编译环境是否支持某种intrinsic函数的办法就好了。

2.2 功能说明

  功能——
1. 引入了编译器支持的所有intrinsic函数.
2. 提供了 INTRIN_MMX 等一系列宏用于判断当前编译环境是否支持该intrin函数.
3. 兼容性补丁. 例如 _mm_cvtss_f32 等.

  用于判断当前编译环境是否支持该intrin函数的宏:
INTRIN_MMX
INTRIN_3dNOW
INTRIN_SSE
INTRIN_SSE2
INTRIN_SSE3
INTRIN_SSSE3
INTRIN_SSE4_1
INTRIN_SSE4_2
INTRIN_SSE4A
INTRIN_AES
INTRIN_PCLMUL
INTRIN_AVX
INTRIN_AVX2
INTRIN_F16C
INTRIN_FMA
INTRIN_FMA4
INTRIN_XOP
INTRIN_LWP
INTRIN_RDRND
INTRIN_FSGSBASE
INTRIN_LZCNT
INTRIN_POPCNT
INTRIN_BMI
INTRIN_BMI2
INTRIN_TBM

三、源码

3.1 zintrin.h

  全部代码——

#ifndef __ZINTRIN_H_INCLUDED
#define __ZINTRIN_H_INCLUDED
// 根据不同的编译器做不同的处理.
#if defined(__GNUC__)   // GCC
#if (defined(__i386__) || defined(__x86_64__) ) && !defined(__llvm__)   // Mac OS X 的 llvm 不支持 intrin 函数.
// header files
#include <x86intrin.h>
#include <cpuid.h>
// macros
#ifdef __MMX__
#define INTRIN_MMX  1
#endif
#ifdef __3dNOW__
#define INTRIN_3dNOW    1
#endif
#ifdef __SSE__
#define INTRIN_SSE  1
#endif
#ifdef __SSE2__
#define INTRIN_SSE2 1
#endif
#ifdef __SSE3__
#define INTRIN_SSE3 1
#endif
#ifdef __SSSE3__
#define INTRIN_SSSE3    1
#endif
#ifdef __SSE4_1__
#define INTRIN_SSE4_1   1
#endif
#ifdef __SSE4_2__
#define INTRIN_SSE4_2   1
#endif
#ifdef __SSE4A__
#define INTRIN_SSE4A    1
#endif
#ifdef __AES__
#define INTRIN_AES  1
#endif
#ifdef __PCLMUL__
#define INTRIN_PCLMUL   1
#endif
#ifdef __AVX__
#define INTRIN_AVX  1
#endif
#ifdef __AVX2__
#define INTRIN_AVX2 1
#endif
#ifdef __F16C__
#define INTRIN_F16C 1
#endif
#ifdef __FMA__
#define INTRIN_FMA  1
#endif
#ifdef __FMA4__
#define INTRIN_FMA4 1
#endif
#ifdef __XOP__
#define INTRIN_XOP  1
#endif
#ifdef __LWP__
#define INTRIN_LWP  1
#endif
#ifdef __RDRND__
#define INTRIN_RDRND    1
#endif
#ifdef __FSGSBASE__
#define INTRIN_FSGSBASE 1
#endif
#ifdef __LZCNT__
#define INTRIN_LZCNT    1
#endif
#ifdef __POPCNT__
#define INTRIN_POPCNT   1
#endif
#ifdef __BMI__
#define INTRIN_BMI  1
#endif
#ifdef __BMI2__
#define INTRIN_BMI2 1
#endif
#ifdef __TBM__
#define INTRIN_TBM  1
#endif
#endif  // #if !defined(__llvm__)
#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_SSE4A    1   // intrin.h
#define INTRIN_LZCNT    1   // intrin.h
#define INTRIN_POPCNT   1   // nmmintrin.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 0   //TODO:待查证. 先设为0.
#define INTRIN_FMA  0
#define INTRIN_F16C 0
#define INTRIN_RDRND    0
#define INTRIN_FSGSBASE 0
#define INTRIN_BMI  0
#define INTRIN_BMI2 0
#define INTRIN_TBM  0
#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

3.2 testzintrin.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_LZCNT
PT_MAKE_STR(INTRIN_LZCNT),
#endif
#ifdef INTRIN_POPCNT
PT_MAKE_STR(INTRIN_POPCNT),
#endif
#ifdef INTRIN_BMI
PT_MAKE_STR(INTRIN_BMI),
#endif
#ifdef INTRIN_BMI2
PT_MAKE_STR(INTRIN_BMI2),
#endif
#ifdef INTRIN_TBM
PT_MAKE_STR(INTRIN_TBM),
#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());
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));
#endif
return 0;
}

3.3 makefile

  全部代码——

# 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版。



参考文献——
《Predefined Macros》. http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.110).aspx
《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

源码下载——
http://files.cnblogs.com/zyl910/zintrin.rar

[C] zintrin.h : 智能引入intrinsic函数。支持VC、GCC,兼容Windows、Linux、Mac OS X相关推荐

  1. [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: 智能引入intrinsi ...

  2. python给函数设置超时时间_在 Linux/Mac 下为Python函数添加超时时间的方法

    我们在使用 requests 这类网络请求第三方库时,可以看到它有一个参数叫做 timeout ,就是指在网络请求发出开始计算,如果超过 timeout 还没有收到返回,就抛出超时异常.(当然存在特殊 ...

  3. Qt编写精美输入法(历时十年迭代/可换肤/支持Qt4/5/6/win/linux/mac/嵌入式等)

    一.前言 大概是从2012年就开始研究用Qt写输入法,因为项目需要,嵌入式板子上,没有对应的输入法,当初使用过很多NVR,里面也是鼠标按下弹出输入法面板进行输入,可以切换数字和字母及中文,于是借鉴着操 ...

  4. python给函数设置超时时间_在 Linux/Mac 下为Python函数添加超时时间

    我们在使用 requests 这类网络请求第三方库时,可以看到它有一个参数叫做timeout,就是指在网络请求发出开始计算,如果超过 timeout 还没有收到返回,就抛出超时异常.(当然存在特殊情况 ...

  5. Ventoy-超强装机神器,支持全部系统(windows,linux,ubuntu),只需要一个U盘

    https://zhuanlan.zhihu.com/p/137477151 兼容性目前(2020-6-26)还比较差,功能强大,相信未来会成为主流工具,提前码住

  6. [C] 跨平台使用Intrinsic函数范例1——使用SSE、AVX指令集 处理 单精度浮点数组求和(支持vc、gcc,兼容Windows、Linux、Mac)...

    作者:zyl910. 本文面对对SSE等SIMD指令集有一定基础的读者,以单精度浮点数组求和为例演示了如何跨平台使用SSE.AVX指令集.因使用了stdint.zintrin.ccpuid这三个模块, ...

  7. [C] 跨平台使用Intrinsic函数范例3——使用MMX、SSE2指令集 处理 32位整数数组求和...

    作者:zyl910. 本文面对对SSE等SIMD指令集有一定基础的读者,以32位整数数组求和为例演示了如何跨平台使用MMX.SSE2指令集.支持vc.gcc编译器,在Windows.Linux.Mac ...

  8. 基于ARM 的neon介绍以及常用intrinsic函数总结

    在介绍NEON前,必须要介绍一下SIMD. 1.什么是simd 众所周知,计算机程序需要编译成指令才能让 CPU 识别并执行运算.所以,CPU 指令处理数据的能力是衡量 CPU 性能的重要指标.为了提 ...

  9. mac android工具下载,适用于Mac OS的安卓数据恢复软件Android Toolkit,支持macOS 10.15?...

    原标题:适用于Mac OS的安卓数据恢复软件Android Toolkit,支持macOS 10.15? 适用于Mac OS的安卓数据恢复软件你觉得哪款好用? 安卓数据恢复软件Android Tool ...

最新文章

  1. 小米数据管理与应用实践
  2. alexa工具条下载_如何聆听(和删除)您给Alexa的每条命令
  3. ORACLE数据库数据迁移
  4. MyBatisplus字段名与表名的映射
  5. php 中国时间转换美国时间差,美国中国时差换算(世界时间换算器在线)
  6. Ubuntu生成so共享库的方法
  7. 在大学里计算机专业总分多少,计算机专业高考多少分录取?附中国计算机专业大学排名及分数线...
  8. 华硕Fonepad 8、MeMO Pad 7以及MeMO Pad 8也随之登场
  9. B_随笔_关于网站记录(2)
  10. Digg.com:投票的动力因素分析
  11. Power BI 学习笔记(一)
  12. QT 制作 gif 录屏 小工具
  13. @Value注解失效的原因分析
  14. 代码随想录训练营day42
  15. ifix5.8 启动过程中 iFX系统未启动!Profice iFIX 启动文件末找到无法执行任务:IOCNTRL.EXE
  16. linux自带spi驱动 ,可自己配置CS
  17. 开关稳压DC—DC降压电路简介
  18. JQuery蒙板层(特效)
  19. Linux系统调用号列表、系统调用的三种方式
  20. 训练营感想_为什么训练营很有价值

热门文章

  1. 前端工程不了解?带你踩坑加爬坑。
  2. 使用CxImage进行图形和格式转换(CBitmap to jpg or png or gif or bmp)
  3. linux使用rm -rf删除数据后恢复
  4. 企业为什么要用知识管理系统?
  5. Scrapy 提示错误 DEBUG: Crawled (403) <GET https://book.douban.com/top250> (referer: None)
  6. 中软Java工程师面试题
  7. 测试打印机性能的软件,首选项设置及打印速度测试
  8. 计算机基础——2.3 内存,主板和I/O
  9. bios sgx需要开启吗_电脑通用bios设置详解方法就是这么简单
  10. ACM:Capricorn's Trial