Why LR is saved before calling a 'noreturn' function ?

Dear all,

I don't get how llvm handles functions with __attribute__((noreturn)).
It seems that LR register is backed up on the stack whilst it will never be used to return from a 'noreturn' function.
I have this problem with a home-made backend but it seems that ARM flavour of clang has same behaviour.
By the way, SP is also saved, I don't understand why.

Is there a syntax error in my C code snippet ?
Any idea on how to solve it in llvm ?

### C code snippet
$ cat noreturn.c
extern void bar(int* a) __attribute__((noreturn));

void foo(int* a) __attribute__ ((noreturn));
void foo(int* a){
  bar(a);
  *a=42;
}

### Generated assembly with target ARM
$ clang --target=arm -S -O1 noreturn.c -o noreturn.s && cat noreturn.s
clang: warning: unknown platform, assuming -mfloat-abi=soft
clang: warning: unknown platform, assuming -mfloat-abi=soft
  .text
  .syntax unified
  .eabi_attribute 67, "2.09" @ Tag_conformance
  .cpu arm7tdmi
  .eabi_attribute 6, 2 @ Tag_CPU_arch
  .eabi_attribute 8, 1 @ Tag_ARM_ISA_use
  .eabi_attribute 17, 1 @ Tag_ABI_PCS_GOT_use
  .eabi_attribute 20, 1 @ Tag_ABI_FP_denormal
  .eabi_attribute 21, 1 @ Tag_ABI_FP_exceptions
  .eabi_attribute 23, 3 @ Tag_ABI_FP_number_model
  .eabi_attribute 34, 0 @ Tag_CPU_unaligned_access
  .eabi_attribute 24, 1 @ Tag_ABI_align_needed
  .eabi_attribute 25, 1 @ Tag_ABI_align_preserved
  .eabi_attribute 38, 1 @ Tag_ABI_FP_16bit_format
  .eabi_attribute 18, 4 @ Tag_ABI_PCS_wchar_t
  .eabi_attribute 26, 2 @ Tag_ABI_enum_size
  .eabi_attribute 14, 0 @ Tag_ABI_PCS_R9_use
  .file "noreturn.c"
  .globl foo
  .align 2
  .type foo,%function
foo: @ @foo
  .fnstart
@ BB#0: @ %entry
  push {r11, lr}
  mov r11, sp
  bl bar
.Lfunc_end0:
  .size foo, .Lfunc_end0-foo
  .cantunwind
  .fnend

  .ident "clang version 3.8.0 (git@git-lialp.intra.cea.fr:IL222352/wuc-clang.git d8f7ebf3bc146bce1b1b5e567484a56b60f2ec44) (git@git-lialp.intra.cea.fr:IL222352/wuc-llvm.git d2234a333ed3f101488a33ed3f58e2769cb8fda6)"
  .section ".note.GNU-stack","",%progbits

### Just to make sure : LLVM representation of the function has the attribute noreturn
$ clang --target=arm -S -emit-llvm -O1 noreturn.c -o noreturn.ll && cat noreturn.ll
; ModuleID = 'noreturn.c'
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
target triple = "armv4t--"

; Function Attrs: noreturn nounwind
define arm_aapcscc void @foo(i32* %a) #0 {
entry:
  tail call arm_aapcscc void @bar(i32* %a) #2
  unreachable
}

; Function Attrs: noreturn
declare arm_aapcscc void @bar(i32*) #1

attributes #0 = { noreturn nounwind "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="arm7tdmi" "target-features"="+soft-float,+strict-align,-crypto,-neon" "unsafe-fp-math"="false" "use-soft-float"="true" }
attributes #1 = { noreturn "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="arm7tdmi" "target-features"="+soft-float,+strict-align,-crypto,-neon" "unsafe-fp-math"="false" "use-soft-float"="true" }
attributes #2 = { noreturn nounwind }

!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 1, !"min_enum_size", i32 4}
!2 = !{!"clang version 3.8.0 (git@git-lialp.intra.cea.fr:IL222352/wuc-clang.git d8f7ebf3bc146bce1b1b5e567484a56b60f2ec44) (git@git-lialp.intra.cea.fr:IL222352/wuc-llvm.git d2234a333ed3f101488a33ed3f58e2769cb8fda6)"}

Hi Frédéric,

I don't get how llvm handles functions with __attribute__((noreturn)).
It seems that LR register is backed up on the stack whilst it will never be used to return from a 'noreturn' function.

In your case, Clang is producing a proper frame record. r11 is set to
the address of a struct roughly this form:

struct {
  void *previous_r11;
  void *return_address;
};

This is necessary for producing a coherent backtraces and other
usability features.

It could be dropped with -fomit-frame-pointer, but we don't seem to
take that opportunity. The code inserting that prologue is very late
(and very fragile anyway) and it just happens not to bother with that
optimization.

The main place noreturn is used is for larger-scale optimizations
earlier on (eliminating code after a noreturn call as dead, for
example).

I have this problem with a home-made backend but it seems that ARM flavour of clang has same behaviour.

To fix it, you'll want to look into XYZFrameLowering.cpp (and possibly
code that ends up marking your LR as callee-saved in
XYZCallingConv.td).

By the way, SP is also saved, I don't understand why.

This is the frame pointer.

Cheers.

Tim.

Hi Tim,

De : Tim Northover [mailto:t.p.northover@gmail.com]
In your case, Clang is producing a proper frame record. r11 is set to the
address of a struct roughly this form:

struct {
  void *previous_r11;
  void *return_address;
};

This is necessary for producing a coherent backtraces and other usability
features.

If I get your point, this additional instructions are need by gdb to rebuild the call stack.
Strange that we cannot remove it if we want to save every tiny bit in a memory-constraint target.
-fomit-frame-pointer does not change this.

To fix it, you'll want to look into XYZFrameLowering.cpp (and possibly code
that ends up marking your LR as callee-saved in XYZCallingConv.td).

You got it, LR was wrongly marked as callee-saved here.
Thanks for you help.

To keep the stack aligned?

Joerg