了解 GCC 针对 UB n = ++n + ++n 的未优化程序集 - 为什么在移位之前递增两次?

Understanding GCC's un-optimized assembly for UB n = ++n + ++n - why increment twice before shifting?

我知道这是未定义的行为,实际上没有人编写这样的代码。但是我很好奇编译器会对这段代码做什么。

int n = 3;
n = ++n + ++n;

为了比较,我同时使用了 clang 和 gcc 进行了编译。 没有优化。这是从 clang 生成的程序集:

# clang -O0
movl    , -4(%rbp)
movl    -4(%rbp), %ecx
addl    , %ecx
movl    %ecx, -4(%rbp)
movl    -4(%rbp), %edx
addl    , %edx
movl    %edx, -4(%rbp)
addl    %edx, %ecx
movl    %ecx, -4(%rbp)

它正在复制寄存器中的 3,递增它,然后再次复制这个递增的值并再次递增它,然后加起来 (3+1) + (3+1+1)。这看起来很简单。

但是我无法理解 GCC 在做什么。这是生成的程序集:

# gcc -O0
movl    , -4(%rbp)
addl    , -4(%rbp)
addl    , -4(%rbp)
sall    -4(%rbp)

据我了解,它是自增两次,然后左移(sall)一次,即乘以2。

我认为它注意到 ++n 在操作数的两边是相同的,所以它把它作为公因数。但是,在那种情况下,为什么它会增加 两次

Clang 版本给出 9 而 GCC 给出 10。 (给定 UB,任何结果都是可以接受的,但这证实了编译器内部逻辑的最终结果实际上是不同的。)

谁能解释一下 GCC 在这里试图完成什么?

一元运算符++表示它的操作数在计算之前要递增。 Clang 会这样解释您的表达式:

n = n + 1
tmp1 = n
n = n + 1
tmp2 = n
n = tmp1 + tmp2

而 GCC 做的是类似的事情,在下降到表达式之前处理预增量:

n = n + 1
n = n + 1
tmp1 = n
tmp2 = n
n = tmp1 + tmp2

然后,意识到 + 的两个操作数是相同的表达式,它执行强度降低产生

n = n + 1
n = n + 1
n = n << 1

尽管缺少优化标志,但仍可能会执行此强度降低,因为众所周知,GCC 会在优化标志影响结果之前在编译过程的早期执行某些强度降低。

但是请注意,结果可能因编译器选项不同而不同。