Clang 是否为内联汇编生成了错误的代码?

Dav*_*vid 17 c clang inline-assembly

我有一些 C 代码:

#include "stdio.h"

typedef struct num {
    unsigned long long x;
} num;

int main(int argc, char **argv) {
    struct num anum;
    anum.x = 0;
    __asm__("movq %%rax, %0\n" : "=m" (anum.x) : "rax"(2));
    printf("%llu\n",anum.x);
}
Run Code Online (Sandbox Code Playgroud)

我正在我的(英特尔)Mac 笔记本电脑上编译并运行它。

代码的输出似乎有所不同,具体取决于我使用 (GNU) GCC 还是 Clang 进行编译。gnucc -o gnu-test test.c我使用GCC(从https://gcc.gnu.org/install/download.htmlgnucc下载源代码后在 Mac 上从源代码构建)和Clang(内置 macOS Clang)进行编译。clang -o clang-test test.c

在我的 Mac 上,使用 GNU,结果是2(这是我所期望的)。使用 Clang,结果是140701838959608.

Clang 结果对我来说似乎是错误的,但我也想知道我的内联汇编是否不太正确,而 GCC 恰好没有暴露我的错误。

我在Compiler Explorer上尝试了相同的代码, GCC(x86-64 GCC 13.2 给出2)和 Clang(x86-64 Clang 16.0.0 给出)的输出也不同140726522786920

我尝试用以下命令反汇编 Clang 二进制文件objdump -d

#include "stdio.h"

typedef struct num {
    unsigned long long x;
} num;

int main(int argc, char **argv) {
    struct num anum;
    anum.x = 0;
    __asm__("movq %%rax, %0\n" : "=m" (anum.x) : "rax"(2));
    printf("%llu\n",anum.x);
}
Run Code Online (Sandbox Code Playgroud)

似乎100003f80: 48 89 00 movq %rax, (%rax)是问题所在?Clang 具有正确的值 inecx和要写入的正确地址 in rax,但它movq %rax, (%rax)代替了movq %rcx, (%rax)?

int*_*jay 29

Clang 正在生成正确的代码,但您对输入操作数指定了不正确的约束。

约束 ( "rax") 不被解释为寄存器名称。相反,约束中的每个字母指定一个允许的操作数类型。这里的第一个字母r,允许使用任何通用寄存器,这使得选择rcx有效。

要约束寄存器rax,需要使用"a"约束。请参阅机器限制页面中的 x86 部分。

__asm__("movq %%rax, %0\n" : "=m" (anum.x) : "a"(2));
Run Code Online (Sandbox Code Playgroud)

  • 太好了,谢谢你的澄清。这里有一个+1,我建议添加“`“rax”`不被视为寄存器名称,而是作为3个不同约束r,a和x的组合”和“当组合`r`(任何寄存器)和`a`(ax 型寄存器),`r` 胜出”答案本身。 (7认同)
  • @paxdiablo 是的。[GCC 文档](https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html) 说:最简单的约束是一个充满字母的字符串,每个字母描述一种操作数,即允许的。 (4认同)
  • @mtraceur `"rax"` 不被视为寄存器名称,而是被视为 3 个不同约束 r、a 和 x 的组合。对 rax、eax 或 ax(取决于操作数大小)指定约束的正确方法是使用“a”约束而不是“rax”。 (3认同)
  • interjay,所以我猜约束是并集而不是交集。换句话说,“r”(任何寄存器)和“a”(ax 类型寄存器)将是宽松的“r”而不是限制性的“a”? (3认同)
  • 附带说明一下:ia86 有一个实验性的 GCC 端口,它打破了一个字母 = 一个约束的传统。/sf/ask/4388038161/ (3认同)