了解 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 会在优化标志影响结果之前在编译过程的早期执行某些强度降低。
但是请注意,结果可能因编译器选项不同而不同。
我知道这是未定义的行为,实际上没有人编写这样的代码。但是我很好奇编译器会对这段代码做什么。
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 会在优化标志影响结果之前在编译过程的早期执行某些强度降低。
但是请注意,结果可能因编译器选项不同而不同。