GCC是否可以优化isnan(x)|| isnan(y)成isunordered(x,y)?

Joh*_*nck 19 c c++ optimization gcc compiler-optimization

这是我的代码:

int f(double x, double y)
{
  return std::isnan(x) || std::isnan(y);
}
Run Code Online (Sandbox Code Playgroud)

如果您使用的是C而不是C++,只需替换std::__builtin_(不要简单地删除std::,原因如下所示:为什么GCC对C++ <cmath>比C <math.h>更有效地实现isnan()?).

这是集会:

ucomisd %xmm0, %xmm0 ; set parity flag if x is NAN
setp    %dl          ; copy parity flag to %edx
ucomisd %xmm1, %xmm1 ; set parity flag if y is NAN
setp    %al          ; copy parity flag to %eax
orl     %edx, %eax   ; OR one byte of each result into a full-width register
Run Code Online (Sandbox Code Playgroud)

现在让我们尝试另一种做同样事情的配方:

int f(double x, double y)
{
  return std::isunordered(x, y);
}
Run Code Online (Sandbox Code Playgroud)

这是替代方案的组装:

xorl    %eax, %eax
ucomisd %xmm1, %xmm0
setp    %al
Run Code Online (Sandbox Code Playgroud)

这很棒 - 我们将生成的代码减少了一半!这工作,因为ucomisd套,如果奇偶标志或者其操作数为NAN,所以我们可以在同一时间,SIMD式测试两个值.

您可以在野外查看原始版本的代码,例如:https://svn.r-project.org/R/trunk/src/nmath/qnorm.c

如果我们能够让GCC足够聪明,可以isnan()在任何地方组合两个电话,那将非常酷.我的问题是:我们可以,怎么样?我对编译器的工作原理有所了解,但我不知道GCC在哪里可以执行这种优化.基本思想是每当有一对isnan()(或__builtin_isnan)调用OR在一起时,它应该同时ucomisd使用两个操作数发出一条指令.

编辑添加Basile Starynkevitch的答案提示的一些研究:

如果我使用-fdump-tree-all编译,我会发现两个似乎相关的文件.首先,*.gimple包含这个(以及更多):

D.2229 = x unord x;
D.2230 = y unord y;
D.2231 = D.2229 | D.2230;
Run Code Online (Sandbox Code Playgroud)

在这里我们可以清楚地看到GCC知道它会传递(x, x)isunordered().如果我们想通过在此级别进行转换来进行优化,则规则大致为:"替换a unord a | b unord b为" a unord b.这是编译我的第二个C代码时得到的结果:

D.2229 = x unord y;
Run Code Online (Sandbox Code Playgroud)

另一个有趣的文件是*.original:

return <retval> = (int) (x unord x || y unord y);
Run Code Online (Sandbox Code Playgroud)

这实际上是由生成的整个非评论文件-fdump-tree-original.对于更好的源代码,它看起来像这样:

return <retval> = x unord y;
Run Code Online (Sandbox Code Playgroud)

显然,可以应用相同类型的转换(仅此处||而不是|).

但不幸的是,如果我们修改源代码,例如:

if (__builtin_isnan(x))
  return true;
if (__builtin_isnan(y))
  return true;
return false;
Run Code Online (Sandbox Code Playgroud)

然后我们得到了完全不同的Gimple和Original输出文件,尽管最终的程序集和以前一样.那么也许最好在管道的后期尝试这种转换?该*.optimized文件(以及其他)显示与原始版本的"if"s版本相同的代码,因此这很有希望.

Mar*_*sse 10

这种优化不仅是可能的,现在可以在gcc-6中找到:https: //gcc.gnu.org/viewcvs/gcc?view = revision&vision = 222077

  • 你花时间添加简化是非常好的. (3认同)