0

In inline assembly the first : refers to output and the second to input, what if I don't want to use the output? can I leave it empty like this:

asm ("add $0, %rcx"
:
:"m"(Example) /* intput */
);

Plus if I want to use output only can I delete the other :?

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

2 Answers2

1

You can leave the output part empty and omit colons if all parts after the colons are empty.

Quote from Extended Asm (Using the GNU Compiler Collection (GCC)):

asm asm-qualifiers ( AssemblerTemplate 
                 : OutputOperands 
                 [ : InputOperands
                 [ : Clobbers ] ])

If there are no output operands but there are input operands, place two consecutive colons where the output operands would go:

__asm__ ("some instructions"
   : /* No outputs. */
   : "r" (Offset / 8));

Note that you normally need to tell the compiler about a register you modify, either with an output operand or a clobber. You can put multiple colons on the same line, for example
asm("" ::: "memory") is a common way to write a compiler barrier.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • This is a dangerous answer; you still have to tell the compiler about all registers you step on. For example, the code in the question could use an `"rcx"` clobber instead of a dummy `"=c"` output, but one or the other is required, otherwise you step on the compiler's toes (UB). – Peter Cordes May 21 '21 at 18:10
  • I maybe should have just written a separate answer; I noticed more problems with the code in the question while I was editing your answer to mention that, and ended up writing significantly more than just about an RCX clobber. I'm fine contributing that to your answer, but let me know if you don't want it there; I can copy it to a separate answer. – Peter Cordes May 21 '21 at 18:31
  • @PeterCordes It looks like another answer for me and I think your answer should be yours. Please separate your answer. – MikeCAT May 21 '21 at 18:33
1

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.

MikeCAT
  • 73,922
  • 11
  • 45
  • 70
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847