0

I'm just getting started with LLVM. Consider the following programs.

hello1.c

#include <stdio.h>

int main(void)
{
        printf("Hello, world!\n");
}

hello2.c

#include <stdio.h>

int main(void)
{
        printf("Hello, world!\n");
        return 0;
}

These generate the following LLVM IR strings

$ clang -S -emit-llvm -o hello1.ll hello1.c
$ clang -S -emit-llvm -o hello2.ll hello2.c
$ clang --version
Debian clang version 11.0.1-2

hello1.ll

; ModuleID = 'res/c/hello.c'
source_filename = "res/c/hello.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@.str = private unnamed_addr constant [15 x i8] c"Hello, world!\0A\00", align 1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
  %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str, i64 0, i64 0))
  ret i32 0
}

declare dso_local i32 @printf(i8*, ...) #1

attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

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

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"Debian clang version 11.0.1-2"}

hello2.ll

; ....

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str, i64 0, i64 0))
  ret i32 0
}

; ....

As you can see, the addition of the return 0 appears to generate the allocation of a new stack temporary %1 with value 0. It doesn't seem that this value is used anywhere. Even if I change the return stmt to, e.g., return -1, there is still the allocation and zeroing of the temporary %1, and the constant literal only appears in the return instruction ret i32 -1.

I know that this will be inconsequential due to optimizations (e.g., live variable analysis). Is this an idiosyncrasy of the clang handling of return statements or is there a deeper reason for this?

Jonathan Lam
  • 16,831
  • 17
  • 68
  • 94
  • 1
    It's just that clang doesn't know that it'll be unused at the point where it's made, and so it does something that has no cost and is simple. – arnt Nov 12 '21 at 09:37
  • @arnt You're right, this is a duplicate. In particular, it's the possibility for multiple return statements that probably causes `%1` to be allocated. – Jonathan Lam Nov 12 '21 at 19:34

0 Answers0