From 93db40a147a219f6b5a60bdfe34afae33545deed Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Sat, 12 Sep 2015 01:07:37 +0000 Subject: Always_inline codegen rewrite. Current implementation may end up emitting an undefined reference for an "inline __attribute__((always_inline))" function by generating an "available_externally alwaysinline" IR function for it and then failing to inline all the calls. This happens when a call to such function is in dead code. As the inliner is an SCC pass, it does not process dead code. Libc++ relies on the compiler never emitting such undefined reference. With this patch, we emit a pair of 1. internal alwaysinline definition (called F.alwaysinline) 2a. A stub F() { musttail call F.alwaysinline } -- or, depending on the linkage -- 2b. A declaration of F. The frontend ensures that F.inlinefunction is only used for direct calls, and the stub is used for everything else (taking the address of the function, really). Declaration (2b) is emitted in the case when "inline" is meant for inlining only (like __gnu_inline__ and some other cases). This approach, among other nice properties, ensures that alwaysinline functions are always internal, making it impossible for a direct call to such function to produce an undefined symbol reference. This patch is based on ideas by Chandler Carruth and Richard Smith. llvm-svn: 247494 --- clang/test/CodeGen/always-inline.c | 2 + clang/test/CodeGen/always_inline-unused.c | 31 ++++++++ clang/test/CodeGen/always_inline-wrappers.c | 108 ++++++++++++++++++++++++++++ clang/test/CodeGen/always_inline.c | 18 +++-- clang/test/CodeGen/function-attributes.c | 5 +- clang/test/CodeGen/pr9614.c | 10 +-- 6 files changed, 162 insertions(+), 12 deletions(-) create mode 100644 clang/test/CodeGen/always_inline-unused.c create mode 100644 clang/test/CodeGen/always_inline-wrappers.c (limited to 'clang/test/CodeGen') diff --git a/clang/test/CodeGen/always-inline.c b/clang/test/CodeGen/always-inline.c index c9fd1ae2d80..ed8ecc7bfa7 100644 --- a/clang/test/CodeGen/always-inline.c +++ b/clang/test/CodeGen/always-inline.c @@ -1,7 +1,9 @@ // RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s // RUN: %clang_cc1 -fno-inline -emit-llvm %s -o - | FileCheck %s +// CHECK-LABEL: define void @i_want_bar() // CHECK-NOT: foo +// CHECK: ret void void bar() { } diff --git a/clang/test/CodeGen/always_inline-unused.c b/clang/test/CodeGen/always_inline-unused.c new file mode 100644 index 00000000000..eb733f72c7e --- /dev/null +++ b/clang/test/CodeGen/always_inline-unused.c @@ -0,0 +1,31 @@ +// Test alwaysinline definitions w/o any non-direct-call uses. +// None of the declarations are emitted. Stub are only emitted when the original +// function can not be discarded. + +// RUN: %clang_cc1 -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s + +void __attribute__((__always_inline__)) f1() {} +inline void __attribute__((__always_inline__)) f2() {} +static inline void __attribute__((__always_inline__)) f3() {} +inline void __attribute__((gnu_inline, __always_inline__)) f4() {} +static inline void __attribute__((gnu_inline, __always_inline__)) f5() {} +inline void __attribute__((visibility("hidden"), __always_inline__)) f6() {} +inline void __attribute__((visibility("hidden"), gnu_inline, __always_inline__)) f7() {} + +void g() { + f1(); + f2(); + f3(); + f4(); + f5(); + f6(); + f7(); +} + +// CHECK: define void @f1() +// CHECK-NOT: void @f2() +// CHECK-NOT: void @f3() +// CHECK: define void @f4() +// CHECK-NOT: void @f5() +// CHECK-NOT: void @f6() +// CHECK: define hidden void @f7() diff --git a/clang/test/CodeGen/always_inline-wrappers.c b/clang/test/CodeGen/always_inline-wrappers.c new file mode 100644 index 00000000000..ba0b148a7c5 --- /dev/null +++ b/clang/test/CodeGen/always_inline-wrappers.c @@ -0,0 +1,108 @@ +// Test different kinds of alwaysinline definitions. + +// RUN: %clang_cc1 -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-INLINE +// RUN: %clang_cc1 -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-USE +// RUN: %clang_cc1 -disable-llvm-optzns -fno-inline -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK +// RUN: %clang_cc1 -disable-llvm-optzns -fno-inline -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK-USE + +void __attribute__((__always_inline__)) f1() {} +inline void __attribute__((__always_inline__)) f2() {} +static inline void __attribute__((__always_inline__)) f3() {} +inline void __attribute__((gnu_inline, __always_inline__)) f4() {} +static inline void __attribute__((gnu_inline, __always_inline__)) f5() {} +inline void __attribute__((visibility("hidden"), __always_inline__)) f6() {} +inline void __attribute__((visibility("hidden"), gnu_inline, __always_inline__)) f7() {} + +void g() { + f1(); + f2(); + f3(); + f4(); + f5(); + f6(); + f7(); +} + +void (*p)(void); +void h() { + p = f1; + p = f2; + p = f3; + p = f4; + p = f5; + p = f6; + p = f7; +} + +void (*const cp1)(void) = f1; +void (*p1)(void) = f1; +void (*p2)(int) = (void (*)(int))f1; + +void __attribute__((__always_inline__)) f8(void(*f)(void)) {} + +void call() { + f8(f1); +} + +// CHECK-DAG: define internal void @f1.alwaysinline() #[[AI:[0-9]+]] +// CHECK-DAG: define internal void @f2.alwaysinline() #[[AI_IH:[0-9]+]] +// CHECK-DAG: define internal void @f3.alwaysinline() #[[AI_IH]] +// CHECK-DAG: define internal void @f4.alwaysinline() #[[AI_IH]] +// CHECK-DAG: define internal void @f5.alwaysinline() #[[AI_IH]] +// CHECK-DAG: define internal void @f6.alwaysinline() #[[AI_IH]] +// CHECK-DAG: define internal void @f7.alwaysinline() #[[AI_IH]] + + +// CHECK-DAG: define void @f1() #[[NOAI:[01-9]+]] +// CHECK-DAG: musttail call void @f1.alwaysinline() + +// CHECK-DAG: declare void @f2() #[[NOAI:[01-9]+]] + +// CHECK-DAG: define internal void @f3() #[[NOAI:[01-9]+]] +// CHECK-DAG: musttail call void @f3.alwaysinline() + +// CHECK-DAG: define void @f4() #[[NOAI:[01-9]+]] +// CHECK-DAG: musttail call void @f4.alwaysinline() + +// CHECK-DAG: define internal void @f5() #[[NOAI:[01-9]+]] +// CHECK-DAG: musttail call void @f5.alwaysinline() + +// CHECK-DAG: declare hidden void @f6() #[[NOAI:[01-9]+]] + +// CHECK-DAG: define hidden void @f7() #[[NOAI:[01-9]+]] +// CHECK-DAG: musttail call void @f7.alwaysinline() + + +// CHECK-DAG: @cp1 = constant void ()* @f1, align +// CHECK-DAG: @p1 = global void ()* @f1, align +// CHECK-DAG: @p2 = global void (i32)* bitcast (void ()* @f1 to void (i32)*), align + +// CHECK: attributes #[[AI]] = {{.*alwaysinline.*}} +// CHECK-INLINE: attributes #[[AI_IH]] = {{.*alwaysinline.*inlinehint.*}} +// CHECK-NOT: attributes #[[NOAI]] = {{.*alwaysinline.*}} + +// CHECK-USE-LABEL: define void @g() +// CHECK-USE-NOT: ret void +// CHECK-USE: call void @f1.alwaysinline() +// CHECK-USE-NEXT: call void @f2.alwaysinline() +// CHECK-USE-NEXT: call void @f3.alwaysinline() +// CHECK-USE-NEXT: call void @f4.alwaysinline() +// CHECK-USE-NEXT: call void @f5.alwaysinline() +// CHECK-USE-NEXT: call void @f6.alwaysinline() +// CHECK-USE-NEXT: call void @f7.alwaysinline() +// CHECK-USE-NEXT: ret void + +// CHECK-USE-LABEL: define void @h() +// CHECK-USE-NOT: ret void +// CHECK-USE: store void ()* @f1, +// CHECK-USE-NEXT: store void ()* @f2, +// CHECK-USE-NEXT: store void ()* @f3, +// CHECK-USE-NEXT: store void ()* @f4, +// CHECK-USE-NEXT: store void ()* @f5, +// CHECK-USE-NEXT: store void ()* @f6, +// CHECK-USE-NEXT: store void ()* @f7, +// CHECK-USE-NEXT: ret void + +// CHECK-USE-LABEL: define void @call() +// CHECK-USE: call void @f8.alwaysinline(void ()* @f1) +// CHECK-USE: ret void diff --git a/clang/test/CodeGen/always_inline.c b/clang/test/CodeGen/always_inline.c index c91fd43f276..00b4234dc71 100644 --- a/clang/test/CodeGen/always_inline.c +++ b/clang/test/CodeGen/always_inline.c @@ -1,8 +1,5 @@ -// RUN: %clang -emit-llvm -S -o %t %s -// RUN: not grep '@f0' %t -// RUN: not grep 'call ' %t -// RUN: %clang -mllvm -disable-llvm-optzns -emit-llvm -S -o %t %s -// RUN: grep '@f0' %t | count 2 +// RUN: %clang -target x86_64-pc-linux-gnu -emit-llvm -S -o - %s | FileCheck %s +// RUN: %clang -target x86_64-pc-linux-gnu -mllvm -disable-llvm-optzns -emit-llvm -S -o - %s | FileCheck %s --check-prefix=CHECK-NO-OPTZNS //static int f0() { static int __attribute__((always_inline)) f0() { @@ -18,3 +15,14 @@ inline int f2() __attribute__((always_inline)); int f2() { return 7; } int f3(void) { return f2(); } +// CHECK-LABEL: define i32 @f1() +// CHECK: ret i32 1 +// CHECK-LABEL: define i32 @f2() +// CHECK: ret i32 7 +// CHECK-LABEL: define i32 @f3() +// CHECK: ret i32 7 + +// CHECK-NO-OPTZNS: define i32 @f3() +// CHECK-NO-OPTZNS-NOT: ret i32 +// CHECK-NO-OPTZNS: call i32 @f2.alwaysinline() +// CHECK-NO-OPTZNS-NEXT: ret i32 diff --git a/clang/test/CodeGen/function-attributes.c b/clang/test/CodeGen/function-attributes.c index 177ad848f74..4a7d4fc0edb 100644 --- a/clang/test/CodeGen/function-attributes.c +++ b/clang/test/CodeGen/function-attributes.c @@ -25,8 +25,8 @@ void f6(signed short x) { } void f7(unsigned short x) { } -// CHECK-LABEL: define void @f8() -// CHECK: [[AI:#[0-9]+]] +// CHECK: define void @f8() +// CHECK: [[NUW:#[0-9]+]] // CHECK: { void __attribute__((always_inline)) f8(void) { } @@ -129,7 +129,6 @@ void f20(void) { } // CHECK: attributes [[NUW]] = { nounwind optsize readnone{{.*}} } -// CHECK: attributes [[AI]] = { alwaysinline nounwind optsize readnone{{.*}} } // CHECK: attributes [[ALIGN]] = { nounwind optsize readnone alignstack=16{{.*}} } // CHECK: attributes [[RT]] = { nounwind optsize returns_twice{{.*}} } // CHECK: attributes [[NR]] = { noreturn nounwind optsize } diff --git a/clang/test/CodeGen/pr9614.c b/clang/test/CodeGen/pr9614.c index 63cb5af1868..d264ef3a9ee 100644 --- a/clang/test/CodeGen/pr9614.c +++ b/clang/test/CodeGen/pr9614.c @@ -1,4 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-pc-linux -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-pc-linux -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK0 +// RUN: %clang_cc1 -triple x86_64-pc-linux -O1 -disable-llvm-optzns -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK1 extern void foo_alias (void) __asm ("foo"); inline void foo (void) { @@ -24,7 +25,7 @@ extern inline __attribute__((__always_inline__, __gnu_inline__)) void *memchr(vo void f(void) { foo(); - abs(0); + int x = abs(0); strrchr_foo("", '.'); prefetch(); memchr("", '.', 0); @@ -32,9 +33,10 @@ void f(void) { // CHECK-LABEL: define void @f() // CHECK: call void @foo() -// CHECK: call i32 @abs(i32 0) +// CHECK: call i32 @abs( // CHECK: call i8* @strrchr( -// CHECK: call void @llvm.prefetch( +// CHECK0: call void @llvm.prefetch( +// CHECK1: call void @prefetch.alwaysinline( // CHECK: call i8* @memchr( // CHECK: ret void -- cgit v1.2.3