llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.21k stars 11.64k forks source link

how to remove unnecessary prologue for noreturn function #55317

Open zq1997 opened 2 years ago

zq1997 commented 2 years ago

If the function never returns, saving the callee-save registers is unnecessary. Then, how to disable these pushs?

// noreturn.c
int bar(int, int, int);

__attribute__ ((noreturn)) int foo(int a, int b, int c) {
    bar(bar(a, a, a), bar(b, b, b), bar(c, c, c));
    while(1) {}
}
clang -c -O3 -fno-unwind-tables noreturn.c
objdump -d noreturn.o
0000000000000000 <foo>:
   0:   55                      push   %rbp     <- unnecessary
   1:   41 56                   push   %r14     <- unnecessary
   3:   53                      push   %rbx     <- unnecessary
   4:   89 d3                   mov    %edx,%ebx
   6:   89 f5                   mov    %esi,%ebp
   8:   89 fe                   mov    %edi,%esi
   a:   89 fa                   mov    %edi,%edx
   c:   e8 00 00 00 00          call   11 <foo+0x11>
  11:   41 89 c6                mov    %eax,%r14d
  14:   89 ef                   mov    %ebp,%edi
  16:   89 ee                   mov    %ebp,%esi
  18:   89 ea                   mov    %ebp,%edx
  1a:   e8 00 00 00 00          call   1f <foo+0x1f>
  1f:   89 c5                   mov    %eax,%ebp
  21:   89 df                   mov    %ebx,%edi
  23:   89 de                   mov    %ebx,%esi
  25:   89 da                   mov    %ebx,%edx
  27:   e8 00 00 00 00          call   2c <foo+0x2c>
  2c:   44 89 f7                mov    %r14d,%edi
  2f:   89 ee                   mov    %ebp,%esi
  31:   89 c2                   mov    %eax,%edx
  33:   e8 00 00 00 00          call   38 <foo+0x38>
  38:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  3f:   00 
  40:   eb fe                   jmp    40 <foo+0x40>
llvmbot commented 2 years ago

@llvm/issue-subscribers-clang-codegen

xgupta commented 1 year ago

foo is calling bar which is not marked as noreturn, that's why it saves the callee-save registers.

jnk0le commented 1 year ago

foo is calling bar which is not marked as noreturn, that's why it saves the callee-save registers.

It doesn't need to care, it's not going to return anyway.

Infinite loop at the end seems to do the same thing as noreturn attribute.

From what I know there is no way to get rid of the prologues in such cases. The only thing that has to remain is stack allocation unless it can be proven that the stack won't underflow, stack allocated varisbles from lower level are not in use and there are no stack canaries involved. (gcc naked attr removes also that, so it's not a solution)

while(1) {}

UB in C++ BTW

int foo() { }

is another UB, should not cause issues in this case with recent version of llvm/gcc

jnk0le commented 1 year ago

According to gcc list keeping prologue is intentionsal to allow backtracing (abort() etc.) and throwing exceptions (under -fno-exceptions of course)

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56165#c2

The only solution is to introduce new attribute be it noreturn_noexcept_nobacktrace or some kind of custom ABI/calling convention attribute or abuse "prestacked annotation" I have proposed to solve risc-v interrupts. (e.g. __attribute__((prestacked("x1,x5-x31,f0-f31,fcsr"))) for an infinite loop function)

jnk0le commented 11 months ago

I have sent an official RFC for prestacked annotation https://discourse.llvm.org/t/rfc-prestacked-annotation-to-solve-risc-v-interrupt-stacking-mess/74120

That would solve the noreturn bloat if implemented on all archs.