This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG20faf789199d: [ThinLTO] Add noRecurse and noUnwind thinlink
function attribute propagation (authored by modimo).
Changed prior to commit:
https://reviews.llvm.org/D36850?vs=374934&id=375367#toc
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D36850/new/
https://reviews.llvm.org/D36850
Files:
clang/test/CodeGen/thinlto-distributed-cfi-devirt.ll
clang/test/CodeGen/thinlto-distributed-cfi.ll
clang/test/CodeGen/thinlto-funcattr-prop.ll
llvm/include/llvm/AsmParser/LLToken.h
llvm/include/llvm/IR/GlobalValue.h
llvm/include/llvm/IR/ModuleSummaryIndex.h
llvm/include/llvm/LTO/LTO.h
llvm/include/llvm/Transforms/IPO/FunctionAttrs.h
llvm/include/llvm/Transforms/IPO/FunctionImport.h
llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
llvm/lib/AsmParser/LLLexer.cpp
llvm/lib/AsmParser/LLParser.cpp
llvm/lib/Bitcode/Reader/BitcodeReader.cpp
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
llvm/lib/IR/AsmWriter.cpp
llvm/lib/IR/ModuleSummaryIndex.cpp
llvm/lib/LTO/LTO.cpp
llvm/lib/LTO/LTOBackend.cpp
llvm/lib/LTO/ThinLTOCodeGenerator.cpp
llvm/lib/Transforms/IPO/FunctionAttrs.cpp
llvm/lib/Transforms/IPO/FunctionImport.cpp
llvm/test/Assembler/thinlto-summary.ll
llvm/test/Bitcode/thinlto-function-summary-refgraph.ll
llvm/test/Bitcode/thinlto-type-vcalls.ll
llvm/test/ThinLTO/X86/deadstrip.ll
llvm/test/ThinLTO/X86/dot-dumper.ll
llvm/test/ThinLTO/X86/dot-dumper2.ll
llvm/test/ThinLTO/X86/funcattrs-prop-exported-internal.ll
llvm/test/ThinLTO/X86/funcattrs-prop-maythrow.ll
llvm/test/ThinLTO/X86/funcattrs-prop-undefined.ll
llvm/test/ThinLTO/X86/funcattrs-prop-unknown.ll
llvm/test/ThinLTO/X86/funcattrs-prop-weak.ll
llvm/test/ThinLTO/X86/funcattrs-prop.ll
llvm/test/ThinLTO/X86/funcimport_alwaysinline.ll
llvm/test/ThinLTO/X86/function_entry_count.ll
llvm/test/ThinLTO/X86/linkonce_resolution_comdat.ll
Index: llvm/test/ThinLTO/X86/linkonce_resolution_comdat.ll
===================================================================
--- llvm/test/ThinLTO/X86/linkonce_resolution_comdat.ll
+++ llvm/test/ThinLTO/X86/linkonce_resolution_comdat.ll
@@ -3,15 +3,17 @@
; verification error.
; RUN: opt -module-summary %s -o %t1.bc
; RUN: opt -module-summary %p/Inputs/linkonce_resolution_comdat.ll -o %t2.bc
-; RUN: llvm-lto -thinlto-action=run %t1.bc %t2.bc -exported-symbol=f -exported-symbol=g -thinlto-save-temps=%t3.
+; RUN: llvm-lto -thinlto-action=run -disable-thinlto-funcattrs=0 %t1.bc %t2.bc -exported-symbol=f -exported-symbol=g -thinlto-save-temps=%t3.
; RUN: llvm-dis %t3.0.3.imported.bc -o - | FileCheck %s --check-prefix=IMPORT1
; RUN: llvm-dis %t3.1.3.imported.bc -o - | FileCheck %s --check-prefix=IMPORT2
; Copy from first module is prevailing and converted to weak_odr, copy
; from second module is preempted and converted to available_externally and
; removed from comdat.
-; IMPORT1: define weak_odr i32 @f(i8* %0) unnamed_addr comdat($c1) {
-; IMPORT2: define available_externally i32 @f(i8* %0) unnamed_addr {
+; IMPORT1: define weak_odr i32 @f(i8* %0) unnamed_addr [[ATTR:#[0-9]+]] comdat($c1) {
+; IMPORT2: define available_externally i32 @f(i8* %0) unnamed_addr [[ATTR:#[0-9]+]] {
+
+; CHECK-DAG: attributes [[ATTR]] = { norecurse nounwind }
; RUN: llvm-nm -o - < %t1.bc.thinlto.o | FileCheck %s --check-prefix=NM1
; NM1: W f
Index: llvm/test/ThinLTO/X86/function_entry_count.ll
===================================================================
--- llvm/test/ThinLTO/X86/function_entry_count.ll
+++ llvm/test/ThinLTO/X86/function_entry_count.ll
@@ -2,7 +2,7 @@
; RUN: opt -thinlto-bc %p/Inputs/function_entry_count.ll -write-relbf-to-summary -thin-link-bitcode-file=%t2.thinlink.bc -o %t2.bc
; First perform the thin link on the normal bitcode file.
-; RUN: llvm-lto2 run %t1.bc %t2.bc -o %t.o -save-temps -thinlto-synthesize-entry-counts \
+; RUN: llvm-lto2 run %t1.bc %t2.bc -o %t.o -save-temps -disable-thinlto-funcattrs=0 -thinlto-synthesize-entry-counts \
; RUN: -r=%t1.bc,g, \
; RUN: -r=%t1.bc,f,px \
; RUN: -r=%t1.bc,h,px \
@@ -10,15 +10,16 @@
; RUN: -r=%t2.bc,g,px
; RUN: llvm-dis -o - %t.o.1.3.import.bc | FileCheck %s
-; RUN: llvm-lto -thinlto-action=run -thinlto-synthesize-entry-counts -exported-symbol=f \
+; RUN: llvm-lto -thinlto-action=run -disable-thinlto-funcattrs=0 -thinlto-synthesize-entry-counts -exported-symbol=f \
; RUN: -exported-symbol=g -exported-symbol=h -thinlto-save-temps=%t3. %t1.bc %t2.bc
; RUN: llvm-dis %t3.0.3.imported.bc -o - | FileCheck %s
-; CHECK: define void @h() !prof ![[PROF2:[0-9]+]]
-; CHECK: define void @f(i32{{.*}}) !prof ![[PROF1:[0-9]+]]
+; CHECK: define void @h() [[ATTR:#[0-9]+]] !prof ![[PROF2:[0-9]+]]
+; CHECK: define void @f(i32{{.*}}) [[ATTR:#[0-9]+]] !prof ![[PROF1:[0-9]+]]
; CHECK: define available_externally void @g() !prof ![[PROF2]]
; CHECK-DAG: ![[PROF1]] = !{!"synthetic_function_entry_count", i64 10}
; CHECK-DAG: ![[PROF2]] = !{!"synthetic_function_entry_count", i64 198}
+; CHECK-DAG: attributes [[ATTR]] = { norecurse nounwind }
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
Index: llvm/test/ThinLTO/X86/funcimport_alwaysinline.ll
===================================================================
--- llvm/test/ThinLTO/X86/funcimport_alwaysinline.ll
+++ llvm/test/ThinLTO/X86/funcimport_alwaysinline.ll
@@ -1,7 +1,7 @@
; RUN: opt -module-summary %s -o %t1.bc
; RUN: opt -module-summary %p/Inputs/funcimport_alwaysinline.ll -o %t2.bc
-; RUN: llvm-lto2 run %t1.bc %t2.bc -o %t.o -save-temps \
+; RUN: llvm-lto2 run -disable-thinlto-funcattrs=0 %t1.bc %t2.bc -o %t.o -save-temps \
; RUN: -r=%t1.bc,foo,plx \
; RUN: -r=%t2.bc,main,plx \
; RUN: -r=%t2.bc,foo,l \
@@ -23,4 +23,4 @@
}
attributes #0 = { alwaysinline nounwind uwtable }
-; CHECK2: ^2 = gv: (guid: {{.*}}, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 1))))
+; CHECK2: ^2 = gv: (guid: {{.*}}, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 1, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0))))
Index: llvm/test/ThinLTO/X86/funcattrs-prop.ll
===================================================================
--- /dev/null
+++ llvm/test/ThinLTO/X86/funcattrs-prop.ll
@@ -0,0 +1,151 @@
+; RUN: split-file %s %t
+; RUN: opt -module-summary %t/a.ll -o %t/a.bc
+; RUN: opt -module-summary %t/b.ll -o %t/b.bc
+; RUN: opt -module-summary %t/c.ll -o %t/c.bc
+
+;; ThinLTO Function attribute propagation uses the prevailing symbol to propagate attributes to its callers.
+;; Interposable (linkonce and weak) linkages are fair game given we know the prevailing copy will be used in the final binary.
+; RUN: llvm-lto2 run -disable-thinlto-funcattrs=0 %t/a.bc %t/b.bc %t/c.bc -o %t1 -save-temps \
+; RUN: -r=%t/a.bc,call_extern,plx -r=%t/a.bc,call_linkonceodr,plx -r=%t/a.bc,call_weakodr,plx -r=%t/a.bc,call_linkonce,plx -r=%t/a.bc,call_weak,plx -r=%t/a.bc,call_linkonce_may_unwind,plx -r=%t/a.bc,call_weak_may_unwind,plx \
+; RUN: -r=%t/a.bc,extern, -r=%t/a.bc,linkonceodr, -r=%t/a.bc,weakodr, -r=%t/a.bc,linkonce, -r=%t/a.bc,weak, -r=%t/a.bc,linkonce_may_unwind, -r=%t/a.bc,weak_may_unwind, \
+; RUN: -r=%t/b.bc,extern,p -r=%t/b.bc,linkonceodr,p -r=%t/b.bc,weakodr,p -r=%t/b.bc,linkonce,p -r=%t/b.bc,weak,p -r=%t/b.bc,linkonce_may_unwind,p -r=%t/b.bc,weak_may_unwind, \
+; RUN: -r=%t/c.bc,extern, -r=%t/c.bc,linkonceodr, -r=%t/c.bc,weakodr, -r=%t/c.bc,linkonce, -r=%t/c.bc,weak, -r=%t/c.bc,linkonce_may_unwind, -r=%t/c.bc,weak_may_unwind,p -r=%t/c.bc,may_throw,
+
+; RUN: llvm-dis %t1.1.3.import.bc -o - | FileCheck %s
+
+;--- a.ll
+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-unknown-linux-gnu"
+
+;; These functions are identical between b.ll and c.ll
+declare void @extern()
+declare void @linkonceodr()
+declare void @weakodr()
+
+declare void @linkonce()
+declare void @weak()
+
+;; b.ll contains non-throwing copies of these functions
+;; c.ll contains throwing copies of these functions
+declare void @linkonce_may_unwind()
+declare void @weak_may_unwind()
+
+; CHECK: define dso_local void @call_extern() [[ATTR_NOUNWIND:#[0-9]+]]
+define void @call_extern() {
+ call void @extern()
+ ret void
+}
+
+; CHECK: define dso_local void @call_linkonceodr() [[ATTR_NOUNWIND:#[0-9]+]]
+define void @call_linkonceodr() {
+ call void @linkonceodr()
+ ret void
+}
+
+; CHECK: define dso_local void @call_weakodr() [[ATTR_NOUNWIND:#[0-9]+]]
+define void @call_weakodr() {
+ call void @weakodr()
+ ret void
+}
+
+; CHECK: define dso_local void @call_linkonce() [[ATTR_NOUNWIND:#[0-9]+]]
+define void @call_linkonce() {
+ call void @linkonce()
+ ret void
+}
+
+; CHECK: define dso_local void @call_weak() [[ATTR_NOUNWIND:#[0-9]+]]
+define void @call_weak() {
+ call void @weak()
+ ret void
+}
+
+; CHECK: define dso_local void @call_linkonce_may_unwind() [[ATTR_NOUNWIND:#[0-9]+]]
+;; The prevailing copy here comes from b.ll, which contains nounwind and norecurse
+define void @call_linkonce_may_unwind() {
+ call void @linkonce_may_unwind()
+ ret void
+}
+
+; CHECK: define dso_local void @call_weak_may_unwind() [[ATTR_MAYTHROW:#[0-9]+]]
+;; The prevailing copy hree comes from c.ll, which only contains norecurse
+define void @call_weak_may_unwind() {
+ call void @weak_may_unwind()
+ ret void
+}
+
+; CHECK-DAG: attributes [[ATTR_NOUNWIND]] = { norecurse nounwind }
+; CHECK-DAG: attributes [[ATTR_MAYTHROW]] = { norecurse }
+
+;--- b.ll
+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-unknown-linux-gnu"
+
+attributes #0 = { nounwind norecurse }
+
+define void @extern() #0 {
+ ret void
+}
+
+define linkonce_odr void @linkonceodr() #0 {
+ ret void
+}
+
+define weak_odr void @weakodr() #0 {
+ ret void
+}
+
+define linkonce void @linkonce() #0 {
+ ret void
+}
+
+define weak void @weak() #0 {
+ ret void
+}
+
+define linkonce void @linkonce_may_unwind() #0 {
+ ret void
+}
+
+define weak void @weak_may_unwind() #0 {
+ ret void
+}
+
+;--- c.ll
+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-unknown-linux-gnu"
+
+attributes #0 = { nounwind norecurse }
+attributes #1 = { norecurse }
+
+define void @extern() #0 {
+ ret void
+}
+
+define linkonce_odr void @linkonceodr() #0 {
+ ret void
+}
+
+define weak_odr void @weakodr() #0 {
+ ret void
+}
+
+define linkonce void @linkonce() #0 {
+ ret void
+}
+
+define weak void @weak() #0 {
+ ret void
+}
+
+declare void @may_throw()
+
+define linkonce void @linkonce_may_unwind() #1 {
+ call void @may_throw()
+ ret void
+}
+
+define weak void @weak_may_unwind() #1 {
+ call void @may_throw()
+ ret void
+}
Index: llvm/test/ThinLTO/X86/funcattrs-prop-weak.ll
===================================================================
--- /dev/null
+++ llvm/test/ThinLTO/X86/funcattrs-prop-weak.ll
@@ -0,0 +1,60 @@
+; RUN: split-file %s %t
+; RUN: opt -thinlto-bc %t/a.ll -thin-link-bitcode-file=%t1.thinlink.bc -o %t1.bc
+; RUN: opt -thinlto-bc %t/b.ll -thin-link-bitcode-file=%t1.thinlink.bc -o %t2.bc
+; RUN: opt -thinlto-bc %t/c.ll -thin-link-bitcode-file=%t1.thinlink.bc -o %t3.bc
+
+; If the prevailing weak symbol is defined in a native file, the IR copies should be dead and propagation should not occur
+; RUN: llvm-lto2 run -disable-thinlto-funcattrs=0 %t1.bc %t2.bc %t3.bc -o %t.o \
+; RUN: -r %t1.bc,caller,px -r %t1.bc,callee,lx \
+; RUN: -r %t2.bc,callee,x \
+; RUN: -r %t3.bc,callee,x \
+; RUN: -save-temps
+
+; RUN: llvm-dis -o - %t.o.1.3.import.bc | FileCheck %s
+
+; If the prevailing weak symbol is in an IR file, it should be the one used in the final binary and thus propagation
+; should be based off of that copy
+; RUN: llvm-lto2 run -O3 -disable-thinlto-funcattrs=0 %t1.bc %t2.bc %t3.bc -o %t.2.o \
+; RUN: -r %t1.bc,caller,px -r %t1.bc,callee,lx \
+; RUN: -r %t2.bc,callee,px \
+; RUN: -r %t3.bc,callee,x \
+; RUN: -save-temps
+
+; RUN: llvm-dis -o - %t.2.o.1.3.import.bc | FileCheck %s --check-prefix=PREVAILING
+; RUN: llvm-dis -o - %t.2.o.2.3.import.bc | FileCheck %s --check-prefix=PREVAILING-B
+; RUN: llvm-dis -o - %t.2.o.3.3.import.bc | FileCheck %s --check-prefix=PREVAILING-C
+
+;--- a.ll
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare i32 @callee()
+
+; CHECK-NOT: Function Attrs:
+; CHECK: define i32 @caller()
+
+; PREVAILING: Function Attrs: norecurse nounwind
+; PREVAILING-NEXT: define i32 @caller()
+define i32 @caller() {
+ %res = call i32 @callee()
+ ret i32 %res
+}
+
+;--- b.ll
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; PREVAILING-B: define weak i32 @callee()
+define weak i32 @callee() {
+ ret i32 5
+}
+
+;--- c.ll
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; PREVAILING-C: declare i32 @callee()
+define weak i32 @callee() {
+ ret i32 6
+}
+
Index: llvm/test/ThinLTO/X86/funcattrs-prop-unknown.ll
===================================================================
--- /dev/null
+++ llvm/test/ThinLTO/X86/funcattrs-prop-unknown.ll
@@ -0,0 +1,35 @@
+; Unknown (e.g. indirect) calls returns conservative results from function propagation
+; RUN: opt -thinlto-bc %s -thin-link-bitcode-file=%t1.thinlink.bc -o %t1.bc
+; RUN: llvm-lto2 run -disable-thinlto-funcattrs=0 %t1.bc -o %t.o -save-temps \
+; RUN: -r %t1.bc,indirect,px -r %t1.bc,inlineasm,px -r %t1.bc,selectcallee,px -r %t1.bc,f, -r %t1.bc,g, -r %t1.bc,global,
+; RUN: llvm-dis -o - %t.o.1.3.import.bc | FileCheck %s
+
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; CHECK-NOT: ; Function Attrs:
+; CHECK: define i32 @indirect(i32 ()* nocapture %0) {
+define i32 @indirect(i32 ()* nocapture) {
+ %2 = tail call i32 %0()
+ ret i32 %2
+}
+
+; CHECK-NOT: ; Function Attrs:
+; CHECK: define i8* @inlineasm() {
+define i8* @inlineasm() {
+entry:
+ %0 = tail call i8* asm sideeffect "lea ff_h264_cabac_tables(%rip), $0", "=&r,~{dirflag},~{fpsr},~{flags}"()
+ ret i8* %0
+}
+
+; CHECK-NOT: ; Function Attrs:
+; CHECK: define void @selectcallee() {
+define void @selectcallee() {
+ ; Test calls that aren't handled either as direct or indirect.
+ call void select (i1 icmp eq (i32* @global, i32* null), void ()* @f, void ()* @g)()
+ ret void
+}
+
+declare void @f()
+declare void @g()
+@global = extern_weak global i32
\ No newline at end of file
Index: llvm/test/ThinLTO/X86/funcattrs-prop-undefined.ll
===================================================================
--- /dev/null
+++ llvm/test/ThinLTO/X86/funcattrs-prop-undefined.ll
@@ -0,0 +1,31 @@
+; Callee1 isn't defined, propagation goes conservative
+; RUN: split-file %s %t
+; RUN: opt -thinlto-bc %t/main.ll -thin-link-bitcode-file=%t1.thinlink.bc -o %t1.bc
+; RUN: opt -thinlto-bc %t/callees.ll -thin-link-bitcode-file=%t2.thinlink.bc -o %t2.bc
+; RUN: llvm-lto2 run -disable-thinlto-funcattrs=0 %t1.bc %t2.bc -o %t.o -r %t1.bc,caller,px -r %t1.bc,callee,l -r %t1.bc,callee1,l -r %t2.bc,callee,px -save-temps
+; RUN: llvm-dis -o - %t.o.1.3.import.bc | FileCheck %s
+
+;--- main.ll
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare void @callee()
+declare void @callee1()
+
+; CHECK-NOT: Function Attrs:
+; CHECK: define void @caller()
+define void @caller() {
+ call void @callee()
+ call void @callee1()
+ ret void
+}
+
+;--- callees.ll
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+attributes #0 = { nounwind norecurse }
+
+define void @callee() #0 {
+ ret void
+}
Index: llvm/test/ThinLTO/X86/funcattrs-prop-maythrow.ll
===================================================================
--- /dev/null
+++ llvm/test/ThinLTO/X86/funcattrs-prop-maythrow.ll
@@ -0,0 +1,115 @@
+; For instructions explicitly defined as mayThrow, make sure they prevent nounwind propagation
+; RUN: split-file %s %t
+; RUN: opt -thinlto-bc %t/main.ll -thin-link-bitcode-file=%t1.thinlink.bc -o %t1.bc
+; RUN: opt -thinlto-bc %t/callees.ll -thin-link-bitcode-file=%t2.thinlink.bc -o %t2.bc
+; RUN: llvm-lto2 run -disable-thinlto-funcattrs=0 %t1.bc %t2.bc -o %t.o -r %t1.bc,caller,px -r %t1.bc,caller1,px -r %t1.bc,caller2,px -r %t1.bc,caller_nounwind,px \
+; RUN: -r %t1.bc,cleanupret,l -r %t1.bc,catchret,l -r %t1.bc,resume,l -r %t1.bc,cleanupret_nounwind,l \
+; RUN: -r %t2.bc,cleanupret,px -r %t2.bc,catchret,px -r %t2.bc,resume,px -r %t2.bc,cleanupret_nounwind,px -r %t2.bc,nonThrowing,px -r %t2.bc,__gxx_personality_v0,px -save-temps
+; RUN: llvm-dis -o - %t2.bc | FileCheck %s --check-prefix=SUMMARY
+; RUN: llvm-dis -o - %t.o.1.3.import.bc | FileCheck %s
+
+;--- main.ll
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare void @cleanupret()
+declare void @catchret()
+declare void @resume()
+
+; Functions can have mayThrow instructions but also be marked noUnwind
+; if they have terminate semantics (e.g. noexcept). In such cases
+; propagation trusts the original noUnwind value in the function summary
+declare void @cleanupret_nounwind()
+
+; CHECK: define void @caller() [[ATTR_MAYTHROW:#[0-9]+]]
+define void @caller() {
+ call void @cleanupret()
+ ret void
+}
+
+; CHECK: define void @caller1() [[ATTR_MAYTHROW:#[0-9]+]]
+define void @caller1() {
+ call void @catchret()
+ ret void
+}
+
+; CHECK: define void @caller2() [[ATTR_MAYTHROW:#[0-9]+]]
+define void @caller2() {
+ call void @resume()
+ ret void
+}
+
+; CHECK: define void @caller_nounwind() [[ATTR_NOUNWIND:#[0-9]+]]
+define void @caller_nounwind() {
+ call void @cleanupret_nounwind()
+ ret void
+}
+
+; CHECK-DAG: attributes [[ATTR_NOUNWIND]] = { norecurse nounwind }
+; CHECK-DAG: attributes [[ATTR_MAYTHROW]] = { norecurse }
+
+; SUMMARY-DAG: = gv: (name: "cleanupret", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 4, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 1, hasUnknownCall: 0), calls: ((callee: ^{{.*}})), refs: (^{{.*}}))))
+; SUMMARY-DAG: = gv: (name: "resume", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 4, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 1, hasUnknownCall: 0), calls: ((callee: ^{{.*}})), refs: (^{{.*}}))))
+; SUMMARY-DAG: = gv: (name: "catchret", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 1, hasUnknownCall: 0), calls: ((callee: ^{{.*}})), refs: (^{{.*}}))))
+
+;--- callees.ll
+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-unknown-linux-gnu"
+attributes #0 = { nounwind }
+
+define void @nonThrowing() #0 {
+ ret void
+}
+
+declare i32 @__gxx_personality_v0(...)
+
+define void @cleanupret() personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ invoke void @nonThrowing()
+ to label %exit unwind label %pad
+pad:
+ %cp = cleanuppad within none [i7 4]
+ cleanupret from %cp unwind to caller
+exit:
+ ret void
+}
+
+define void @catchret() personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ invoke void @nonThrowing()
+ to label %exit unwind label %pad
+pad:
+ %cs1 = catchswitch within none [label %catch] unwind to caller
+catch:
+ %cp = catchpad within %cs1 [i7 4]
+ catchret from %cp to label %exit
+exit:
+ ret void
+}
+
+define void @resume() uwtable optsize ssp personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ invoke void @nonThrowing()
+ to label %try.cont unwind label %lpad
+
+try.cont: ; preds = %entry, %invoke.cont4
+ ret void
+
+lpad: ; preds = %entry
+ %exn = landingpad {i8*, i32}
+ cleanup
+ resume { i8*, i32 } %exn
+}
+
+define void @cleanupret_nounwind() #0 personality i32 (...)* @__gxx_personality_v0 {
+entry:
+ invoke void @nonThrowing()
+ to label %exit unwind label %pad
+pad:
+ %cp = cleanuppad within none [i7 4]
+ cleanupret from %cp unwind to caller
+exit:
+ ret void
+}
+
+attributes #0 = { nounwind }
\ No newline at end of file
Index: llvm/test/ThinLTO/X86/funcattrs-prop-exported-internal.ll
===================================================================
--- /dev/null
+++ llvm/test/ThinLTO/X86/funcattrs-prop-exported-internal.ll
@@ -0,0 +1,58 @@
+; Function import can promote an internal function to external but not mark it as prevailing.
+; Given that the internal function's attributes would have already propagated to its callers
+; that are part of the import chain there's no need to actually propagate off this copy as
+; propagating the caller performs the same thing.
+; RUN: split-file %s %t
+; RUN: opt -thinlto-bc %t/main.ll -thin-link-bitcode-file=%t1.thinlink.bc -o %t1.bc
+; RUN: opt -thinlto-bc %t/callees.ll -thin-link-bitcode-file=%t2.thinlink.bc -o %t2.bc
+; RUN: llvm-lto2 run -disable-thinlto-funcattrs=0 \
+; RUN: %t1.bc %t2.bc -o %t.o \
+; RUN: -r %t1.bc,caller,l -r %t1.bc,caller_noattr,l -r %t1.bc,importer,px -r %t1.bc,importer_noattr,px \
+; RUN: -r %t2.bc,caller,px -r %t2.bc,caller_noattr,px \
+; RUN: -save-temps
+; RUN: llvm-dis -o - %t.o.1.3.import.bc | FileCheck %s --match-full-lines
+
+;--- main.ll
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare void @caller()
+declare void @caller_noattr()
+
+; CHECK: define void @importer() [[ATTR_PROP:#[0-9]+]] {
+define void @importer() {
+ call void @caller()
+ ret void
+}
+
+; If somehow the caller doesn't get the attributes, we
+; shouldn't propagate from the internal callee.
+; CHECK: define void @importer_noattr() {
+define void @importer_noattr() {
+ call void @caller_noattr()
+ ret void
+}
+
+; CHECK: define available_externally hidden void @callee{{.*}}
+
+; CHECK-DAG: attributes [[ATTR_PROP]] = { norecurse nounwind }
+
+;--- callees.ll
+target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+attributes #0 = { nounwind norecurse }
+
+define void @caller() #0 {
+ call void @callee()
+ ret void
+}
+
+define void @caller_noattr() {
+ call void @callee()
+ ret void
+}
+
+define internal void @callee() #0 {
+ ret void
+}
Index: llvm/test/ThinLTO/X86/dot-dumper2.ll
===================================================================
--- llvm/test/ThinLTO/X86/dot-dumper2.ll
+++ llvm/test/ThinLTO/X86/dot-dumper2.ll
@@ -15,7 +15,7 @@
; COMBINED-NEXT: color = lightgrey;
; COMBINED-NEXT: label =
; COMBINED-NEXT: node [style=filled,fillcolor=lightblue];
-; COMBINED-NEXT: M0_[[MAIN:[0-9]+]] [shape="record",label="main|extern (inst: 2, ffl: 000000)}"]; // function
+; COMBINED-NEXT: M0_[[MAIN:[0-9]+]] [shape="record",label="main|extern (inst: 2, ffl: 000000000)}"]; // function
; COMBINED-NEXT: // Edges:
; COMBINED-NEXT: }
; COMBINED-NEXT: // Module:
Index: llvm/test/ThinLTO/X86/dot-dumper.ll
===================================================================
--- llvm/test/ThinLTO/X86/dot-dumper.ll
+++ llvm/test/ThinLTO/X86/dot-dumper.ll
@@ -21,7 +21,7 @@
; PERMODULE-NEXT: label = "";
; PERMODULE-NEXT: node [style=filled,fillcolor=lightblue];
; PERMODULE-NEXT: M0_[[MAIN_ALIAS:[0-9]+]] [style="dotted,filled",shape="box",label="main_alias",fillcolor="red"]; // alias, dead
-; PERMODULE-NEXT: M0_[[MAIN:[0-9]+]] [shape="record",label="main|extern (inst: 4, ffl: 000000)}",fillcolor="red"]; // function, dead
+; PERMODULE-NEXT: M0_[[MAIN:[0-9]+]] [shape="record",label="main|extern (inst: 4, ffl: 000000000)}",fillcolor="red"]; // function, dead
; PERMODULE-NEXT: // Edges:
; PERMODULE-NEXT: M0_[[MAIN_ALIAS]] -> M0_[[MAIN]] [style=dotted]; // alias
; PERMODULE-NEXT: }
@@ -40,7 +40,7 @@
; COMBINED-NEXT: label = "dot-dumper{{.*}}1.bc";
; COMBINED-NEXT: node [style=filled,fillcolor=lightblue];
; COMBINED-NEXT: M0_[[MAIN_ALIAS:[0-9]+]] [style="dotted,filled",shape="box",label="main_alias",fillcolor="red"]; // alias, dead
-; COMBINED-NEXT: M0_[[MAIN:[0-9]+]] [shape="record",label="main|extern (inst: 4, ffl: 000000)}"]; // function, preserved
+; COMBINED-NEXT: M0_[[MAIN:[0-9]+]] [shape="record",label="main|extern (inst: 4, ffl: 000000000)}"]; // function, preserved
; COMBINED-NEXT: // Edges:
; COMBINED-NEXT: M0_[[MAIN_ALIAS]] -> M0_[[MAIN]] [style=dotted]; // alias
; COMBINED-NEXT: }
@@ -50,10 +50,10 @@
; COMBINED-NEXT: color = lightgrey;
; COMBINED-NEXT: label = "dot-dumper{{.*}}2.bc";
; COMBINED-NEXT: node [style=filled,fillcolor=lightblue];
-; COMBINED-NEXT: M1_[[FOO:[0-9]+]] [shape="record",label="foo|extern (inst: 4, ffl: 000010)}"]; // function
+; COMBINED-NEXT: M1_[[FOO:[0-9]+]] [shape="record",label="foo|extern (inst: 4, ffl: 000010000)}"]; // function
; COMBINED-NEXT: M1_[[A:[0-9]+]] [shape="Mrecord",label="A|extern}"]; // variable, immutable
; COMBINED-NEXT: M1_[[B:[0-9]+]] [shape="Mrecord",label="B|extern}"]; // variable, immutable, constant
-; COMBINED-NEXT: M1_{{[0-9]+}} [shape="record",label="bar|extern (inst: 1, ffl: 000000)}",fillcolor="red"]; // function, dead
+; COMBINED-NEXT: M1_{{[0-9]+}} [shape="record",label="bar|extern (inst: 1, ffl: 000000000)}",fillcolor="red"]; // function, dead
; COMBINED-NEXT: // Edges:
; COMBINED-NEXT: M1_[[FOO]] -> M1_[[B]] [style=dashed,color=forestgreen]; // const-ref
; COMBINED-NEXT: M1_[[FOO]] -> M1_[[A]] [style=dashed,color=forestgreen]; // const-ref
Index: llvm/test/ThinLTO/X86/deadstrip.ll
===================================================================
--- llvm/test/ThinLTO/X86/deadstrip.ll
+++ llvm/test/ThinLTO/X86/deadstrip.ll
@@ -29,6 +29,7 @@
; RUN: -r %t2.bc,_another_dead_func,pl \
; RUN: -r %t2.bc,_linkonceodrfuncwithalias,pl \
; RUN: -thinlto-threads=1 \
+; RUN: -disable-thinlto-funcattrs=0 \
; RUN: -debug-only=function-import 2>&1 | FileCheck %s --check-prefix=DEBUG --check-prefix=STATS
; RUN: llvm-dis < %t.out.1.3.import.bc | FileCheck %s --check-prefix=LTO2
; RUN: llvm-dis < %t.out.2.3.import.bc | FileCheck %s --check-prefix=LTO2-CHECK2
@@ -66,7 +67,7 @@
; LTO2-NOT: available_externally {{.*}} @baz()
; LTO2: @llvm.global_ctors =
; LTO2: define internal void @_GLOBAL__I_a()
-; LTO2: define internal void @bar() {
+; LTO2: define internal void @bar() [[ATTR:#[0-9]+]] {
; LTO2: define internal void @bar_internal()
; LTO2-NOT: @dead_func()
; LTO2-NOT: available_externally {{.*}} @baz()
@@ -78,7 +79,7 @@
; Make sure we keep @linkonceodrfuncwithalias in Input/deadstrip.ll alive as it
; is reachable from @main.
-; LTO2-CHECK2: define weak_odr dso_local void @linkonceodrfuncwithalias() {
+; LTO2-CHECK2: define weak_odr dso_local void @linkonceodrfuncwithalias() [[ATTR:#[0-9]+]] {
; We should have eventually removed @baz since it was internalized and unused
; CHECK2-NM-NOT: _baz
@@ -98,6 +99,8 @@
; DEBUG-DAG: Initialize import for 15611644523426561710 (boo)
; DEBUG-DAG: Ignores Dead GUID: 2384416018110111308 (another_dead_func)
+; LTO2-DAG: attributes [[ATTR]] = { norecurse nounwind }
+
; STATS: 3 function-import - Number of dead stripped symbols in index
; Next test the case where Inputs/deadstrip.ll does not get a module index,
Index: llvm/test/Bitcode/thinlto-type-vcalls.ll
===================================================================
--- llvm/test/Bitcode/thinlto-type-vcalls.ll
+++ llvm/test/Bitcode/thinlto-type-vcalls.ll
@@ -112,19 +112,19 @@
; DIS: ^0 = module: (path: "{{.*}}", hash: (0, 0, 0, 0, 0))
; DIS: ^1 = gv: (name: "llvm.type.test") ; guid = 608142985856744218
-; DIS: ^2 = gv: (name: "f1", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16)))))) ; guid = 2072045998141807037
-; DIS: ^3 = gv: (name: "f3", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16)))))) ; guid = 4197650231481825559
+; DIS: ^2 = gv: (name: "f1", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 8, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16)))))) ; guid = 2072045998141807037
+; DIS: ^3 = gv: (name: "f3", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16)))))) ; guid = 4197650231481825559
; DIS: ^4 = gv: (name: "llvm.type.checked.load") ; guid = 5568222536364573403
; DIS: ^5 = gv: (name: "llvm.assume") ; guid = 6385187066495850096
-; DIS: ^6 = gv: (name: "f2", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 24), vFuncId: (guid: 16434608426314478903, offset: 32)))))) ; guid = 8471399308421654326
-; DIS: ^7 = gv: (name: "f4", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (guid: 6699318081062747564, offset: 16), args: (42)), (vFuncId: (guid: 6699318081062747564, offset: 24), args: (43))))))) ; guid = 10064745020953272174
-; DIS: ^8 = gv: (name: "f5", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, typeIdInfo: (typeCheckedLoadConstVCalls: ((vFuncId: (guid: 6699318081062747564, offset: 16), args: (42))))))) ; guid = 11686717102184386164
+; DIS: ^6 = gv: (name: "f2", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 24), vFuncId: (guid: 16434608426314478903, offset: 32)))))) ; guid = 8471399308421654326
+; DIS: ^7 = gv: (name: "f4", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (guid: 6699318081062747564, offset: 16), args: (42)), (vFuncId: (guid: 6699318081062747564, offset: 24), args: (43))))))) ; guid = 10064745020953272174
+; DIS: ^8 = gv: (name: "f5", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeCheckedLoadConstVCalls: ((vFuncId: (guid: 6699318081062747564, offset: 16), args: (42))))))) ; guid = 11686717102184386164
; DIS: ^9 = gv: (name: "f6", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 2, typeIdInfo: (typeTests: (7546896869197086323))))) ; guid = 11834966808443348068
; COMBINED-DIS: ^0 = module: (path: "{{.*}}thinlto-type-vcalls.ll.tmp.o", hash: (0, 0, 0, 0, 0))
-; COMBINED-DIS: ^1 = gv: (guid: 2072045998141807037, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16))))))
-; COMBINED-DIS: ^2 = gv: (guid: 4197650231481825559, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16))))))
-; COMBINED-DIS: ^3 = gv: (guid: 8471399308421654326, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 24), vFuncId: (guid: 16434608426314478903, offset: 32))))))
-; COMBINED-DIS: ^4 = gv: (guid: 10064745020953272174, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (guid: 6699318081062747564, offset: 16), args: (42)), (vFuncId: (guid: 6699318081062747564, offset: 24), args: (43)))))))
-; COMBINED-DIS: ^5 = gv: (guid: 11686717102184386164, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, typeIdInfo: (typeCheckedLoadConstVCalls: ((vFuncId: (guid: 6699318081062747564, offset: 16), args: (42)))))))
+; COMBINED-DIS: ^1 = gv: (guid: 2072045998141807037, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 8, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16))))))
+; COMBINED-DIS: ^2 = gv: (guid: 4197650231481825559, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16))))))
+; COMBINED-DIS: ^3 = gv: (guid: 8471399308421654326, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 24), vFuncId: (guid: 16434608426314478903, offset: 32))))))
+; COMBINED-DIS: ^4 = gv: (guid: 10064745020953272174, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 15, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeTestAssumeConstVCalls: ((vFuncId: (guid: 6699318081062747564, offset: 16), args: (42)), (vFuncId: (guid: 6699318081062747564, offset: 24), args: (43)))))))
+; COMBINED-DIS: ^5 = gv: (guid: 11686717102184386164, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 5, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeCheckedLoadConstVCalls: ((vFuncId: (guid: 6699318081062747564, offset: 16), args: (42)))))))
; COMBINED-DIS: ^6 = gv: (guid: 11834966808443348068, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 2, typeIdInfo: (typeTests: (7546896869197086323)))))
Index: llvm/test/Bitcode/thinlto-function-summary-refgraph.ll
===================================================================
--- llvm/test/Bitcode/thinlto-function-summary-refgraph.ll
+++ llvm/test/Bitcode/thinlto-function-summary-refgraph.ll
@@ -158,7 +158,7 @@
; DIS-DAG: = gv: (name: "globalvar", summaries: (variable: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 0, constant: 1)))) ; guid = 12887606300320728018
; DIS-DAG: = gv: (name: "func2") ; guid = 14069196320850861797
; DIS-DAG: = gv: (name: "llvm.ctpop.i8") ; guid = 15254915475081819833
-; DIS-DAG: = gv: (name: "main", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 9, calls: ((callee: ^{{.*}})), refs: (^{{.*}})))) ; guid = 15822663052811949562
+; DIS-DAG: = gv: (name: "main", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 9, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), calls: ((callee: ^{{.*}})), refs: (^{{.*}})))) ; guid = 15822663052811949562
; DIS-DAG: = gv: (name: "bar", summaries: (variable: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1, constant: 0), refs: (^{{.*}})))) ; guid = 16434608426314478903
; Don't try to match the exact GUID. Since it is private, the file path
; will get hashed, and that will be test dependent.
Index: llvm/test/Assembler/thinlto-summary.ll
===================================================================
--- llvm/test/Assembler/thinlto-summary.ll
+++ llvm/test/Assembler/thinlto-summary.ll
@@ -38,7 +38,7 @@
; Functions with various flag combinations (notEligibleToImport, Live,
; combinations of optional function flags).
^15 = gv: (guid: 14, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 1, live: 1, dsoLocal: 0), insts: 1, funcFlags: (noInline: 1))))
-^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readNone: 1, noRecurse: 1, alwaysInline: 1))))
+^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readNone: 1, noRecurse: 1, alwaysInline: 1, noUnwind : 1, mayThrow : 1, hasUnknownCall : 1))))
; This one also tests backwards reference in calls.
^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, funcFlags: (readOnly: 1, returnDoesNotAlias: 1), calls: ((callee: ^15)))))
@@ -82,9 +82,9 @@
; CHECK: ^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 0), refs: (^4))))
; CHECK: ^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 0, constant: 0))))
; CHECK: ^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0, constant: 0))))
-; CHECK: ^15 = gv: (guid: 14, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 1, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0))))
-; CHECK: ^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 1, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 1))))
-; CHECK: ^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 1, noRecurse: 0, returnDoesNotAlias: 1, noInline: 0, alwaysInline: 0), calls: ((callee: ^15)))))
+; CHECK: ^15 = gv: (guid: 14, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 1, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0))))
+; CHECK: ^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 1, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 1, noUnwind: 1, mayThrow: 1, hasUnknownCall: 1))))
+; CHECK: ^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 1, noRecurse: 0, returnDoesNotAlias: 1, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0), calls: ((callee: ^15)))))
; CHECK: ^18 = gv: (guid: 17, summaries: (alias: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), aliasee: ^14)))
; CHECK: ^19 = gv: (guid: 18, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 4, typeIdInfo: (typeTests: (^24, ^26)))))
; CHECK: ^20 = gv: (guid: 19, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (^27, offset: 16))))))
Index: llvm/lib/Transforms/IPO/FunctionImport.cpp
===================================================================
--- llvm/lib/Transforms/IPO/FunctionImport.cpp
+++ llvm/lib/Transforms/IPO/FunctionImport.cpp
@@ -1051,13 +1051,33 @@
return true;
}
-void llvm::thinLTOResolvePrevailingInModule(
- Module &TheModule, const GVSummaryMapTy &DefinedGlobals) {
- auto updateLinkage = [&](GlobalValue &GV) {
+void llvm::thinLTOFinalizeInModule(Module &TheModule,
+ const GVSummaryMapTy &DefinedGlobals,
+ bool PropagateAttrs) {
+ auto FinalizeInModule = [&](GlobalValue &GV, bool Propagate = false) {
// See if the global summary analysis computed a new resolved linkage.
const auto &GS = DefinedGlobals.find(GV.getGUID());
if (GS == DefinedGlobals.end())
return;
+
+ if (Propagate)
+ if (FunctionSummary *FS = dyn_cast<FunctionSummary>(GS->second)) {
+ if (Function *F = dyn_cast<Function>(&GV)) {
+ // TODO: propagate ReadNone and ReadOnly.
+ if (FS->fflags().ReadNone && !F->doesNotAccessMemory())
+ F->setDoesNotAccessMemory();
+
+ if (FS->fflags().ReadOnly && !F->onlyReadsMemory())
+ F->setOnlyReadsMemory();
+
+ if (FS->fflags().NoRecurse && !F->doesNotRecurse())
+ F->setDoesNotRecurse();
+
+ if (FS->fflags().NoUnwind && !F->doesNotThrow())
+ F->setDoesNotThrow();
+ }
+ }
+
auto NewLinkage = GS->second->linkage();
if (GlobalValue::isLocalLinkage(GV.getLinkage()) ||
// Don't internalize anything here, because the code below
@@ -1116,11 +1136,11 @@
// Process functions and global now
for (auto &GV : TheModule)
- updateLinkage(GV);
+ FinalizeInModule(GV, PropagateAttrs);
for (auto &GV : TheModule.globals())
- updateLinkage(GV);
+ FinalizeInModule(GV);
for (auto &GV : TheModule.aliases())
- updateLinkage(GV);
+ FinalizeInModule(GV);
}
/// Run internalization on \p TheModule based on symmary analysis.
Index: llvm/lib/Transforms/IPO/FunctionAttrs.cpp
===================================================================
--- llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -14,6 +14,7 @@
#include "llvm/Transforms/IPO/FunctionAttrs.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
@@ -82,6 +83,11 @@
STATISTIC(NumWillReturn, "Number of functions marked as willreturn");
STATISTIC(NumNoSync, "Number of functions marked as nosync");
+STATISTIC(NumThinLinkNoRecurse,
+ "Number of functions marked as norecurse during thinlink");
+STATISTIC(NumThinLinkNoUnwind,
+ "Number of functions marked as nounwind during thinlink");
+
static cl::opt<bool> EnableNonnullArgPropagation(
"enable-nonnull-arg-prop", cl::init(true), cl::Hidden,
cl::desc("Try to propagate nonnull argument attributes from callsites to "
@@ -95,6 +101,10 @@
"disable-nofree-inference", cl::Hidden,
cl::desc("Stop inferring nofree attribute during function-attrs pass"));
+static cl::opt<bool> DisableThinLTOPropagation(
+ "disable-thinlto-funcattrs", cl::init(true), cl::Hidden,
+ cl::desc("Don't propagate function-attrs in thinLTO"));
+
namespace {
using SCCNodeSet = SmallSetVector<Function *, 8>;
@@ -321,6 +331,195 @@
return MadeChange;
}
+// Compute definitive function attributes for a function taking into account
+// prevailing definitions and linkage types
+static FunctionSummary *calculatePrevailingSummary(
+ ValueInfo VI,
+ DenseMap<ValueInfo, FunctionSummary *> &CachedPrevailingSummary,
+ function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ IsPrevailing) {
+
+ if (CachedPrevailingSummary.count(VI))
+ return CachedPrevailingSummary[VI];
+
+ /// At this point, prevailing symbols have been resolved. The following leads
+ /// to returning a conservative result:
+ /// - Multiple instances with local linkage. Normally local linkage would be
+ /// unique per module
+ /// as the GUID includes the module path. We could have a guid alias if
+ /// there wasn't any distinguishing path when each file was compiled, but
+ /// that should be rare so we'll punt on those.
+
+ /// These next 2 cases should not happen and will assert:
+ /// - Multiple instances with external linkage. This should be caught in
+ /// symbol resolution
+ /// - Non-existent FunctionSummary for Aliasee. This presents a hole in our
+ /// knowledge meaning we have to go conservative.
+
+ /// Otherwise, we calculate attributes for a function as:
+ /// 1. If we have a local linkage, take its attributes. If there's somehow
+ /// multiple, bail and go conservative.
+ /// 2. If we have an external/WeakODR/LinkOnceODR linkage check that it is
+ /// prevailing, take its attributes.
+ /// 3. If we have a Weak/LinkOnce linkage the copies can have semantic
+ /// differences. However, if the prevailing copy is known it will be used
+ /// so take its attributes. If the prevailing copy is in a native file
+ /// all IR copies will be dead and propagation will go conservative.
+ /// 4. AvailableExternally summaries without a prevailing copy are known to
+ /// occur in a couple of circumstances:
+ /// a. An internal function gets imported due to its caller getting
+ /// imported, it becomes AvailableExternally but no prevailing
+ /// definition exists. Because it has to get imported along with its
+ /// caller the attributes will be captured by propagating on its
+ /// caller.
+ /// b. C++11 [temp.explicit]p10 can generate AvailableExternally
+ /// definitions of explicitly instanced template declarations
+ /// for inlining which are ultimately dropped from the TU. Since this
+ /// is localized to the TU the attributes will have already made it to
+ /// the callers.
+ /// These are edge cases and already captured by their callers so we
+ /// ignore these for now. If they become relevant to optimize in the
+ /// future this can be revisited.
+ /// 5. Otherwise, go conservative.
+
+ CachedPrevailingSummary[VI] = nullptr;
+ FunctionSummary *Local = nullptr;
+ FunctionSummary *Prevailing = nullptr;
+
+ for (const auto &GVS : VI.getSummaryList()) {
+ if (!GVS->isLive())
+ continue;
+
+ FunctionSummary *FS = dyn_cast<FunctionSummary>(GVS->getBaseObject());
+ // Virtual and Unknown (e.g. indirect) calls require going conservative
+ if (!FS || FS->fflags().HasUnknownCall)
+ return nullptr;
+
+ const auto &Linkage = GVS->linkage();
+ if (GlobalValue::isLocalLinkage(Linkage)) {
+ if (Local) {
+ LLVM_DEBUG(
+ dbgs()
+ << "ThinLTO FunctionAttrs: Multiple Local Linkage, bailing on "
+ "function "
+ << VI.name() << " from " << FS->modulePath() << ". Previous module "
+ << Local->modulePath() << "\n");
+ return nullptr;
+ }
+ Local = FS;
+ } else if (GlobalValue::isExternalLinkage(Linkage)) {
+ assert(IsPrevailing(VI.getGUID(), GVS.get()));
+ Prevailing = FS;
+ break;
+ } else if (GlobalValue::isWeakODRLinkage(Linkage) ||
+ GlobalValue::isLinkOnceODRLinkage(Linkage) ||
+ GlobalValue::isWeakAnyLinkage(Linkage) ||
+ GlobalValue::isLinkOnceAnyLinkage(Linkage)) {
+ if (IsPrevailing(VI.getGUID(), GVS.get())) {
+ Prevailing = FS;
+ break;
+ }
+ } else if (GlobalValue::isAvailableExternallyLinkage(Linkage)) {
+ // TODO: Handle these cases if they become meaningful
+ continue;
+ }
+ }
+
+ if (Local) {
+ assert(!Prevailing);
+ CachedPrevailingSummary[VI] = Local;
+ } else if (Prevailing) {
+ assert(!Local);
+ CachedPrevailingSummary[VI] = Prevailing;
+ }
+
+ return CachedPrevailingSummary[VI];
+}
+
+bool llvm::thinLTOPropagateFunctionAttrs(
+ ModuleSummaryIndex &Index,
+ function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ IsPrevailing) {
+ // TODO: implement addNoAliasAttrs once
+ // there's more information about the return type in the summary
+ if (DisableThinLTOPropagation)
+ return false;
+
+ DenseMap<ValueInfo, FunctionSummary *> CachedPrevailingSummary;
+ bool Changed = false;
+
+ auto PropagateAttributes = [&](std::vector<ValueInfo> &SCCNodes) {
+ // Assume we can propagate unless we discover otherwise
+ FunctionSummary::FFlags InferredFlags;
+ InferredFlags.NoRecurse = (SCCNodes.size() == 1);
+ InferredFlags.NoUnwind = true;
+
+ for (auto &V : SCCNodes) {
+ FunctionSummary *CallerSummary =
+ calculatePrevailingSummary(V, CachedPrevailingSummary, IsPrevailing);
+
+ // Function summaries can fail to contain information such as declarations
+ if (!CallerSummary)
+ return;
+
+ if (CallerSummary->fflags().MayThrow)
+ InferredFlags.NoUnwind = false;
+
+ for (const auto &Callee : CallerSummary->calls()) {
+ FunctionSummary *CalleeSummary = calculatePrevailingSummary(
+ Callee.first, CachedPrevailingSummary, IsPrevailing);
+
+ if (!CalleeSummary)
+ return;
+
+ if (!CalleeSummary->fflags().NoRecurse)
+ InferredFlags.NoRecurse = false;
+
+ if (!CalleeSummary->fflags().NoUnwind)
+ InferredFlags.NoUnwind = false;
+
+ if (!InferredFlags.NoUnwind && !InferredFlags.NoRecurse)
+ break;
+ }
+ }
+
+ if (InferredFlags.NoUnwind || InferredFlags.NoRecurse) {
+ Changed = true;
+ for (auto &V : SCCNodes) {
+ if (InferredFlags.NoRecurse) {
+ LLVM_DEBUG(dbgs() << "ThinLTO FunctionAttrs: Propagated NoRecurse to "
+ << V.name() << "\n");
+ ++NumThinLinkNoRecurse;
+ }
+
+ if (InferredFlags.NoUnwind) {
+ LLVM_DEBUG(dbgs() << "ThinLTO FunctionAttrs: Propagated NoUnwind to "
+ << V.name() << "\n");
+ ++NumThinLinkNoUnwind;
+ }
+
+ for (auto &S : V.getSummaryList()) {
+ if (auto *FS = dyn_cast<FunctionSummary>(S.get())) {
+ if (InferredFlags.NoRecurse)
+ FS->setNoRecurse();
+
+ if (InferredFlags.NoUnwind)
+ FS->setNoUnwind();
+ }
+ }
+ }
+ }
+ };
+
+ // Call propagation functions on each SCC in the Index
+ for (scc_iterator<ModuleSummaryIndex *> I = scc_begin(&Index); !I.isAtEnd();
+ ++I) {
+ std::vector<ValueInfo> Nodes(*I);
+ PropagateAttributes(Nodes);
+ }
+ return Changed;
+}
+
namespace {
/// For a given pointer Argument, this retains a list of Arguments of functions
Index: llvm/lib/LTO/ThinLTOCodeGenerator.cpp
===================================================================
--- llvm/lib/LTO/ThinLTOCodeGenerator.cpp
+++ llvm/lib/LTO/ThinLTOCodeGenerator.cpp
@@ -55,6 +55,7 @@
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
+#include "llvm/Transforms/IPO/FunctionAttrs.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
@@ -504,7 +505,7 @@
promoteModule(TheModule, Index, ClearDSOLocalOnDeclarations);
// Apply summary-based prevailing-symbol resolution decisions.
- thinLTOResolvePrevailingInModule(TheModule, DefinedGlobals);
+ thinLTOFinalizeInModule(TheModule, DefinedGlobals, /*PropagateAttrs=*/true);
// Save temps: after promotion.
saveTempBitcode(TheModule, SaveTempsDir, count, ".1.promoted.bc");
@@ -763,8 +764,9 @@
resolvePrevailingInIndex(Index, ResolvedODR, GUIDPreservedSymbols,
PrevailingCopy);
- thinLTOResolvePrevailingInModule(
- TheModule, ModuleToDefinedGVSummaries[ModuleIdentifier]);
+ thinLTOFinalizeInModule(TheModule,
+ ModuleToDefinedGVSummaries[ModuleIdentifier],
+ /*PropagateAttrs=*/false);
// Promote the exported values in the index, so that they are promoted
// in the module.
@@ -938,8 +940,9 @@
promoteModule(TheModule, Index, /*ClearDSOLocalOnDeclarations=*/false);
// Internalization
- thinLTOResolvePrevailingInModule(
- TheModule, ModuleToDefinedGVSummaries[ModuleIdentifier]);
+ thinLTOFinalizeInModule(TheModule,
+ ModuleToDefinedGVSummaries[ModuleIdentifier],
+ /*PropagateAttrs=*/false);
thinLTOInternalizeModule(TheModule,
ModuleToDefinedGVSummaries[ModuleIdentifier]);
@@ -1130,6 +1133,8 @@
*Index, IsExported(ExportLists, GUIDPreservedSymbols),
IsPrevailing(PrevailingCopy));
+ thinLTOPropagateFunctionAttrs(*Index, IsPrevailing(PrevailingCopy));
+
// Make sure that every module has an entry in the ExportLists, ImportList,
// GVSummary and ResolvedODR maps to enable threaded access to these maps
// below.
Index: llvm/lib/LTO/LTOBackend.cpp
===================================================================
--- llvm/lib/LTO/LTOBackend.cpp
+++ llvm/lib/LTO/LTOBackend.cpp
@@ -606,7 +606,7 @@
dropDeadSymbols(Mod, DefinedGlobals, CombinedIndex);
- thinLTOResolvePrevailingInModule(Mod, DefinedGlobals);
+ thinLTOFinalizeInModule(Mod, DefinedGlobals, /*PropagateAttrs=*/true);
if (Conf.PostPromoteModuleHook && !Conf.PostPromoteModuleHook(Task, Mod))
return finalizeOptimizationRemarks(std::move(DiagnosticOutputFile));
Index: llvm/lib/LTO/LTO.cpp
===================================================================
--- llvm/lib/LTO/LTO.cpp
+++ llvm/lib/LTO/LTO.cpp
@@ -1521,6 +1521,8 @@
thinLTOResolvePrevailingInIndex(Conf, ThinLTO.CombinedIndex, isPrevailing,
recordNewLinkage, GUIDPreservedSymbols);
+ thinLTOPropagateFunctionAttrs(ThinLTO.CombinedIndex, isPrevailing);
+
generateParamAccessSummary(ThinLTO.CombinedIndex);
if (llvm::timeTraceProfilerEnabled())
Index: llvm/lib/IR/ModuleSummaryIndex.cpp
===================================================================
--- llvm/lib/IR/ModuleSummaryIndex.cpp
+++ llvm/lib/IR/ModuleSummaryIndex.cpp
@@ -447,9 +447,11 @@
static std::string fflagsToString(FunctionSummary::FFlags F) {
auto FlagValue = [](unsigned V) { return V ? '1' : '0'; };
- char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly),
- FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias),
- FlagValue(F.NoInline), FlagValue(F.AlwaysInline), 0};
+ char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly),
+ FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias),
+ FlagValue(F.NoInline), FlagValue(F.AlwaysInline),
+ FlagValue(F.NoUnwind), FlagValue(F.MayThrow),
+ FlagValue(F.HasUnknownCall), 0};
return FlagRep;
}
Index: llvm/lib/IR/AsmWriter.cpp
===================================================================
--- llvm/lib/IR/AsmWriter.cpp
+++ llvm/lib/IR/AsmWriter.cpp
@@ -3203,19 +3203,9 @@
void AssemblyWriter::printFunctionSummary(const FunctionSummary *FS) {
Out << ", insts: " << FS->instCount();
+ if (FS->fflags().anyFlagSet())
+ Out << ", " << FS->fflags();
- FunctionSummary::FFlags FFlags = FS->fflags();
- if (FFlags.ReadNone | FFlags.ReadOnly | FFlags.NoRecurse |
- FFlags.ReturnDoesNotAlias | FFlags.NoInline | FFlags.AlwaysInline) {
- Out << ", funcFlags: (";
- Out << "readNone: " << FFlags.ReadNone;
- Out << ", readOnly: " << FFlags.ReadOnly;
- Out << ", noRecurse: " << FFlags.NoRecurse;
- Out << ", returnDoesNotAlias: " << FFlags.ReturnDoesNotAlias;
- Out << ", noInline: " << FFlags.NoInline;
- Out << ", alwaysInline: " << FFlags.AlwaysInline;
- Out << ")";
- }
if (!FS->calls().empty()) {
Out << ", calls: (";
FieldSeparator IFS;
Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
===================================================================
--- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -1066,6 +1066,9 @@
RawFlags |= (Flags.ReturnDoesNotAlias << 3);
RawFlags |= (Flags.NoInline << 4);
RawFlags |= (Flags.AlwaysInline << 5);
+ RawFlags |= (Flags.NoUnwind << 6);
+ RawFlags |= (Flags.MayThrow << 7);
+ RawFlags |= (Flags.HasUnknownCall << 8);
return RawFlags;
}
Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp
===================================================================
--- llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -938,6 +938,9 @@
Flags.ReturnDoesNotAlias = (RawFlags >> 3) & 0x1;
Flags.NoInline = (RawFlags >> 4) & 0x1;
Flags.AlwaysInline = (RawFlags >> 5) & 0x1;
+ Flags.NoUnwind = (RawFlags >> 6) & 0x1;
+ Flags.MayThrow = (RawFlags >> 7) & 0x1;
+ Flags.HasUnknownCall = (RawFlags >> 8) & 0x1;
return Flags;
}
Index: llvm/lib/AsmParser/LLParser.cpp
===================================================================
--- llvm/lib/AsmParser/LLParser.cpp
+++ llvm/lib/AsmParser/LLParser.cpp
@@ -8521,6 +8521,9 @@
/// [',' 'returnDoesNotAlias' ':' Flag]? ')'
/// [',' 'noInline' ':' Flag]? ')'
/// [',' 'alwaysInline' ':' Flag]? ')'
+/// [',' 'noUnwind' ':' Flag]? ')'
+/// [',' 'mayThrow' ':' Flag]? ')'
+/// [',' 'hasUnknownCall' ':' Flag]? ')'
bool LLParser::parseOptionalFFlags(FunctionSummary::FFlags &FFlags) {
assert(Lex.getKind() == lltok::kw_funcFlags);
@@ -8569,6 +8572,24 @@
return true;
FFlags.AlwaysInline = Val;
break;
+ case lltok::kw_noUnwind:
+ Lex.Lex();
+ if (parseToken(lltok::colon, "expected ':'") || parseFlag(Val))
+ return true;
+ FFlags.NoUnwind = Val;
+ break;
+ case lltok::kw_mayThrow:
+ Lex.Lex();
+ if (parseToken(lltok::colon, "expected ':'") || parseFlag(Val))
+ return true;
+ FFlags.MayThrow = Val;
+ break;
+ case lltok::kw_hasUnknownCall:
+ Lex.Lex();
+ if (parseToken(lltok::colon, "expected ':'") || parseFlag(Val))
+ return true;
+ FFlags.HasUnknownCall = Val;
+ break;
default:
return error(Lex.getLoc(), "expected function flag type");
}
Index: llvm/lib/AsmParser/LLLexer.cpp
===================================================================
--- llvm/lib/AsmParser/LLLexer.cpp
+++ llvm/lib/AsmParser/LLLexer.cpp
@@ -770,6 +770,9 @@
KEYWORD(returnDoesNotAlias);
KEYWORD(noInline);
KEYWORD(alwaysInline);
+ KEYWORD(noUnwind);
+ KEYWORD(mayThrow);
+ KEYWORD(hasUnknownCall);
KEYWORD(calls);
KEYWORD(callee);
KEYWORD(params);
Index: llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
===================================================================
--- llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
+++ llvm/lib/Analysis/ModuleSummaryAnalysis.cpp
@@ -265,6 +265,8 @@
bool HasInlineAsmMaybeReferencingInternal = false;
bool HasIndirBranchToBlockAddress = false;
+ bool HasUnknownCall = false;
+ bool MayThrow = false;
for (const BasicBlock &BB : F) {
// We don't allow inlining of function with indirect branch to blockaddress.
// If the blockaddress escapes the function, e.g., via a global variable,
@@ -282,6 +284,7 @@
if (I.isDebugOrPseudoInst())
continue;
++NumInsts;
+
// Regular LTO module doesn't participate in ThinLTO import,
// so no reference from it can be read/writeonly, since this
// would require importing variable as local copy
@@ -313,8 +316,11 @@
}
findRefEdges(Index, &I, RefEdges, Visited);
const auto *CB = dyn_cast<CallBase>(&I);
- if (!CB)
+ if (!CB) {
+ if (I.mayThrow())
+ MayThrow = true;
continue;
+ }
const auto *CI = dyn_cast<CallInst>(&I);
// Since we don't know exactly which local values are referenced in inline
@@ -370,6 +376,7 @@
ValueInfo.updateRelBlockFreq(BBFreq, EntryFreq);
}
} else {
+ HasUnknownCall = true;
// Skip inline assembly calls.
if (CI && CI->isInlineAsm())
continue;
@@ -480,7 +487,8 @@
// FIXME: refactor this to use the same code that inliner is using.
// Don't try to import functions with noinline attribute.
F.getAttributes().hasFnAttr(Attribute::NoInline),
- F.hasFnAttribute(Attribute::AlwaysInline)};
+ F.hasFnAttribute(Attribute::AlwaysInline),
+ F.hasFnAttribute(Attribute::NoUnwind), MayThrow, HasUnknownCall};
std::vector<FunctionSummary::ParamAccess> ParamAccesses;
if (auto *SSI = GetSSICallback(F))
ParamAccesses = SSI->getParamAccesses(Index);
@@ -726,7 +734,10 @@
F->hasFnAttribute(Attribute::NoRecurse),
F->returnDoesNotAlias(),
/* NoInline = */ false,
- F->hasFnAttribute(Attribute::AlwaysInline)},
+ F->hasFnAttribute(Attribute::AlwaysInline),
+ F->hasFnAttribute(Attribute::NoUnwind),
+ /* MayThrow */ true,
+ /* HasUnknownCall */ true},
/*EntryCount=*/0, ArrayRef<ValueInfo>{},
ArrayRef<FunctionSummary::EdgeTy>{},
ArrayRef<GlobalValue::GUID>{},
Index: llvm/include/llvm/Transforms/IPO/FunctionImport.h
===================================================================
--- llvm/include/llvm/Transforms/IPO/FunctionImport.h
+++ llvm/include/llvm/Transforms/IPO/FunctionImport.h
@@ -222,12 +222,15 @@
StringRef ModulePath, StringRef OutputFilename,
const std::map<std::string, GVSummaryMapTy> &ModuleToSummariesForIndex);
-/// Resolve prevailing symbol linkages and constrain visibility (1. CanAutoHide,
-/// 2. consider visibility from other definitions for ELF) in \p TheModule based
-/// on the information recorded in the summaries during global summary-based
-/// analysis.
-void thinLTOResolvePrevailingInModule(Module &TheModule,
- const GVSummaryMapTy &DefinedGlobals);
+/// Based on the information recorded in the summaries during global
+/// summary-based analysis:
+/// 1. Resolve prevailing symbol linkages and constrain visibility (CanAutoHide
+/// and consider visibility from other definitions for ELF) in \p TheModule
+/// 2. (optional) Apply propagated function attributes to \p TheModule if
+/// PropagateAttrs is true
+void thinLTOFinalizeInModule(Module &TheModule,
+ const GVSummaryMapTy &DefinedGlobals,
+ bool PropagateAttrs);
/// Internalize \p TheModule based on the information recorded in the summaries
/// during global summary-based analysis.
Index: llvm/include/llvm/Transforms/IPO/FunctionAttrs.h
===================================================================
--- llvm/include/llvm/Transforms/IPO/FunctionAttrs.h
+++ llvm/include/llvm/Transforms/IPO/FunctionAttrs.h
@@ -17,6 +17,7 @@
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/LazyCallGraph.h"
+#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/IR/PassManager.h"
namespace llvm {
@@ -38,6 +39,13 @@
/// Returns the memory access properties of this copy of the function.
MemoryAccessKind computeFunctionBodyMemoryAccess(Function &F, AAResults &AAR);
+/// Propagate function attributes for function summaries along the index's
+/// callgraph during thinlink
+bool thinLTOPropagateFunctionAttrs(
+ ModuleSummaryIndex &Index,
+ function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)>
+ isPrevailing);
+
/// Computes function attributes in post-order over the call graph.
///
/// By operating in post-order, this pass computes precise attributes for
Index: llvm/include/llvm/LTO/LTO.h
===================================================================
--- llvm/include/llvm/LTO/LTO.h
+++ llvm/include/llvm/LTO/LTO.h
@@ -23,6 +23,7 @@
#include "llvm/Object/IRSymtab.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/thread.h"
+#include "llvm/Transforms/IPO/FunctionAttrs.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
namespace llvm {
@@ -38,7 +39,7 @@
/// Resolve linkage for prevailing symbols in the \p Index. Linkage changes
/// recorded in the index and the ThinLTO backends must apply the changes to
-/// the module via thinLTOResolvePrevailingInModule.
+/// the module via thinLTOFinalizeInModule.
///
/// This is done for correctness (if value exported, ensure we always
/// emit a copy), and compile-time optimization (allow drop of duplicates).
Index: llvm/include/llvm/IR/ModuleSummaryIndex.h
===================================================================
--- llvm/include/llvm/IR/ModuleSummaryIndex.h
+++ llvm/include/llvm/IR/ModuleSummaryIndex.h
@@ -572,6 +572,50 @@
unsigned NoInline : 1;
// Indicate if function should be always inlined.
unsigned AlwaysInline : 1;
+ // Indicate if function never raises an exception. Can be modified during
+ // thinlink function attribute propagation
+ unsigned NoUnwind : 1;
+ // Indicate if function contains instructions that mayThrow
+ unsigned MayThrow : 1;
+
+ // If there are calls to unknown targets (e.g. indirect)
+ unsigned HasUnknownCall : 1;
+
+ FFlags &operator&=(const FFlags &RHS) {
+ this->ReadNone &= RHS.ReadNone;
+ this->ReadOnly &= RHS.ReadOnly;
+ this->NoRecurse &= RHS.NoRecurse;
+ this->ReturnDoesNotAlias &= RHS.ReturnDoesNotAlias;
+ this->NoInline &= RHS.NoInline;
+ this->AlwaysInline &= RHS.AlwaysInline;
+ this->NoUnwind &= RHS.NoUnwind;
+ this->MayThrow &= RHS.MayThrow;
+ this->HasUnknownCall &= RHS.HasUnknownCall;
+ return *this;
+ }
+
+ bool anyFlagSet() {
+ return this->ReadNone | this->ReadOnly | this->NoRecurse |
+ this->ReturnDoesNotAlias | this->NoInline | this->AlwaysInline |
+ this->NoUnwind | this->MayThrow | this->HasUnknownCall;
+ }
+
+ operator std::string() {
+ std::string Output;
+ raw_string_ostream OS(Output);
+ OS << "funcFlags: (";
+ OS << "readNone: " << this->ReadNone;
+ OS << ", readOnly: " << this->ReadOnly;
+ OS << ", noRecurse: " << this->NoRecurse;
+ OS << ", returnDoesNotAlias: " << this->ReturnDoesNotAlias;
+ OS << ", noInline: " << this->NoInline;
+ OS << ", alwaysInline: " << this->AlwaysInline;
+ OS << ", noUnwind: " << this->NoUnwind;
+ OS << ", mayThrow: " << this->MayThrow;
+ OS << ", hasUnknownCall: " << this->HasUnknownCall;
+ OS << ")";
+ return OS.str();
+ }
};
/// Describes the uses of a parameter by the function.
@@ -688,6 +732,10 @@
/// Get function summary flags.
FFlags fflags() const { return FunFlags; }
+ void setNoRecurse() { FunFlags.NoRecurse = true; }
+
+ void setNoUnwind() { FunFlags.NoUnwind = true; }
+
/// Get the instruction count recorded for this function.
unsigned instCount() const { return InstCount; }
Index: llvm/include/llvm/IR/GlobalValue.h
===================================================================
--- llvm/include/llvm/IR/GlobalValue.h
+++ llvm/include/llvm/IR/GlobalValue.h
@@ -302,11 +302,14 @@
static bool isAvailableExternallyLinkage(LinkageTypes Linkage) {
return Linkage == AvailableExternallyLinkage;
}
+ static bool isLinkOnceAnyLinkage(LinkageTypes Linkage) {
+ return Linkage == LinkOnceAnyLinkage;
+ }
static bool isLinkOnceODRLinkage(LinkageTypes Linkage) {
return Linkage == LinkOnceODRLinkage;
}
static bool isLinkOnceLinkage(LinkageTypes Linkage) {
- return Linkage == LinkOnceAnyLinkage || Linkage == LinkOnceODRLinkage;
+ return isLinkOnceAnyLinkage(Linkage) || isLinkOnceODRLinkage(Linkage);
}
static bool isWeakAnyLinkage(LinkageTypes Linkage) {
return Linkage == WeakAnyLinkage;
@@ -433,6 +436,9 @@
return isAvailableExternallyLinkage(getLinkage());
}
bool hasLinkOnceLinkage() const { return isLinkOnceLinkage(getLinkage()); }
+ bool hasLinkOnceAnyLinkage() const {
+ return isLinkOnceAnyLinkage(getLinkage());
+ }
bool hasLinkOnceODRLinkage() const {
return isLinkOnceODRLinkage(getLinkage());
}
Index: llvm/include/llvm/AsmParser/LLToken.h
===================================================================
--- llvm/include/llvm/AsmParser/LLToken.h
+++ llvm/include/llvm/AsmParser/LLToken.h
@@ -404,6 +404,9 @@
kw_returnDoesNotAlias,
kw_noInline,
kw_alwaysInline,
+ kw_noUnwind,
+ kw_mayThrow,
+ kw_hasUnknownCall,
kw_calls,
kw_callee,
kw_params,
Index: clang/test/CodeGen/thinlto-funcattr-prop.ll
===================================================================
--- /dev/null
+++ clang/test/CodeGen/thinlto-funcattr-prop.ll
@@ -0,0 +1,43 @@
+; REQUIRES: x86-registered-target
+
+; Test that FunctionAttr Propagation is generating correct summaries
+
+; RUN: split-file %s %t
+; RUN: opt -module-summary %t/a.ll -o %t/a.bc
+; RUN: opt -module-summary %t/b.ll -o %t/b.bc
+
+; RUN: llvm-lto2 run -disable-thinlto-funcattrs=0 %t/a.bc %t/b.bc -o %t1.o -save-temps \
+; RUN: -r=%t/a.bc,call_extern,plx \
+; RUN: -r=%t/a.bc,extern, \
+; RUN: -r=%t/b.bc,extern,p
+
+; RUN: llvm-dis %t1.o.index.bc -o - | FileCheck %s --check-prefix=CHECK-INDEX
+; RUN: llvm-dis %t1.o.1.1.promote.bc -o - | FileCheck %s --check-prefix=CHECK-IR
+
+;; Summary for call_extern. Note that llvm-lto2 writes out the index before propagation occurs so call_extern doesn't have its flags updated.
+; CHECK-INDEX: ^2 = gv: (guid: 13959900437860518209, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 1, canAutoHide: 0), insts: 2, calls: ((callee: ^3)))))
+;; Summary for extern
+; CHECK-INDEX: ^3 = gv: (guid: 14959766916849974397, summaries: (function: (module: ^1, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0))))
+
+;--- a.ll
+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-unknown-linux-gnu"
+
+declare void @extern()
+
+; CHECK-IR: Function Attrs: norecurse nounwind
+; CHECK-IR-NEXT: define dso_local void @call_extern()
+define void @call_extern() {
+ call void @extern()
+ ret void
+}
+
+;--- b.ll
+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-unknown-linux-gnu"
+
+attributes #0 = { nounwind norecurse }
+
+define void @extern() #0 {
+ ret void
+}
Index: clang/test/CodeGen/thinlto-distributed-cfi.ll
===================================================================
--- clang/test/CodeGen/thinlto-distributed-cfi.ll
+++ clang/test/CodeGen/thinlto-distributed-cfi.ll
@@ -4,7 +4,7 @@
; RUN: opt -thinlto-bc -thinlto-split-lto-unit -o %t.o %s
-; RUN: llvm-lto2 run -thinlto-distributed-indexes %t.o \
+; RUN: llvm-lto2 run -thinlto-distributed-indexes -disable-thinlto-funcattrs=0 %t.o \
; RUN: -o %t2.index \
; RUN: -r=%t.o,test,px \
; RUN: -r=%t.o,_ZTV1B, \
@@ -24,7 +24,7 @@
; Round trip it through llvm-as
; RUN: llvm-dis %t.o.thinlto.bc -o - | llvm-as -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-DIS
; CHECK-DIS: ^0 = module: (path: "{{.*}}thinlto-distributed-cfi.ll.tmp.o", hash: ({{.*}}, {{.*}}, {{.*}}, {{.*}}, {{.*}}))
-; CHECK-DIS: ^1 = gv: (guid: 8346051122425466633, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 7, typeIdInfo: (typeTests: (^2)))))
+; CHECK-DIS: ^1 = gv: (guid: 8346051122425466633, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 7, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 1, mayThrow: 0, hasUnknownCall: 0), typeIdInfo: (typeTests: (^2)))))
; CHECK-DIS: ^2 = typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: single, sizeM1BitWidth: 0))) ; guid = 7004155349499253778
; RUN: %clang_cc1 -triple x86_64-grtev4-linux-gnu \
Index: clang/test/CodeGen/thinlto-distributed-cfi-devirt.ll
===================================================================
--- clang/test/CodeGen/thinlto-distributed-cfi-devirt.ll
+++ clang/test/CodeGen/thinlto-distributed-cfi-devirt.ll
@@ -7,7 +7,7 @@
; RUN: opt -thinlto-bc -thinlto-split-lto-unit -o %t.o %s
; FIXME: Fix machine verifier issues and remove -verify-machineinstrs=0. PR39436.
-; RUN: llvm-lto2 run -thinlto-distributed-indexes %t.o \
+; RUN: llvm-lto2 run -thinlto-distributed-indexes -disable-thinlto-funcattrs=0 %t.o \
; RUN: -whole-program-visibility \
; RUN: -verify-machineinstrs=0 \
; RUN: -o %t2.index \
@@ -36,7 +36,7 @@
; Round trip it through llvm-as
; RUN: llvm-dis %t.o.thinlto.bc -o - | llvm-as -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-DIS
; CHECK-DIS: ^0 = module: (path: "{{.*}}thinlto-distributed-cfi-devirt.ll.tmp.o", hash: ({{.*}}, {{.*}}, {{.*}}, {{.*}}, {{.*}}))
-; CHECK-DIS: ^1 = gv: (guid: 8346051122425466633, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 18, typeIdInfo: (typeTests: (^2), typeCheckedLoadVCalls: (vFuncId: (^2, offset: 8), vFuncId: (^2, offset: 0))))))
+; CHECK-DIS: ^1 = gv: (guid: 8346051122425466633, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 18, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 1), typeIdInfo: (typeTests: (^2), typeCheckedLoadVCalls: (vFuncId: (^2, offset: 8), vFuncId: (^2, offset: 0))))))
; CHECK-DIS: ^2 = typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: allOnes, sizeM1BitWidth: 7), wpdResolutions: ((offset: 0, wpdRes: (kind: branchFunnel)), (offset: 8, wpdRes: (kind: singleImpl, singleImplName: "_ZN1A1nEi"))))) ; guid = 7004155349499253778
; RUN: %clang_cc1 -triple x86_64-grtev4-linux-gnu \
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits