How to Convert Basic asm to Extended asm
See the gcc docs for Basic asm and Extended asm for the exact syntax and a detailed description of these statements and their options.
Why Convert?
There are a number of reasons you might want to convert basic asm statements into extended asm:
- Extended asm allows the programmer to specify inputs and outputs for the asm as well as which registers it modifies (clobbers) while basic asm does not.
Different C compilers use different semantics regarding which registers the asm code can overwrite. gcc assumes no registers get modified. If your asm modifies registers without informing the compiler (which requires using extended asm), undefined behavior will result.
- Clobbering registers (which basic asm does not support) can give better performance than push/pop.
- Some compilers automatically flush registers to memory before invoking any asm (what gcc calls a "memory" clobber). However gcc's basic asm does not do this. If your asm requires this, you need to use the "memory" clobber in extended asm.
- gcc is considering changing the long-time semantics of basic asm. Instead of not clobbering anything, it may soon begin clobbering everything. This may fix subtle errors in existing code. However there is a (small) chance this will cause problems with existing, correctly function code. Further, this could also introduce performance issues as the contents of some/all registers (even ones your asm doesn't use) may need to be saved/reloaded around your asm statement. To "future-proof" your code against these types of changes, use extended asm and specify exactly what needs to be clobbered.
- Because of the fact that gcc's basic asm has no inputs, outputs or clobbers, it can be difficult for optimizers to consistently position basic asm in the generated code.
One last point: If you are going to be mucking with your inline asm, take a minute to consider not using inline asm at all. Inline asm is difficult to maintain, non-portable, and can actually be slower than using C.
That said...
How to Convert (short)
The steps for the conversion are pretty straight-forward. In brief:
- Add a ":" after the string of your basic asm statement.
- If your basic asm string includes '%' characters, escape them using '%%'.
- On platforms that support dialects (including i386, pa, pdp11, rs6000, sh), the characters '{' '|' and '}' also need to be escaped.
There are some quibbles and quirks, but that's the heart of it.
How to Convert (long)
1) It is possible to convert a basic inline asm statement to extended by simply adding a ":" on the end:
asm(""); /* basic */ asm("":); /* extended */
This will give you the same semantics in extended asm as you were getting in basic (ie perform NO clobbers).
Some people have assumed (incorrectly!) that basic asm performed a clobber on registers, memory or both. That has never been the case in gcc, although other compilers may be different. If the code you are changing assumes or needs a register or memory clobber, you should explicitly add it (for example asm("sync":::"memory");).
Note that all basic asm statements are implicitly volatile (whether they have the volatile keyword or not). Also, extended asm statements that have no outputs are also implicitly volatile. So the two statements above would still be the same if either or both contained the volatile keyword.
It may be better aesthetically to write this instead:
asm("" : : :);
Functionally it is the same as using a single colon, but it may suggest that you considered adding clobbers and deliberately chose not to. A comment saying that "no clobbers are needed" works even better (assuming they aren't).
2) Extended asm treats '%' as an token marker (rather like printf). If your basic asm string includes '%' characters, you will need to escape them using '%%' (for example asm("mov $0, %eax") becomes asm("mov $0, %%eax" : : :);).
3) On platforms that support dialects (including i386, pa, pdp11, rs6000, sh): The '{' '|' and '}' characters all have special meaning (see Multiple assembler dialects in asm templates). They will need to be escaped using '%{', '%|' or '%}' (for example, asm("mov '{', %al") becomes asm("mov '%{', %%al" : : :);).
For correctly written basic asm, that should be all that's needed.
Converting incorrect code
Note that for incorrectly written asm (which is very easy to do), you may need to do more work. For example, consider this MIPS code:
asm ("sync");
In a compiler that clobbers memory for asm statements, this would work correctly. However since gcc's basic asm doesn't clobber memory, it needs to be written something like this:
asm ("sync" : : : "memory");
For another example, consider this ARM code:
__asm__ __volatile__ ( "mov r0, #0x00\n\t" "vmsr fpscr, r0");
The code changes the r0 register without notifying the compiler. While this might be safe in some C compilers, it is incorrect for gcc. Consider one of these alternatives:
/* Clobber the register. */ __asm__ __volatile__ ( "mov r0, #0x00\n\t" "vmsr fpscr, r0": : : "r0"); /* Let GCC pick a register AND init it. */ __asm__ __volatile__ ( "vmsr fpscr, %0\n\t", ::"r"(0)); /* Don't use asm at all. */ __builtin_sh_set_fpscr(0);
Misc
Some unusual cases:
- On i386, extended asm always clobbers the flags, regardless of whether "cc" is specified or not.
- On the mep platform: basic asm clobbers all registers in functions marked as "interrupt." When converting such functions to extended, you should either clobber all registers (to get identical behavior) or clobber just the registers you need (for better performance).
- On ia64, extended asm may handle the stop bits differently. See also -mvolatile-asm-stop.