有没有一种方法告诉GCC一个参数的范围?

3

我有一些经常使用的代码,希望GCC能够进行积极优化。但是我还想编写干净、可重用的代码,其中包括可内联的函数,这些函数在多个位置被调用。有些情况下,在内联函数中,我知道可以删除某些代码,因为条件永远不可能发生。

让我们看一个具体的例子:

#include <assert.h>

static inline int foo(int c)
{
  if (c < 4)
    return c;
  else
    return 4;
}

int bar(int c)
{
  assert(c < 2);

  return foo(c);
}

使用-DNDEBUG -O3编译选项,即使我知道它不需要(c < 4)比较,GCC仍然会生成该比较,因为bar函数的前提条件是c为0或1。如果没有使用-DNDEBUG,GCC确实会删除该比较,因为它被断言隐含了-但是您会有额外的断言开销(这实际上更多)。
是否有一种方法将变量范围传达给GCC,以便进行优化?
如果CLang在这方面做得更好,我也可以考虑切换编译器。

Clang 4.0 / Zapcc 在 x64 上比 GCC 7.1 做得稍微好一些 - 只需要 4 条指令而不是 5 条 - 但两者都不够聪明,无法优化 foo 中的 c < 4。您可以在 https://godbolt.org/g/3Hp4ZA 上使用各种编译器进行检查,我检查过的没有一个能够优化这个问题(这是有道理的)。理想情况下,应该有一些神奇的 pragma 来指定 bar 中 c 的范围,但我认为还没有人想出这样的 pragma... 或者可能是一些智能的 assert 内置函数,它可以从生成的代码中删除自己,但被优化器识别。 - valiano
@Mike:断言只是一个例子,我会删除第一个,因为它在这里不相关(不用于优化)。 - Arnout
1
@valiano:确实,我正在寻找一个智能的断言。GIMPLE(GCC的内部表示)跟踪变量范围,这就是它可以在存在断言的情况下优化掉(c < 4)的方式。我希望有一种明确表达这种范围的方法,而不仅仅是让编译器进行分析。 - Arnout
1个回答

3
你可以在测试中使用__builtin_unreachable(请阅读其他内建函数),告诉编译器,例如:
if (x<2 || x>100)
    __builtin_unreachable();
// Here the compiler knows that x is between 3 and 99 inclusive

在您的情况下,在 bar 的开头添加以下内容(可能包含一些漂亮的宏):
if (c >= 2)
    __builtin_unreachable();

如果您进行强烈的优化(例如,至少使用-O2),编译器会“知道”x在3和99之间(最近版本的GCC包含处理上述“简单”常量区间约束的代码,并在后续的优化阶段中利用它们)。但是,我不确定您是否应该使用它!(至少不要经常使用并将其包装在类似于assert的宏中),因为这可能不值得麻烦,而且编译器实际上只能处理和传播“简单”约束(其细节取决于编译器版本)。据我所知,最近的Clang和GCC都接受该内置函数。还要查看__builtin_trap(也会发出运行时代码)。

1
我正要发布这个。你可以将它包装在一个宏中:#define ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0) - melpomene
运行得非常好!但是,为什么你说不要使用它? - Arnout

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接