假设我有一个像这样的虚拟 C 函数:

void Dummy(uint64* dest, const size_t count)
{
  for (size_t ii = 0; ii < count; ii += 8) {
    *dest++ = (uint64)dest;
  }
}

如果您查看编译器创建的内容,第一条指令将检查的值count是否不为零。

Dummy(unsigned long*, unsigned long):
        cbz     x1, .LBB0_7
        ...
.LBB0_7:
        ret

是否有一个属性可以放置,count以表明该值保证永远不会为零,并且可以省略这种不必要的比较? 还有其他可以部署的编码风格吗?

for循环改为while循环和其他形式似乎并不重要。编译器很聪明!

是的,我确实很关心这个小小的指令。😊

10

  • 5
    C 还是 C++?它们是截然不同的语言。C++ 可能有办法做到这一点,而 C 几乎肯定没有。


    – 

  • 1
    我根本不知道有什么办法可以让编译器做出这样的假设。如果假设是错误的,会发生什么?


    – 

  • 1
    @MarkRansom UB,显然如此。


    – 

  • 2
    @Barmar,也许这就是解决方案。如果您创建一种在参数为零时提前调用 UB 的情况,则编译器将能够推断出它不能为零。


    – 

  • 4
    C++23 有专门针对此的属性。虽然我不知道它是否已经在编译器中合理实现


    – 


最佳答案
2

GCC 具有assume以下属性:

void Dummy(uint64_t* dest, const size_t count)
{
    __attribute__((assume(count != 0)));
    for (size_t ii = 0; ii < count; ii += 8) 
    {
        *dest++ = (uint64_t)dest;
    }
}

然后支票就消失了。

2

  • 4
    自 C++23 以来一直是标准。godbolt.org


    – 


  • 1
    @FrançoisAndrieux 并非每个人都能将 C++23 用于项目中。


    – 

将表达式放在1/count函数中的某个位置。除以零会导致未定义的行为。由于编译器可以假设 UB 永远不会发生,这意味着count不能为零。

3

  • 谢谢@Barmar。这很聪明,但我选择另一种方法。它看起来更简洁一些。


    – 

  • 1
    当然,尽管该解决方案特定于编译器,但理论上它应该适用于任何足够智能的优化器。


    – 

  • 实际上@Barmar,我必须使用的编译器似乎不支持该属性,所以我使用你的方法,用一个巨大的注释块来解释我正在做的事情。


    –