In this recipe, we will see how sibling call optimization works in LLVM. Sibling call optimization can be looked at as an optimized tail call, the only constraint being that the functions should share a similar function signature, that is, matching return types and matching function arguments.
Write a test case for sibling call optimization, making sure that the caller and callee have the same calling conventions (in either C or fastcc), and that the call in the tail position is a tail call:
$ cat sibcall.ll declare i32 @bar(i32, i32) define i32 @foo(i32 %a, i32 %b, i32 %c) { entry: %0 = tail call i32 @bar(i32 %a, i32 %b) ret i32 %0 }
llc
tool to generate the assembly:$ llc sibcall.ll
cat
command:$ cat sibcall.s .text .file "sibcall.ll" .globl foo .align 16, 0x90 .type foo,@function foo: # @foo .cfi_startproc # BB#0: # %entry jmp bar # TAILCALL .Lfunc_end0: .size foo, .Lfunc_end0-foo .cfi_endproc .section ".note.GNU-stack","",@progbits
Sibling call optimization is a restricted version of tail call optimization that can be performed on tail calls without passing the tailcallopt
option. Sibling call optimization works in a similar way to tail call optimization, except that the sibling calls are automatically detected and do not need any ABI changes. The similarity needed in the function signatures is because when the caller function (which calls a tail recursive function) tries to clean up the callee's argument, after the callee has done its work, this may lead to memory leak if the callee exceeds the argument space to perform a sibling call to a function requiring more stack space for arguments.
3.145.73.207