gcc中的"假设"条款

use*_*436 11 c c++ gcc built-in gcc4.9

gcc(最新版本:4.8,4.9)是否具有类似于__assume()icc支持的内置的"假设"子句?例如,__assume( n % 8 == 0 );

Pab*_*ern 16

从gcc 4.8.2开始,gcc中没有__assume()的等价物.我不知道为什么 - 它会非常有用.mafso建议:

#define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0)
Run Code Online (Sandbox Code Playgroud)

这是一个老技巧,至少可以追溯到2010年,可能更长.编译器通常会优化'cond'的评估,因为无论如何,cond为false的任何评估都将是未定义的.但是,如果它包含对不透明(非内联)函数的调用,它似乎不会优化掉'cond'.编译器必须假设不透明调用可能具有副作用(例如,更改全局)并且不能优化掉调用,尽管它可以优化掉结果上的任何计算和分支.出于这个原因,宏观方法充其量只是部分解决方案.

  • 在 gcc 5.2 和 6.1 中也遇到了这个问题。当底层表达式不透明时, cond 不会被优化。即使 cond 被包装在纯函数中,您也会认为编译器可以自由地对其进行优化。另外,我还没有找到一种方法来检测编译器是否优化了条件。这意味着编译器默默地添加不必要的代码可能会损害使用此宏的性能。通过透明的表达,宏的效果非常好。 (2认同)
  • @MappaM 它从 gcc 12.1 开始工作,即编译器正确优化返回 0 的模数。在 gcc 13.1 中,优化更加令人印象深刻,整个循环几乎展开为空。Gcc 13.1 还具有“[[assume(cond)]]”属性,这是从 C++23 开始获取假设的标准方法。 (2认同)

Hol*_*Cat 9

[[assume(...)]];- 添加到 C++23 的可移植版本。

__attribute__((__assume__(...)));- 在 GCC 13 中与上述内容一起添加,对于 C 代码很有用。


wim*_*wim 4

在您的示例中,您想要通知编译器它N是 8 的倍数。您只需插入以下行即可完成此操作

N = N & 0xFFFFFFF8;
Run Code Online (Sandbox Code Playgroud)

在您的代码中(如果N是 32 位整数)。这不会改变N,因为N是 8 的倍数,但从 GCC 4.9 开始,编译器似乎理解这N是 8 的倍数,在这一行之后。

下一个示例展示了这一点,其中添加了两个浮点向量:

int add_a(float * restrict a, float * restrict b, int N)
{
    a = (float*)__builtin_assume_aligned(a, 32);
    b = (float*)__builtin_assume_aligned(b, 32);
    N = N & 0xFFFFFFF8; 
    for (int i = 0; i < N; i++){
        a[i] = a[i] + b[i];
    }
    return 0;
}


int add_b(float * restrict a, float * restrict b, int N)
{
    a = (float*)__builtin_assume_aligned(a, 32);
    b = (float*)__builtin_assume_aligned(b, 32);
    for (int i = 0; i < N; i++){
        a[i] = a[i] + b[i];
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

使用gcc -m64 -std=c99 -O3gcc 版本 4.9,add_a编译为矢量化代码

add_a:
  and edx, -8
  jle .L6
  sub edx, 4
  xor ecx, ecx
  shr edx, 2
  lea eax, [rdx+1]
  xor edx, edx
.L3:
  movaps xmm0, XMMWORD PTR [rdi+rdx]
  add ecx, 1
  addps xmm0, XMMWORD PTR [rsi+rdx]
  movaps XMMWORD PTR [rdi+rdx], xmm0
  add rdx, 16
  cmp ecx, eax
  jb .L3
.L6:
  xor eax, eax
  ret
Run Code Online (Sandbox Code Playgroud)

对于 function ,需要超过 20 条额外指令来处理不是 8 的倍数的add_b情况 :N

add_b:
  test edx, edx
  jle .L17
  lea ecx, [rdx-4]
  lea r8d, [rdx-1]
  shr ecx, 2
  add ecx, 1
  cmp r8d, 2
  lea eax, [0+rcx*4]
  jbe .L16
  xor r8d, r8d
  xor r9d, r9d
.L11:
  movaps xmm0, XMMWORD PTR [rdi+r8]
  add r9d, 1
  addps xmm0, XMMWORD PTR [rsi+r8]
  movaps XMMWORD PTR [rdi+r8], xmm0
  add r8, 16
  cmp ecx, r9d
  ja .L11
  cmp eax, edx
  je .L17
.L10:
  movsx r8, eax
  lea rcx, [rdi+r8*4]
  movss xmm0, DWORD PTR [rcx]
  addss xmm0, DWORD PTR [rsi+r8*4]
  movss DWORD PTR [rcx], xmm0
  lea ecx, [rax+1]
  cmp edx, ecx
  jle .L17
  movsx rcx, ecx
  add eax, 2
  lea r8, [rdi+rcx*4]
  cmp edx, eax
  movss xmm0, DWORD PTR [r8]
  addss xmm0, DWORD PTR [rsi+rcx*4]
  movss DWORD PTR [r8], xmm0
  jle .L17
  cdqe
  lea rdx, [rdi+rax*4]
  movss xmm0, DWORD PTR [rdx]
  addss xmm0, DWORD PTR [rsi+rax*4]
  movss DWORD PTR [rdx], xmm0
.L17:
  xor eax, eax
  ret
.L16:
  xor eax, eax
  jmp .L10
Run Code Online (Sandbox Code Playgroud)

请参阅Godbolt 链接

  • 这也会导致编译器插入代码来执行按位与。 (5认同)