【发布时间】:2023-03-15 16:24:02
【问题描述】:
在尝试to optimize return values on x86_64 时,我注意到了一件奇怪的事情。即,给定代码:
#include <cstdint>
#include <tuple>
#include <utility>
using namespace std;
constexpr uint64_t a = 1u;
constexpr uint64_t b = 2u;
pair<uint64_t, uint64_t> f() { return {a, b}; }
tuple<uint64_t, uint64_t> g() { return tuple<uint64_t, uint64_t>{a, b}; }
Clang 3.8 outputsf 的这个汇编代码:
movl $1, %eax
movl $2, %edx
retq
这是g:
movl $2, %eax
movl $1, %edx
retq
看起来最佳。但是,当compiled with GCC 6.1 时,虽然为f 生成的程序集与Clang 输出的相同,但为g 生成的程序集是:
movq %rdi, %rax
movq $2, (%rdi)
movq $1, 8(%rdi)
ret
看起来返回值的类型被 GCC 归类为 MEMORY,但被 Clang 归类为 INTEGER。我可以确认将 Clang 代码与 GCC 代码链接这样的代码会导致分段错误(Clang 调用 GCC 编译的 g() 写入到 %rdi 碰巧指向的任何地方)并返回无效值(GCC 调用 Clang 编译的 @ 987654337@)。哪个编译器有问题?
相关:
- G++ and clang++ incompatibility with standard library when building shared libraries?
- [cxx-abi-dev] Non-trivial move constructor
另见
【问题讨论】:
-
不是
movl32 位和movq64 位!? -
@DieterLücking:对于小于或等于
UINT32_MAX的正整数常量无关紧要,寄存器的高32位被隐式设置为零。 -
@DieterLücking
movl指令清除 64 位目标寄存器中的其余位。只是指令编码比使用movq的等效指令短1个字节。 -
@jotik:缩短 2 个字节。 5 字节
movl $imm32, %r32编码的 REX 版本是 10 字节movabs $imm64, %r64(REX + 操作码 + 8 字节立即数)。符号扩展movq $imm32, %r/m64形式为 7 个字节:它需要一个 mod/rm 字节,而不是将 dest 寄存器编码到操作码中。 (所以它可以存储到内存中,就像movq $2, (%rdi)正在做的那样)
标签: c++ tuples x86-64 abi compiler-bug