2

In my guide book it says:

In inline assembly, Clobbered registers list is used to tell the compiler which registers we are using (So it can empty them before that).

Which I totally don't understand, why the compiler needs to know so? what's the problem of leaving those registers as is? did they meant instead to back them up and restore them after the assembly code.

Hope someone can provide an example as I spent hours reading about Clobbered registers list with no clear answers to this problem.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
algo
  • 101
  • 6
  • 2
    "Emptying" them is really inaccurate. It's much closer to "back them up and restore them". If you need to keep a value in a register that's about to be clobbered, you can preserve it around the function call site (or inline assembly run) using eg `push` and later `pop`. However, you can also arrange for the possibly clobbered register to simply not be in use any longer. Telling the compiler is needed so the compiler won't expect a value to persist that it wrote to a register before. – ecm Oct 05 '21 at 16:11
  • 4
    If you don't mark a register as clobbered, the compiler assumes that whatever it last put in it is still there. – Thomas Jager Oct 05 '21 at 16:16
  • 2
    @ecm: GCC is never going to use `push` / `pop` around an asm statement because that would be inefficient. Just like it won't do that around a non-inline function call. [What are callee and caller saved registers?](https://stackoverflow.com/a/56178078). – Peter Cordes Oct 05 '21 at 17:00
  • @PeterCordes - Never? https://godbolt.org/z/c4Ebh55de – David Wohlferd Oct 06 '21 at 00:39
  • @DavidWohlferd: Eh, fair point on that wording nitpick. If the rest of the function body is empty then push/pop at the start/end of the function is the same as push/pop around the asm statement. In your example it's *not* empty: there's an `xor eax,eax` between the push/pop. But with a `void` function you could make it happen: https://godbolt.org/z/Yf8neExGP. Still, it's only by coincidence that it's just around the asm statement; if you put two separate asm statements back to back, there's still only one push/pop rbx. (See the same Godbolt link). – Peter Cordes Oct 06 '21 at 01:51
  • If you go back to my link and comment out the asm (or even change the clobbered register to rcx), the push/pop goes away. The push/pop is clearly being added to account for the clobbered rbx. – David Wohlferd Oct 06 '21 at 03:48
  • Yes, the push/pop in the function prologue/epilogue is there because of an RBX clobber. My point is that GCC thinks of those push/pop as part of the prologue/epilogue of the whole function, not specifically around the asm statement. It does of course turn out that way in practice when there's no other code and only one asm statement, so you can get it to make asm like that. (Not @ notifying you since I don't think it's that interesting and I think you understand the distinction, just explaining my point for future readers who did just read this thread.) – Peter Cordes Aug 29 '22 at 02:06

1 Answers1

3

The problems you'd see from failing to tell the compiler about registers you modify would be exactly the same as if you wrote a function in asm that modified some call-preserved registers1. See more explanation and a partial example in Why should certain registers be saved? What could go wrong if not?

In GNU inline-asm, all registers are assumed preserved, except for ones the compiler picks for "=r" / "+r" or other output operands. The compiler might be keeping a loop counter in any register, or anything else that it's going to read later and expect it to still have the value it put there before the instructions from the asm template. (With optimization disabled, the compiler won't keep variables in registers across statements, but it will when you use -O1 or higher.)

Same for all memory except for locations that are part of an "=m" or "+m" memory output operand. (Unless you use a "memory" clobber.) See How can I indicate that the memory *pointed* to by an inline ASM argument may be used? for more details.

Footnote 1:
Unlike for a function, you should not save/restore any registers with your own instructions inside the asm template. Just tell the compiler about it so it can save/restore at the start/end of the whole function after inlining, and avoid having any values it needs in them. In fact, in ABIs with a red-zone (like x86-64 System V) using push/pop inside the asm would be destructive: Using base pointer register in C++ inline asm


The design philosophy of GNU C inline asm is that it uses the same syntax as the compiler internal machine-description files. The standard use-case is for wrapping a single instruction, which is why you need early-clobber declarations if the asm code in the template string doesn't read all its inputs before it writes some registers.

The template is a black box to the compiler; it's up to you to accurately describe it to the optimizing compiler. Any mistake is effectively undefined behaviour, and leaves room for the compiler to mess up other variables in the surrounding code, potentially even in functions that call this one if you modify a call-preserved register that the compiler wasn't otherwise using.

That makes it impossible to verify correctness just by testing. You can't distinguish "correct" from "happens to work with this surrounding code and set of compiler options". This is one reason why you should avoid inline asm unless the benefits outweigh the downsides and risk of bugs. https://gcc.gnu.org/wiki/DontUseInlineAsm

GCC just does a string substitution into the template string, very much like printf, and sends the whole result (including the compiler-generated instructions for the pure C code) to the assembler as a single file. Have a look on https://godbolt.org/ sometime; even if you have invalid instructions in the inline asm, the compiler itself doesn't notice. Only when you actually assemble will there be a problem. ("binary" mode on the compiler-explorer site.)


See also https://stackoverflow.com/tags/inline-assembly/info for more links to guides.

ecm
  • 2,583
  • 4
  • 21
  • 29
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847