How to Generate Assembly Code with Clang in Intel Syntax

How to generate assembly code with clang in Intel syntax?

As noted below by @thakis, newer versions of Clang (3.5+) accept the -masm=intel argument.


For older versions, this should get clang to emit assembly code with Intel syntax:

clang++ -S -mllvm --x86-asm-syntax=intel test.cpp

You can use -mllvm <arg> to pass in llvm options from the clang command line. Sadly this option doesn't appear to be well documented, and thus I only found it by browsing through the llvm mailing lists.

How to inline-assembly with Clang 11, intel syntax and substitution variables

I'm not aware of any good way to do this, I recommend AT&T syntax for GNU C inline asm (or dialect-alternatives add {%1,%0 | %0,%1} so it works both ways for GCC.) Options like -masm=intel don't get clang to substitute in bare register names the way they do for GCC.

(Update: clang 14 changes that: How to set gcc or clang to use Intel syntax permanently for inline asm() statements?)

How to generate assembly code with clang in Intel syntax? is about the syntax used for -S output, and unlike GCC it's not connected to the syntax for inline-asm input to the compiler. The behaviour of --x86-asm-syntax=intel hasn't changed: it still outputs in Intel syntax, and doesn't help you with inline asm.


You can abuse %V0 or %V[i] (instead of %0 or %[i]) to print the "naked" full-register name in the template https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#x86Operandmodifiers, but that sucks because it only prints the full register name. Even for a 32-bit int that picked EAX, it will print RAX instead of EAX.

(It also doesn't work for "m" memory operands to get dword ptr [rsp + 16] or whatever compiler's choice of addressing mode, but it's better than nothing. Although IMO it's not better than just using AT&T syntax.)


Or you could pick hard registers like "=a"(var) and then just explicitly use EAX instead of %0. But that's worse and defeats some of the optimization benefit of the constraint system.

You do still need ".intel_syntax noprefix\n" in your template, and you should end your template with ".att_syntax" to switch the assembler back to AT&T mode to assemble the later compiler-generated asm. (Needed if you want your code to work with GCC! clang's built-in assembler doesn't merge your inline asm text into one big asm text file before assembling, it goes straight to machine code for compiler-generated instructions.)


Obviously telling the compiler it can pick any register with "=r", and then actually using your own hard-coded choices, will create undefined behaviour when the compiler picks differently. You'll step on the compilers toes and corrupt values it wanted to use later, and have it take garbage from the wrong registers as the output. IDK why you bothered to include that in your question; that would break in exactly the same way for AT&T syntax for the same fairly obvious reason.

How do you use gcc to generate assembly code in Intel syntax?

Use -masm=intel

gcc -S -masm=intel -Og -fverbose-asm test.c

That works with GCC, and clang3.5 and later. GCC manual:

  • -masm=dialect

    Output asm instructions using selected dialect. Supported choices
    are intel or att (the default one). Darwin does not support intel.

For Mac OSX, note that by default, the gcc command actually runs clang. Modern clang supports -masm=intel as a synonym for this, but this always works with clang:

clang++ -S -mllvm --x86-asm-syntax=intel test.cpp

Note that until clang 14, this does not change how clang processes inline asm() statements, unlike for GCC.

These are the options used by Matt Godbolt's Compiler Explorer site by default: https://godbolt.org/

See also How to remove "noise" from GCC/clang assembly output? for other options and tips for getting asm output that's interesting to look at.

How to set gcc or clang to use Intel syntax permanently for inline asm() statements?

Use -masm=intel and don't use any .att_syntax directives in your inline asm. This works with GCC and I think ICC, and with any constraints you use. Other methods don't. (See Can I use Intel syntax of x86 assembly with GCC? for a simple answer saying that; this answer explores exactly what goes wrong, including with clang 13 and earlier.)

That also works in clang 14 and later. (Which isn't released yet but the patch is part of current trunk; see https://reviews.llvm.org/D113707).

Clang 13 and earlier would always use AT&T syntax for inline asm, both in substituting operands and in assembling as op src, dst. But even worse, clang -masm=intel would do that even when taking the Intel side of an asm template using dialect-alternatives like asm ("add {att | intel}" : ... )`!

clang -masm=intel did still control how it printed asm after its built-in assembler turned an asm() statement into some internal representation of the instruction. e.g. Godbolt showing clang13 -masm=intel turning add %0, 1 as add dword ptr [1], eax, but clang trunk producing add eax, 1.

Some of the rest of this answer talking about clang hasn't been updated for this new clang patch.

Clang does support Intel-syntax inside MSVC-style asm-blocks, but that's terrible (no constraints so inputs / outputs have to go through memory.

If you were hard-coding register names with clang, -masm=intel would be usable (or the equivalent -mllvm --x86-asm-syntax=intel). But it chokes on mov %eax, 5 in Intel-syntax mode so you can't let %0 expand to an AT&T-syntax register name.


-masm=intel makes the compiler use .intel_syntax noprefix at the top of its asm output file, and use Intel-syntax when generating asm from C outside your inline-asm statement. Using .att_syntax at the bottom of your asm template breaks the compiler's asm, hence the error messages like PTR [rbp-4] looking like junk to the assembler (which is expecting AT&T syntax).

The "too many operands for mov" is because in AT&T syntax, mov eax, ebx is a mov from a memory operand (with symbol name eax) to a memory operand (with symbol name ebx)


Some people suggest using .intel_syntax noprefix and .att_syntax prefix around your asm template. That can sometimes work but it's problematic. And incompatible with the preferred method of -masm=intel.

Problems with the "sandwich" method:

When the compiler substitutes operands into your asm template, it will do so according to -masm=. This will always break for memory operands (the addressing-mode syntax is completely different).

It will also break with clang even for registers. Clang's built-in assembler does not accept %eax as a register name in Intel-syntax mode, and doesn't accept .intel_syntax prefix (as opposed to the noprefix that's usually used with Intel-syntax).

Consider this function:

int foo(int x) {
asm(".intel_syntax noprefix \n\t"
"add %0, 1 \n\t"
".att_syntax"
: "+r"(x)
);
return x;
}

It assembles as follows with GCC (Godbolt):

        movl    %edi, %eax
.intel_syntax noprefix
add %eax, 1 # AT&T register name in Intel syntax
.att_syntax

The sandwich method depends on GAS accepting %eax as a register name even in Intel-syntax mode. GAS from GNU Binutils does, but clang's built-in assembler doesn't.

On a Mac, even using real GCC the asm output has to assemble with an as that's based on clang, not GNU Binutils.

Using clang on that source code complains:

<source>:2:35: error: unknown token in expression
asm(".intel_syntax noprefix \n\t"
^
<inline asm>:2:6: note: instantiated into assembly here
add %eax, 1
^

(The first line of the error message didn't handle the multi-line string literal very well. If you use ; instead of \n\t and put everything on one line the clang error message works better but the source is a mess.)


I didn't check what happens with "ri" constraints when the compiler picks an immediate; it will still decorate it with $ but IDK if GAS silently ignores that, too, in Intel syntax mode.


PS: your asm statement has a bug: you forgot an early-clobber on your output operand so nothing is stopping the compiler from picking the same register for the %0 output and the %2 input that you don't read until the 2nd instruction. Then mov will destroy an input.

But using mov as the first or last instruction of an asm-template is usually also a missed-optimization bug. In this case you can and should just use lea %0, [%1 + %2] to let the compiler add with the result written to a 3rd register, non-destructively. Or just wrap the add instruction (using a "+r" operand and an "r", and let the compiler worry about data movement.) If it had to load the value from memory anyway, it can put it in the right register so no mov is needed.


PS: it's possible to write inline asm that works with -masm=intel or att, using GNU C inline asm dialect alternatives. e.g.

void atomic_inc(int *p) {
asm( "lock add{l $1, %0 | %0, 1}"
: "+m" (*p)
:: "memory"
);
}

compiles with gcc -O2 (-masm=att is the default) to

atomic_inc(int*):
lock addl $1, (%rdi)
ret

Or with -masm=intel to:

atomic_inc(int*):
lock add DWORD PTR [rdi], 1
ret

Notice that the l suffix is required for AT&T, and the dword ptr is required for intel, because memory, immediate doesn't imply an operand-size. And that the compiler filled in valid addressing-mode syntax for both cases.

This works with clang, but only the AT&T version ever gets used.

How can i produce an object file from an assembler source in the same way as clang -c main.c does

as -o foo.o foo.s

If you have a .S, normally that means you want to run it through the C preprocessor before assembling. The gcc and clang front-ends will do that for you: gcc -c foo.S (default output filename foo.o, instead of a.out). If your .S doesn't actually have any CPP directives like #if or #define, though, you can just assemble it with as.


a.out is the default name for the output of as, but it is an object file like you'd get from gcc -c foo.s, not a linked executable! GNU Binutils as does not produce linked executables.

(The default output filename for ld foo.o or gcc / clang without -c is also a.out, but don't be fooled by the name.)

You can use gcc -v -c foo.s to show the as command line it uses, including the -o option. (clang has a built-in assembler, so it won't run a separate as command, but the gcc front-end truly does just run as to assemble asm source files. And without -c, then runs ld (via collect2) to link the object file into an executable.)

e.g. on my x86-64 GNU/Linux system:

$ cat > foo.s
mov $231, %eax # __NR_exit_group
syscall
$ as foo.s
$ file a.out
a.out: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
$ ld a.out -o exit
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000
$ ls -l exit a.out
-rw-r--r-- 1 peter peter 664 Mar 22 08:23 a.out
-rwxr-xr-x 1 peter peter 4632 Mar 22 08:23 exit
$ file exit
exit: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

$ ./a.out
bash: ./a.out: Permission denied
$ ./exit
$ strace ./exit
execve("./exit", ["./exit"], 0x7ffc72823fc0 /* 55 vars */) = 0
exit_group(0) = ?
+++ exited with 0 +++
$ as --version
GNU assembler (GNU Binutils) 2.35.1
...
This assembler was configured for a target of `x86_64-pc-linux-gnu'.
$ ld --version
GNU ld (GNU Binutils) 2.35.1
...


Related Topics



Leave a reply



Submit