如何从预处理器启用内部函数

问题描述 投票:0回答:1

我可以通过使用查找表找到16位值的第n个设置位,但是对于32位值,如果不将其分解并使用多个LUT则无法做到这一点。在How to efficiently find the n-th set bit?中对此进行了讨论,该建议提出了与此类似的用法

#include <immintrin.h>
//...
unsigned i = 0x6B5;                 // 11010110101
unsigned n = 4;                     //    ^
unsigned j = _pdep_u32(1u << n, i);
int bitnum = __builtin_ctz(j));     // result is bit 7

我已经分析了此建议的循环方法

j = i;
for (k = 0; k < n; k++)
    j &= j - 1;
return (__builtin_ctz(j));

Nominal Animal的答案中,有比特纠错码的两个变体。旋转的分支变体是三个代码版本中最快的,不是很多。但是在我的actual代码中,使用__builtin_ctz的上述代码片段更快。还有其他答案,例如fuz的建议也暗示了我的发现:比特旋转与循环方法花费的时间相似。

所以我现在想尝试使用_pdep_u32,但无法识别_pdep_u32。我读了gcc.gnu.org

使用-mbmi2时,下列内置功能可用。...unsigned int _pdep_u32 (unsigned int, unsigned int)unsigned long long _pdep_u64 (unsigned long long, unsigned long long)...

但是我正在使用在线编译器,但是无法访问这些选项。

是否可以通过预处理器启用选项?

关于从命令行选项控制预处理器的材料很多,但是我找不到相反的方法。

最终我想使用64位版本_pdep_u64

c gcc bit-manipulation intrinsics
1个回答
0
投票

除了内联汇编,gcc / clang绝不会发出未通过命令行选项,函数属性或编译指示启用的汇编指令。

If您不能像普通人一样在命令行上使用GCC目标选项(例如-march=native启用编译主机支持的所有功能,并为该计算机进行调整),您< [can改为使用a pragma设置特定于目标的选项。但是效果不是很好。似乎#pragma target("arch=skylake")破坏了GCC的immintrin.h,显然是在未启用AVX512的情况下试图编译AVX512函数。 The GCC docs do show examples of using "arch=core2"作为函数属性,甚至"arch=core2"

尽管您可以设置"sse4.1,arch=core2",但不会破坏任何内容。 (调整不会启用ISA扩展,只会影响代码生成的选择。)

target("bmi2,tune=skylake")

[#include <immintrin.h> // #pragma GCC target ("arch=haswell") // also broken, disables BMI2?? #pragma GCC target ("bmi2,tune=skylake") // this can be after immintrin.h int foo(unsigned x) { // unsigned i = 0x6B5; // 11010110101 unsigned i = x; unsigned n = 4; // ^ unsigned j = _pdep_u32(1u << n, i); int bitnum = __builtin_ctz(j); // result is bit 7 return bitnum; } int constprop() { // check that GCC can still "understand" the intrinsic return foo(0x6B5); } with only

compiles cleanly on Godbolt,无-O3选项。-m

[如果您在使用foo: mov r8d, edi mov edi, 16 pdep edi, edi, r8d bsf eax, edi ret constprop: mov eax, 7 ret 定义正确的包装程序时遇到麻烦,则可以查看GCC的标头以了解如何定义它们。例如immintrin.h_pdep_u32(单整数)的包装,而u64是__builtin_ia32_pdep_si(双整数)的包装。

这些__builtin_ia32_pdep_di函数始终由GCC定义,即使没有标头,但这些__builtin内置函数只能在具有兼容目标选项的函数中使用。对于不支持指令的目标,没有ia32这样的后备。

但是似乎至少对于BMI2指令而言,__builtin_popcount_pdep_u32由GCC的_pdep_u64定义,而与命令行选项无关

(它将定义immintrin.h CPP宏)。可能是这种用例的原因:具有目标属性的单个函数,或具有杂注的函数块。__BMI2__版本需要BMI2 + x86-64,而32位版本仅需要BMI2(即,也可以在32位模式下使用)。 _pdep_u64,因此64位操作数大小需要64位模式。


此外:MSVC编译了内在函数,没有大惊小怪

是的,MSVC和GCC / clang关于内在函数的哲学不同。 MSVC允许您使用任何内容,并假设您将执行运行时调度,以确保执行永远不会达到此计算机不支持的内部函数。

这可能与MSVC根本上根本没有优化内部函数有关,通常甚至没有通过它们进行简单的常量传播。我想,因为它试图确保在C ++抽象机中未达到内在函数时,相应的asm指令不会执行,因此我认为这是一种缓慢而安全的方法。

但是程序崩溃了(我想我的CPU不支持pdep is a scalar integer instruction,如链接的问题所建议)。

是,并非所有CPU都具有BMI2。您无法使用GCC进行任何更改。

© www.soinside.com 2019 - 2024. All rights reserved.