GCC determines which operands are output vs. input from the colons, not inferred from lack of "="
or "+"
. Yes this is redundant, but no you can't do anything about it.
You can put multiple colons on the same line, for example
asm("" ::: "memory")
is a common way to write a compiler barrier. So it's not painful to include the necessary colons. You could write asm("..." :: "m"(input) );
if that would actually be safe without any outputs or clobbers.
But you normally need to tell the compiler about a register you modify, either with an output operand or a clobber, so you often need all three colons. Or if you don't write any registers, usually that's because you're wrapping some "system" instruction that has some kind of effect that you generally don't want the compiler to reorder memory accesses around. Like invlpg
. Or for example:
asm("clflush %0" ::"m"(*ptr) : "memory");
should have a memory clobber to order the cache-line flush after any earlier stores (which might have been to the same line).
You code has some syntax bugs, as well as correctness / UB, at least if you meant to add the memory operand, not a constant 0
.
In GNU C Extended Asm, the template string is very much like a printf format string for the compiler to substitute in operands where you use %something
. (And yes, it's just a dumb text substitution to create text to feed to the assembler, as
. GCC doesn't "understand" your asm, that's why you have to describe it accurately to the compiler using input/output/clobber constraints).
When you want a literal %
, like in register names in AT&T syntax such as %rcx
, you have to actually write %%rcx
.
(Normally it's best to avoid hard-coding registers; use dummy output operands to let the compiler pick which registers to use. You can even name them, like %[input]
)
I assume you meant to add the memory source operand to RCX. That would be %0
.
$0
is an immediate 0, i.e. RCX += 0
, so the instruction only actually modifies FLAGS.
Assuming you meant add %0, %%rcx
, your code writes a register (RCX). You must tell the compiler about registers / memory you modify. Otherwise it might have a C variable in RCX, and expect to read its value after the asm statement. So you need either a (dummy) output or a clobber anyway.
(If you did actually mean "add $0, %%rcx"
, then the only architectural effect is to set FLAGS. Inline asm for i386 / amd64 already implicitly has a "cc"
clobber so we don't have to tell the compiler about that side-effect.)
Your options for "add %0, %%rcx"
to be safe include:
Use a clobber:
asm ("add %0, %%rcx" // %0 expands the first operand, $0 was an immediate
:
: "Irm"(Example) /* input, also allow reg or 32-bit immediate */
: "rcx"
);
Use a dummy output operand (and make it volatile
, like it was implicitly when you had no output operand). Note that we get to omit the : clobbers
part of the asm statement.
uint64_t dummy;
asm volatile ("add %0, %%rcx"
: "=c"(dummy); // "c" forces picking cl/cx/ecx/rcx based on size
: "Irm"(Example)
);
// without volatile, the asm statement can be optimized away if you don't read dummy later
Note that push %%rcx ; pop %%rcx
around the add would not be safe: it modifies RSP which might affect the addressing mode the compiler picked for "m"(Example)
, and it steps on the red-zone below RSP.
See https://stackoverflow.com/tags/inline-assembly/info for more.