Having inline assembly with a clobber list in a function marked naked seems dangerous as this will alter the stack but only at -O0 in the case below, higher optimisations level are fine. eg: $ clang --version clang version 3.9.0 @r276109 Target: x86_64-unknown-linux-gnu #include <stdlib.h> __attribute__((__naked__, __noinline__)) int foo(size_t) { __asm__ __volatile__ ( "ret" : : : "rdi" ); } $ clang -O0 -S test.cpp # BB#0: # %entry movq %rdi, -16(%rbp) # 8-byte Spill <- Stack altered #APP retq #NO_APP
Alternatively we could avoid generating code around inline asm if it appears in a naked function.
(In reply to comment #1) > Alternatively we could avoid generating code around inline asm if it appears > in a naked function. Yes, I think it depends whether clobbered list are meant to be supported inside naked functions, or are their behaviour undefined? This gcc documentation https://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html#ARM-Function-Attributes seems to suggest that only "basic asm" is allowed in a naked function, clobbered list are considered "extended asm" I think. Does it mean we could emit an undefined behaviour warning whenever a non "basic asm" statement is found in a naked function?
FTR, we've got a use for simple input arguments in inline asm in naked functions, like this: define void @f() #0 { entry: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(void ()* @f.cfi) unreachable } attributes #0 = { naked } We use this to emit CFI jumptables, see https://reviews.llvm.org/D28012 for more context. Without the input argument, we'd have to do asm name mangling in IR transform pass, which is ugly and wrong. Btw, win32 backend is not happy about any assembly in a naked function right now. target triple = "x86_64-pc-windows-msvc" define void @f() #0 { entry: call void asm "nop", ""() unreachable } attributes #0 = { naked } include/llvm/CodeGen/MachineFunction.h:452: bool llvm::MachineFunction::hasWinCFI() const: Assertion `HasWinCFI.hasValue() && "HasWinCFI not set yet!"' failed.
It looks like the register spill instruction is nothing to do with the inline assembler statement register clobber list. The extra instruction is a spill of a function input argument: Consider the source: __attribute__((__naked__, __noinline__)) void foo(SIZE_T x, SIZE_T y, SIZE_T z) { __asm__ __volatile__ ( "ret" : : : ); } It produces the following assembler: foo: # @foo # %bb.0: # %entry #APP retq #NO_APP movq %rsi, -8(%rbp) # 8-byte Spill movq %rdi, -16(%rbp) # 8-byte Spill movq %rdx, -24(%rbp) # 8-byte Spill As can be seen the spilled registers are the function input arguments. It happens only if the clang uses Fast registry allocator, which produces spill to the stack frame instruction for every function input argument. Other registry allocators in similar situation, mark those registers as "dead", replace them with "KILL %%reg" instructions and no machine instructions will be produced for them during the instruction selection phase. So, the question is, what is a best way to fix the issue? Disable spill instruction generation for "naked" function? Produce a warning message?
potential fix on Phabricator: https://reviews.llvm.org/D43542