为什么这是尾声?

Why is this a tail call?

这是一个简单的 hello world:

#include <stdio.h>

int main() {
    printf("hello world\n");
    return 0;
}

这里编译为LLVM IR:

will@ox:~$ clang -S -O3 -emit-llvm ~/test_apps/hello1.c -o -
; ModuleID = '/home/will/test_apps/hello1.c'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@str = private unnamed_addr constant [12 x i8] c"hello world[=11=]"

; Function Attrs: nounwind uwtable
define i32 @main() #0 {
  %puts = tail call i32 @puts(i8* getelementptr inbounds ([12 x i8]* @str, i64 0, i64 0))
  ret i32 0
}

; Function Attrs: nounwind
declare i32 @puts(i8* nocapture readonly) #1

attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind }

!llvm.ident = !{!0}

!0 = !{!"Ubuntu clang version 3.6.0-2ubuntu1 (tags/RELEASE_360/final) (based on LLVM 3.6.0)"}

description of tail-call optimisation表示必须满足以下条件:

The call is a tail call - in tail position (ret immediately follows call and ret uses value of call or is void).

但是在这个例子中,由 puts() 编辑的值 return 不应用作函数的 return 值。

这是合法的尾调用优化吗? main() return 是什么意思?

LLVM 中的 tail 标志有点奇怪。这只是意味着对 puts 的调用是尾调用优化的候选者,特别是不允许访问调用者堆栈上的任何变量。代码生成器在真正将调用转为跳转之前,仍然必须确保调用处于适合尾调用优化的位置,而这里不是这种情况。

如果您查看 LLVM 发出的程序集,您会发现没有发生尾调用优化:

$ clang -O -S -o - bug.c
        [...]
main:                                   # @main
        .cfi_startproc
# BB#0:                                 # %entry
        pushq   %rax
.Ltmp0:
        .cfi_def_cfa_offset 16
        movl    $.Lstr, %edi
        callq   puts
        xorl    %eax, %eax
        popq    %rdx
        retq