assert()s、优化和D中的assume()指令



假设我有一个assert(),类似于assert( x < limit );我用以下代码片段查看了GDC中优化器在发布和调试构建中的行为:

uint cxx1( uint x )
{
assert( x < 10 );
return x % 10;
}
uint cxx1a( uint x )
in  { assert( x < 10 ); }
body
{
return x % 10;
}
uint cxx2( uint x )
{
if ( !( x < 10 ))
assert(0);
return x % 10;
}

现在,当我在调试模式下构建时,断言具有触发巨大优化的非常令人愉快的效果。GDC完全摆脱了可怕的代码来进行模运算,因为它知道由于断言的if条件而导致的x的可能范围。但在发布模式中,if条件被丢弃,所以突然之间,可怕的代码又回来了,cxx1()甚至cxx1a()中都不再有任何优化。具有讽刺意味的是,发布模式生成的代码比调试代码糟糕得多。当然,没有人希望属于if测试的可执行代码出现在发布代码中,因为我们必须失去所有的开销。

现在,理想情况下,我想从向编译器传达信息的意义上表达条件,无论发布/调试版本如何,关于可能总是被假设为真实的条件,因此这些假设可以以非常强大的方式指导优化。

我相信有些C++编译器有一种叫做__assume()之类的东西,但这里的内存让我很失望。GCC有一个__builtin_unreachable()特殊指令,该指令可能可用于构建assume()功能。基本上,如果我可以构建自己的assume()指令,它将具有断言已知值或已知范围的某些真相的效果,并将这些值或范围公开/发布到优化过程中,而不考虑发布/调试模式,但在发布构建中根本不为asseme()条件生成任何实际代码,而在调试模式下,它将与assert()完全相同。

我尝试了一个实验,你可以在cxx2中看到它总是触发优化,做得很好,但它为assume()的if条件生成了道德上的调试代码,即使在发布模式下,也可以通过测试和条件跳转到未定义的指令来停止进程。

有人知道这是否可以解决吗?或者你认为这是一个有用的D编译器幻想清单项目?

据我所知,__builtin_unreachable是GCC中类似assume的函数的次佳替代品。在某些情况下,if条件可能仍然没有得到优化,尽管:;假设";gcc 中的子句

GCC内建可通过导入gcc.builtins在GDC中获得。以下是如何包装__builtin_unreachable函数的示例:

import gcc.builtins;
void assume()(bool condition)
{
if (!condition)
__builtin_unreachable();
}
bool foo(int a)
{
assume(a > 10);
return a > 10;
}

这里有两个有趣的细节:

  1. 我们不需要字符串混合或类似复杂的东西。只要使用-O编译,GDC无论如何都会完全优化函数调用
  2. 为了实现这一点,assume函数必须内联。不幸的是,当assume与调用函数位于不同的模块中时,并不完全支持内联普通函数。作为一种变通方法,我们使用一个具有0个模板参数的模板。这应该确保内联始终可以工作

您可以在此处测试和修改此示例:explore.dgnu.org

现在,我们(GDC开发人员)可以在发布模式下轻松地将assert(...)重写为if(...) __builtin_unreachable()。但这可能会破坏一些代码,因此dmd应该首先实现这一点。

好吧,我真的不知道你想要什么?cxx2是解决方案更多信息

最新更新