我知道有类似的问题,但是使用不同的标志编译不同的文件是不可接受的解决方案,因为它会使代码库快速复杂化。回答“不,这是不可能的”。
在任何版本的Clang OR GCC中,是否可以为SSE 2/3 / 3S / 4.1编译内在函数,同时只允许编译器使用SSE指令集进行优化?
编辑:例如,我希望编译器将_mm_load_si128()
转换为movdqa
,但编译器不得在除此内在函数之外的任何其他位置发出此指令,类似如何使用MSVC编译器。
EDIT2 :我有动态调度程序和几个版本的单个函数,使用内在函数编写不同的指令集。使用多个文件会使维护更加困难,因为相同版本的代码将跨越多个文件,并且有很多此类函数。
EDIT3 :请求的示例源代码:https://github.com/AviSynth/AviSynthPlus/blob/master/avs_core/filters/resample.cpp或该文件夹中的大多数文件。
答案 0 :(得分:9)
这是一种使用gcc的方法,可能是可以接受的。所有源代码都进入单个源文件。单个源文件分为几个部分。一节根据使用的命令行选项生成代码。 main()和处理器功能检测等功能在本节中介绍。另一部分根据目标覆盖编译指示生成代码。可以使用目标覆盖值支持的内部函数。只有在处理器功能检测确认存在所需的处理器功能后,才应调用本节中的功能。此示例具有AVX2代码的单个覆盖部分。在编写针对多个目标优化的函数时,可以使用多个覆盖部分。
// temporarily switch target so that all x64 intrinsic functions will be available
#pragma GCC push_options
#pragma GCC target ("arch=core-avx2")
#include <intrin.h>
// restore the target selection
#pragma GCC pop_options
//----------------------------------------------------------------------------
// the following functions will be compiled using default code generation
//----------------------------------------------------------------------------
int dummy1 (int a) {return a;}
//----------------------------------------------------------------------------
// the following functions will be compiled using core-avx2 code generation
// all x64 intrinc functions are available
#pragma GCC push_options
#pragma GCC target ("arch=core-avx2")
//----------------------------------------------------------------------------
static __m256i bitShiftLeft256ymm (__m256i *data, int count)
{
__m256i innerCarry, carryOut, rotate;
innerCarry = _mm256_srli_epi64 (*data, 64 - count); // carry outs in bit 0 of each qword
rotate = _mm256_permute4x64_epi64 (innerCarry, 0x93); // rotate ymm left 64 bits
innerCarry = _mm256_blend_epi32 (_mm256_setzero_si256 (), rotate, 0xFC); // clear lower qword
*data = _mm256_slli_epi64 (*data, count); // shift all qwords left
*data = _mm256_or_si256 (*data, innerCarry); // propagate carrys from low qwords
carryOut = _mm256_xor_si256 (innerCarry, rotate); // clear all except lower qword
return carryOut;
}
//----------------------------------------------------------------------------
// the following functions will be compiled using default code generation
#pragma GCC pop_options
//----------------------------------------------------------------------------
int main (void)
{
return 0;
}
//----------------------------------------------------------------------------
答案 1 :(得分:0)
我正在恢复此线程,因为最近在Visual Studio中添加了LLVM / clang-cl支持,因为遇到了同样的问题。我需要在运行时检查(例如)AVX支持的代码。通过MSVC编译器,我可以使用此代码:
bool hasAVX(void) {
__try { // VMASKMOVPD = AVX instruction
__declspec(align(32)) double d[4] = { 4.4, 3.3, 2.2, 1.1 };
__m256d dData = _mm256_setr_pd(8.888, 7.777, 6.666, 5.555);
_mm256_store_pd(d, dData);
}
__except (EXCEPTION_EXECUTE_HANDLER) { return false; }
return true;
}
它的构建没有任何特殊的体系结构标志(不需要/ arch:AVX)。但是,使用clang-cl,如果不使用-mavx构建,即使定义了#,也不会定义“ __m256d”和“ mm256_setr_pd”。 (全局)头文件中包含#pragma clang属性
#pragma clang attribute push (__attribute__((target("arch=avx"))), apply_to=function)
#include <intrin.h>
#pragma clang attribute pop
有任何线索吗?
艾德里安
PS:我的“信誉度”小于50,因此无法添加为评论!
答案 2 :(得分:-1)
除了编译器本身的开关之外,无法控制用于编译器的指令集。换句话说,没有编译指示或其他功能,只有整个编译器标志。
这意味着实现您想要的唯一可行解决方案是使用-msseX并将您的源分成多个文件(当然,您总是可以使用各种聪明的#include
等将单个文本文件保存为主要来源,只需在多个地方包含相同的文件)
当然,编译器的源代码是可用的。我相信GCC和Clang / LLVM的维护人员会很乐意接受改进的补丁。但请记住,从“解析源”到“发出指令”的路径是漫长而复杂的。如果我们这样做会发生什么:
#pragma use_sse=1
void func()
{
... some code goes here ...
}
#pragma use_sse=3
void func2()
{
...
func();
...
}
现在,如果编译器内联它,func足够短,可以内联吗?如果是这样,它应该使用func()的sse1或sse3指令。
我知道你可能并不关心那种困难,但Clang和GCC的维护人员确实必须以某种方式解决这个问题。
编辑: 在声明SSE内在函数(以及许多其他内在函数)的头文件中,典型函数看起来像这样:
extern __inline __m128 __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_add_ss (__m128 __A, __m128 __B)
{
return (__m128) __builtin_ia32_addss ((__v4sf)__A, (__v4sf)__B);
}
只有在启用了-msse选项后,才能在编译器中使用builtin_ia32_addss。因此,如果您说服编译器仍然允许您在使用-mno-sse时使用_mm_add_ss(),则会出现“__builtin_ia32_addss未在此范围内声明”的错误(我刚试过)。
改变这种特殊行为可能不是很难 - 可能只有少数几个地方代码“引入内置函数”。但是,我不相信代码中还有其他问题,稍后会在编译器中实际发出指令。
我在基于Clang的编译器中使用“内置函数”做了一些工作,不幸的是,从“解析器”到“代码生成”涉及到几个步骤,其中涉及内置函数。 / p>
Edit2:
与GCC相比,为Clang解决这个问题更复杂,因为编译器本身已经理解了SSE指令,所以它只是在头文件中有这个:
static __inline__ __m128 __attribute__((__always_inline__, __nodebug__))
_mm_add_ps(__m128 __a, __m128 __b)
{
return __a + __b;
}
编译器将知道要添加几个__m128,它需要生成正确的SSE指令。我刚刚下载了Clang(我在家里,我对Clang的工作正在进行中,与SSE完全没有关系,只是内置函数 - 而且我没有真正对Clang做过很多改动,但它足以大致了解内置函数的工作方式)。
但是,从您的角度来看,它不是内置函数会使情况变得更糟,因为operator+
转换要复杂得多。我很确定编译器只是把它变成了“添加这两件事”,然后将它传递给LLVM以进行进一步的工作 - LLVM将是了解SSE指令等的部分。但是为了你的目的,这会使情况更糟,因为这是一个“内在函数”的事实现在几乎丢失了,编译器只是处理它就像你写了一个+ b一样,a和b的副作用是128位长的类型。这使得处理生成“正确的指令”并将“所有其他”指令保持在不同的SSE级别变得更加复杂。