C++ 如何指导基于断言的GCC优化而不需要运行时成本?

C++ 如何指导基于断言的GCC优化而不需要运行时成本?,c++,gcc,optimization,assert,compiler-optimization,C++,Gcc,Optimization,Assert,Compiler Optimization,我的代码中使用了一个宏,它在调试模式下执行以下操作: #define contract(condition) \ if (!(condition)) \ throw exception("a contract has been violated"); 。。。但在发布模式下: #define contract(condition) \ if (!(condition)) \ __builtin_unreachable(); foo(int):

我的代码中使用了一个宏,它在调试模式下执行以下操作:

#define contract(condition) \
    if (!(condition)) \
        throw exception("a contract has been violated");
。。。但在发布模式下:

#define contract(condition) \
    if (!(condition)) \
        __builtin_unreachable();
foo(int):
        mov     eax, 1
        ret
这在
assert()
上的作用是,在发布版本中,由于UB传播,编译器可以大量优化代码

例如,使用以下代码进行测试:

int foo(int i) {
    contract(i == 1);
    return i;
}

// ...

foo(0);
。。。在调试模式下引发异常,但为无条件
返回1生成程序集在释放模式下:

#define contract(condition) \
    if (!(condition)) \
        __builtin_unreachable();
foo(int):
        mov     eax, 1
        ret
条件,以及一切依赖于它的东西,已经被优化了

我的问题出现在更复杂的情况下。当编译器无法证明该条件没有副作用时,它不会对其进行优化,与不使用契约相比,这是一种runtme惩罚


有没有一种方法可以表示合同中的条件没有副作用,因此它总是被优化掉?

因此,不是答案,而是一些可能会带来某些结果的有趣结果

我最终得到了以下玩具代码:

#define contract(x) \
    if (![&]() __attribute__((pure, noinline)) { return (x); }()) \
        __builtin_unreachable();

bool noSideEffect(int i);

int foo(int i) {
    contract(noSideEffect(i));

    contract(i == 1);

    return i;
}
你也可以;)

noSideEffect
是我们知道没有副作用的函数,但编译器没有。
事情是这样的:

  • GCC具有
    \uuuuuuuuuuuuuuuu属性((纯))
    ,用于将函数标记为没有副作用

  • 使用
    pure
    属性限定
    noSideEffect
    将完全删除函数调用。很好

  • 但是我们不能修改
    noSideEffect
    的声明。那么,把它的调用封装在一个本身是纯的函数中怎么样?因为我们正在尝试制作一个自包含的宏,所以lambda听起来不错

  • 令人惊讶的是,这不起作用。。。除非我们将
    noinline
    添加到lambda!我假设优化器首先内联lambda,在考虑优化对
    noSideEffect
    的调用之前,会丢失
    pure
    属性。使用
    noinline
    ,优化器能够以一种有点违反直觉的方式消除一切。太好了

  • 但是,现在有两个问题:使用
    noinline
    属性,编译器为每个lambda生成一个主体,即使它们从未使用过。Meh——无论如何,链接器都可能会将它们扔掉。
    但更重要的是。。。实际上,我们丢失了
    \u内置的不可访问()
    所启用的优化:(

  • 总而言之,您可以在上面的代码段中删除或放回
    noinline
    ,并得到以下结果之一:

    使用
    noinline

    ;未使用的代码
    foo(int):{lambda()#2}::运算符()()常量:
    mov-rax,QWORD-PTR[rdi]
    cmp DWORD PTR[rax],1
    塞特艾尔
    ret
    foo(int):{lambda()#1}::运算符()()常量:
    mov-rax,QWORD-PTR[rdi]
    mov edi,德沃德PTR[rax]
    jmp noSideEffect(国际)
    ;没有函数调用,但执行了对i的访问
    foo(int):
    电子数据交换
    ret
    
    noinline

    ;没有未使用的代码
    ;访问i的权限已优化,
    ;但对“noSideEffect”的呼吁一直没有取消。
    foo(int):
    副区长,8
    呼叫noSideEffect(内部)
    mov-eax,1
    加上rsp,8
    ret
    
    有没有一种方式可以表示合同中的条件没有副作用,因此它总是被优化的

    不太可能

    众所周知,你不能收集大量的断言,将它们转化为假设(通过
    \uuu builtin\u unreachable
    )并期望得到好的结果(例如John Regehr)

    一些线索:

    • CLANG,虽然已经有了
      \uuu内置的不可访问的
      内在特性,但正是为了这个目的引入的

    • (*)注意到:

      // GSL_ASSUME(cond)
      //
      // Tell the optimizer that the predicate cond must hold. It is unspecified
      // whether or not cond is actually evaluated.
      
      GCC没有明确提供一般假设工具,但一般假设可以使用控制流和
      \uuuu内置的不可访问的
      内在函数的组合进行编码

      提供一般假设的现有实现在实现保留的标识符空间中使用一些关键字(
      \uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu(例如,
      std::假设
      )似乎很难

    • 指南支持库(由Microsoft托管,但并非特定于Microsoft)仅包含以下代码:

      #ifdef _MSC_VER
      #define GSL_ASSUME(cond) __assume(cond)
      #elif defined(__clang__)
      #define GSL_ASSUME(cond) __builtin_assume(cond)
      #elif defined(__GNUC__)
      #define GSL_ASSUME(cond) ((cond) ? static_cast<void>(0) : __builtin_unreachable())
      #else
      #define GSL_ASSUME(cond) static_cast<void>(!!(cond))
      #endif
      

    *)文件:EWG的指导是在拟定合同设施内提供功能。

    没有办法强制优化代码,就好像它是死代码一样,因为GCC必须始终与标准保持一致

    另一方面,可以通过使用属性
    error
    检查表达式是否没有任何副作用,该属性将在函数调用无法优化时显示错误

    宏的一个示例,用于检查优化的内容并执行UB传播:

    #define _contract(condition) \
        {
            ([&]() __attribute__ ((noinline,error ("contract could not be optimized out"))) {
                if (condition) {} // using the condition in if seem to hide `unused` warnings.
            }());
            if (!(condition))
                __builtin_unreachable();
        }
    
    如果没有优化,error属性将无法工作(因此此宏只能用于release\optimization模式编译)。 请注意,在链接过程中会显示指示契约有任何副作用的错误


    评论不是为了进一步讨论;这段对话已经进行了。我经常玩这个玩具,我认为它是GCC中的一个缺陷,当它内联时,它会去掉
    pure
    属性,谢谢你的知识。请问“cond”是什么是否以及在何处声明?@jack_1729
    cond
    是类似于宏的函数的输入参数(请查看)。
    GSL_假设
    需要一个布尔表达式/条件(
    cond
    )。