5

I have the following code which compiles fine with the gcc command gcc ./example.c. The program itself calls the function "add_two" which simply adds two integers. To use the intel syntax within the extended assembly instructions I need to switch at first to intel and than back to AT&T. According to the gcc documentation it is possible to switch to intel syntax entirely by using gcc -masm=intel ./exmaple.

Whenever I try to compile it with the switch -masm=intel it won't compile and I don't understand why? I already tried to delete the instruction .intel_syntax but it still don't compile.

#include <stdio.h>

int add_two(int, int);

int main(){
     int src = 3;
     int dst = 5;
     printf("summe = %d \n", add_two(src, dst));
     return 0;
}

int add_two(int src, int dst){

    int sum;

    asm (
        ".intel_syntax;"  //switch to intel syntax
        "mov %0, %1;"
        "add %0, %2;"

        ".att_syntax;"  //switch to at&t syntax
        : "=r" (sum) //output
        : "r" (src), "r" (dst) //input
    );

    return sum;
}

The error message by compiling the above mentioned program with gcc -masm=intel ./example.c is:

tmp/ccEQGI4U.s: Assembler messages:
/tmp/ccEQGI4U.s:55: Error: junk `PTR [rbp-4]' after expression
/tmp/ccEQGI4U.s:55: Error: too many memory references for `mov'
/tmp/ccEQGI4U.s:56: Error: too many memory references for `mov' 
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Dennis
  • 965
  • 1
  • 9
  • 17
  • [How do you use gcc to generate assembly code in Intel syntax?](http://stackoverflow.com/q/199966/995714), [Can I use Intel syntax of x86 assembly with GCC?](http://stackoverflow.com/q/9347909/995714) what does it mean by "it won't compile"? What's the error? – phuclv Aug 15 '16 at 11:16
  • 7
    The most obvious thing in your example is that you use `-masm=intel` to tell _C_ to generate Intel assembler syntax, but before you end your extended assembler template you tell it to switch to at&t syntax with `.att_syntax`. The code generator has no idea you have done this in the template so is still emitting Intel . When passed to GNU assembler everything after `.att_syntax` will be Intel but you've told the assembler to treat it at AT&T. Remove `.att_syntax`. If you are always using `-masm=intel` you need not bother switching to Intel syntax at the beginning of the assembler template. – Michael Petch Aug 15 '16 at 13:53
  • 1
    You are right. Removing `.intel_syntax` and `.att_syntax` by using `gcc -masm=intel ./example` works. – Dennis Aug 15 '16 at 14:44
  • Bake it into your make file flags – Frank C. Aug 15 '16 at 18:54
  • 1
    You could have debugged this yourself by looking at the `gcc -S` output, and noticing that compiler-generated intel-syntax instructions came after your `.att_syntax` directive. The assembler error message would point you to the right line number. – Peter Cordes Aug 15 '16 at 21:08
  • 2
    A quirk: You might expect the compiler to use 3 registers here (2 in, 1 out). And yes, it might. But it might also decide to use the same register for both %0 and %2 (resulting in the wrong answer). Why would it? The compiler thinks of the `asm` as a single instruction that consumes the inputs and produces the outputs. If it's a single instruction, why "waste" one of the very few registers it has that it could be using for something else? To tell the compiler not to do reuse any regs, use "=&r" (aka [earlyclobber](https://gcc.gnu.org/onlinedocs/gcc/Modifiers.html)) for the output constraint. – David Wohlferd May 17 '19 at 00:48

2 Answers2

8

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
3

Note that -masm= also affects the default inline assembler syntax:

Output assembly instructions using selected dialect. Also affects which dialect is used for basic "asm" and extended "asm". Supported choices (in dialect order) are att or intel. The default is att. Darwin does not support intel.

That means that your first .intel_syntax directive is superfluous and the final .att_syntax is wrong because your GCC call compiles C to Intel assembler code.

IOW, either stick to -masm=intel or sandwich your inline Intel assembler code sections between .intel_syntax noprefix and .att_syntax prefix directives - but don't do both.

Note that the sandwich method isn't compatible with all inline assembler constraints - e.g. a constraint that involves m (i.e. memory operand) would insert an operand in ATT syntax which would yield an error like 'Error: junk (%rbp) after expression'. In those cases you have to use -masm=intel.

maxschlepzig
  • 35,645
  • 14
  • 145
  • 182
  • 1
    If you ever use an `"m"` or `"=m"` operand, you must use `-masm=intel`. Using `.intel_syntax noprefix` won't tell the compiler to substitute Intel-syntax addressing modes so you could get something like `add eax, (%rdi, %rsi, 4)` from `add eax, %0`. I think GAS may still accept `%eax` and so on register names in intel-noprefix: it means prefixes aren't required, not that they're rejected. Maybe also `$` on immediates? Less sure about that, but anyway, the sandwich method and only works for the most trivial cases; I wouldn't recommend it. – Peter Cordes Sep 28 '19 at 23:16
  • @PeterCordes Good point. I've updated my answer with a note. The OP's example doesn't use memory operands, though. – maxschlepzig Sep 29 '19 at 09:58
  • The OP already seems to know that `-masm=intel` affects inline asm syntax. The part they're missing is that it also affects compiler-generated asm (which is why `.att_syntax` breaks things). That's why I added test to that intro sentence in my edit. – Peter Cordes Sep 29 '19 at 10:19
  • And BTW, `-masm=intel` is the only thing that works with clang. The sandwich method fails unless you hard-code all the registers. If you try to use `.intel_syntax prefix` you get: ```error: '.intel_syntax prefix' is not supported: registers must not have a '%' prefix in .intel_syntax```. Otherwise with `.intel_syntax noprefix` you get an error on `inc %eax` from clang expanding `inc %0`, because `%eax` isn't a valid register name in Intel-syntax mode. https://godbolt.org/z/65yLdh. But GAS does accept it. I know the question is about GCC not clang, but sandwich makes your code less portable – Peter Cordes Sep 29 '19 at 10:23