Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Clang generating incorrect code for inline assembly?

I have some C code:

#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);
}

which I'm compiling and running on my (Intel) Mac laptop.

The output from the code seems to be different depending on whether I compile with (GNU) GCC or Clang. I compile with gnucc -o gnu-test test.c for GCC (I built gnucc from source on my Mac after downloading the source from https://gcc.gnu.org/install/download.html) and clang -o clang-test test.c for Clang (built-in macOS Clang).

On my Mac, with GNU, the result is 2 (which is what I expect). With Clang, the result is 140701838959608.

The Clang result seems wrong to me, but I'm also wondering if, perhaps, my inline assembly isn't quite correct and GCC just happens to not expose my error.

I tried out the same code on Compiler Explorer and the output there is also different for GCC (x86-64 GCC 13.2 gives 2) and Clang (x86-64 Clang 16.0.0 gives 140726522786920).

I tried disassembling the Clang binary with objdump -d:

clang-test:    file format mach-o 64-bit x86-64

Disassembly of section __TEXT,__text:

0000000100003f60 <_main>:
100003f60: 55                        pushq  %rbp
100003f61: 48 89 e5                  movq   %rsp, %rbp
100003f64: 48 83 ec 20               subq   $32, %rsp
100003f68: 89 7d fc                  movl   %edi, -4(%rbp)
100003f6b: 48 89 75 f0               movq   %rsi, -16(%rbp)
100003f6f: 48 c7 45 e8 00 00 00 00   movq   $0, -24(%rbp)
100003f77: 48 8d 45 e8               leaq   -24(%rbp), %rax
100003f7b: b9 02 00 00 00            movl   $2, %ecx
100003f80: 48 89 00                  movq   %rax, (%rax)
100003f83: 48 8b 75 e8               movq   -24(%rbp), %rsi
100003f87: 48 8d 3d 16 00 00 00      leaq   22(%rip), %rdi   ## 0x100003fa4 <_printf+0x100003fa4>
100003f8e: b0 00                     movb   $0, %al
100003f90: e8 09 00 00 00            callq  0x100003f9e <_printf+0x100003f9e>
100003f95: 31 c0                     xorl   %eax, %eax
100003f97: 48 83 c4 20               addq   $32, %rsp
100003f9b: 5d                        popq   %rbp
100003f9c: c3                        retq

Disassembly of section __TEXT,__stubs:

0000000100003f9e <__stubs>:
100003f9e: ff 25 5c 00 00 00         jmpq    *92(%rip)       ## 0x100004000 <_printf+0x100004000>

and 100003f80: 48 89 00 movq %rax, (%rax) seems to be the issue? Clang has the correct value in ecx and the correct address to write to in rax, but it does movq %rax, (%rax) instead of movq %rcx, (%rax)?

like image 261
David Avatar asked Dec 07 '25 05:12

David


1 Answers

Clang is generating correct code, but you specified the incorrect constraint on the input operand.

The constraint ("rax") is not interpreted as a register name. Instead, each letter in the constraint specifies an allowed operand type. The first letter here, r, allows using any general register, which makes the choice of rcx valid.

To constrain to the rax register, you need to use the "a" constraint. See the x86 section in the machine constraints page.

__asm__("movq %%rax, %0\n" : "=m" (anum.x) : "a"(2));
like image 181
interjay Avatar answered Dec 09 '25 16:12

interjay