在每个功能/每个代码块的基础上启用SSE4的正确方法?

时间:2014-06-08 00:24:43

标签: xcode clang llvm sse

对于我的一个OS X程序,我有一些使用SSE4.1指令的优化案例。在仅限SSE3的计算机上,运行非优化分支:

// SupportsSSE4_1 returns true on CPUs that support SSE4.1, false otherwise
if (SupportsSSE4_1()) {

    // Code that uses _mm_dp_ps, an SSE4 instruction

    ...

    __m128 hDelta   = _mm_sub_ps(here128, right128);
    __m128 vDelta   = _mm_sub_ps(here128, down128);

    hDelta = _mm_sqrt_ss(_mm_dp_ps(hDelta, hDelta, 0x71));
    vDelta = _mm_sqrt_ss(_mm_dp_ps(vDelta, vDelta, 0x71));

    ...

} else {
    // Equivalent code that uses SSE3 instructions
    ...
}

为了使上述内容得到编译,我必须将CLANG_X86_VECTOR_INSTRUCTIONS设置为sse4.1

然而,这似乎告诉clang可以在我的程序中的任何地方使用ROUNDSD指令。因此,程序在仅使用SIGILL: ILL_ILLOPC的SSE3计算机上崩溃。

对于SupportsSSE4_1() if块的真分支内的代码行启用SSE4.1的最佳做法是什么?

3 个答案:

答案 0 :(得分:11)

目前无法在clang中以块/函数粒度定位不同的ISA扩展。您只能以 file 粒度执行此操作(将您的SSE4.1代码放入单独的文件中并指定该文件以使用-msse4.1)。如果这对您来说是一项重要功能,请提交错误报告以进行申请!

但是,我应该注意到DPPS的实际好处在大多数实际场景中都非常小(并且使用DPPS甚至会减慢一些代码序列!)。除非这个特定的代码序列是关键的,并且您已经仔细测量了使用DPPS的效果,否则即使该编译器功能可用,对于SSE4.1的特殊情况也可能不值得。

答案 1 :(得分:6)

您可以制作CPU调度程序。您可以在一个文件中执行此操作,但必须编译两次。首先使用SSE4.1然后不使用然后链接到SSE4.1的目标文件中。第一次调用函数myfunc时,它会调用函数myfunc_dispatch来确定指令集,并将指针设置为myfunc_SSE41myfunc_SSE3。下次调用func myfunc时,它会直接跳转到指令集的函数。

//clang -c -O3 -msse4.1 foo.cpp -o foo_sse41.o
//clang -O3 -msse3 foo.cpp foo_sse41.o   

typedef float MyFuncType(float*);

MyFuncType myfunc, myfunc_SSE41, myfunc_SSE3, myfunc_dispatch;
MyFuncType * myfunc_pointer = &myfunc_dispatch;

#ifdef __SSE4_1__
float myfunc_SSE41(float* a) {
    //SSE41 code
}
#else
float  myfunc_SSE3(float *a) {
    //SSE3 code
}

float myfunc_dispatch(float *a) {
    if(SupportsSSE4_1()) {
        myfunc_pointer = myfunc_SSE41;
    }
    else {
        myfunc_pointer = myfunc_SSE3;
    }
    myfunc_pointer(a);
}

float myfunc(float *a) {
    (*myfunc_pointer)(a);
}
int main() {
    //myfunc(a);
}
#endif

答案 2 :(得分:4)

根据操作系统的不同,您可能会在将来使用功能多媒体功能。我现在正在处理这项功能,但它还有一段时间可以用于生产编译器。

有关详细信息,请参阅http://gcc.gnu.org/wiki/FunctionMultiVersioning