【发布时间】:2022-01-18 00:33:49
【问题描述】:
我知道这是未定义的行为,实际上没有人编写这样的代码。不过我很好奇编译器会对这段代码做什么。
int n = 3;
n = ++n + ++n;
我同时使用 clang 和 gcc 进行编译以进行比较。 没有优化。这是从 clang 生成的程序集:
# clang -O0
movl $3, -4(%rbp)
movl -4(%rbp), %ecx
addl $1, %ecx
movl %ecx, -4(%rbp)
movl -4(%rbp), %edx
addl $1, %edx
movl %edx, -4(%rbp)
addl %edx, %ecx
movl %ecx, -4(%rbp)
它将3复制到一个寄存器中,递增它,然后再次复制这个递增的值并再次递增它,然后将(3+1) + (3+1+1)相加。这看起来很简单。
但是,我无法理解 GCC 正在做什么。这是生成的程序集:
# gcc -O0
movl $3, -4(%rbp)
addl $1, -4(%rbp)
addl $1, -4(%rbp)
sall -4(%rbp)
据我了解,它递增两次,然后左移一次(sall),即乘以 2。
我认为它注意到++n 在操作数的两边是相同的,所以它把它当作公因数。但是在那种情况下,为什么它会增加两次?
Clang 的版本给出了9,GCC 给出了10。 (考虑到 UB,任何结果都是可以接受的,但这证实了编译器内部逻辑的最终结果实际上是不同的。)
谁能解释一下 GCC 在这里试图完成什么?
【问题讨论】:
-
“我明白这是未定义的行为”——然后走开。试图理解为什么未定义的行为会如此表现是没有意义的。
-
不,我知道这是未定义的行为,我试图通过查看程序集来了解 GCC 试图完成的工作。
-
数字电子学中有一个术语叫做“无关紧要”——它是输入的组合,不应该发生。它非常有用,因为在这种情况下,逻辑可以做任何事情,以便针对合法输入进行优化。同样在这里,这段代码是“无关紧要的”,编译器正在做一些事情,如果代码是合法的,则会产生更优化的输出。
-
我认为由于此处未定义操作顺序,因此 GCC 会评估
++两次,然后将结果值相加优化n + n = 2n -
没有人能理解,因为没有人能知道这一行的作用,因为它是 Undefined Behaviour。
标签: c assembly gcc x86-64 undefined-behavior