modimo updated this revision to Diff 368178. modimo added a comment. Remove llvm/test/ThinLTO/X86/weak_externals.ll from diffs
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/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/test/Assembler/thinlto-summary.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-indirect.ll llvm/test/ThinLTO/X86/funcattrs-prop-undefined.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)))) Index: llvm/test/ThinLTO/X86/funcattrs-prop.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/funcattrs-prop.ll @@ -0,0 +1,150 @@ +; 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 + +;; Function attribute propagation +;; 1. If external, linkonce_odr or weak_odr. We capture the attributes of the prevailing symbol for propagation. +;; 2. If linkonce or weak, individual callers may optimize using different copies. As such, we conservatively merge +;; the attributes of all copies for propagation. +; 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,p \ +; 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, -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" + +declare void @extern() +declare void @linkonceodr() +declare void @weakodr() + +declare void @linkonce() +declare void @weak() + +; b.ll contains non-recursing copies +; c.ll contains recursing copies +declare void @linkonce_may_unwind() +declare void @weak_may_unwind() + +; CHECK: define dso_local void @call_extern() [[ATTR_PROP:#[0-9]+]] +define void @call_extern() { + call void @extern() + ret void +} + +; CHECK: define dso_local void @call_linkonceodr() [[ATTR_PROP:#[0-9]+]] +define void @call_linkonceodr() { + call void @linkonceodr() + ret void +} + +; CHECK: define dso_local void @call_weakodr() [[ATTR_PROP:#[0-9]+]] +define void @call_weakodr() { + call void @weakodr() + ret void +} + +; CHECK: define dso_local void @call_linkonce() [[ATTR_PROP:#[0-9]+]] +define void @call_linkonce() { + call void @linkonce() + ret void +} + +; CHECK: define dso_local void @call_weak() [[ATTR_PROP:#[0-9]+]] +define void @call_weak() { + call void @weak() + ret void +} + +; CHECK: define dso_local void @call_linkonce_may_unwind() [[ATTR_NORECURSE:#[0-9]+]] +define void @call_linkonce_may_unwind() { + call void @linkonce_may_unwind() + ret void +} + +; CHECK: define dso_local void @call_weak_may_unwind() [[ATTR_NORECURSE:#[0-9]+]] +define void @call_weak_may_unwind() { + call void @weak_may_unwind() + ret void +} + +; CHECK-DAG: attributes [[ATTR_PROP]] = { norecurse nounwind } +; CHECK-DAG: attributes [[ATTR_NORECURSE]] = { 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-undefined.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/funcattrs-prop-undefined.ll @@ -0,0 +1,32 @@ + +; 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-indirect.ll =================================================================== --- /dev/null +++ llvm/test/ThinLTO/X86/funcattrs-prop-indirect.ll @@ -0,0 +1,14 @@ +; 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 -r %t1.bc,indirect,px -save-temps +; 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: norecurse nounwind +; CHECK: define i32 @indirect(i32 ()* nocapture %0) { +define i32 @indirect(i32 ()* nocapture) { + %2 = tail call i32 %0() + ret i32 %2 +} 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: 0000000)}"]; // 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: 0000000)}",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: 0000000)}"]; // 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: 0000100)}"]; // 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: 0000000)}",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/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)))) ; 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)))) +; 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)))) +; 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), 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/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/DenseSet.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(NumThinLTONoRecurse, + "Number of functions marked as norecurse during thinLTO"); +STATISTIC(NumThinLTONoUnwind, + "Number of functions marked as nounwind during thinLTO"); + 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,14 @@ "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")); + +static cl::opt<bool> ConservativeWeakLinkage( + "thinlto-funcattrs-conservative-weak", cl::init(false), cl::Hidden, + cl::desc("Go conservative when propagating weak linkage")); + namespace { using SCCNodeSet = SmallSetVector<Function *, 8>; @@ -322,6 +336,282 @@ return MadeChange; } +namespace { +struct ThinLTOAttributeSummary { + SmallVector<const FunctionSummary *, 1> FunctionSummaries; + FunctionSummary::FFlags Flags; +}; +} // namespace + +// Compute definitive function attributes for a function taking into account +// prevailing definitions and linkage types +static ThinLTOAttributeSummary &calculateDefinitiveAttributes( + ValueInfo VI, + DenseMap<ValueInfo, ThinLTOAttributeSummary> &CachedAttributes, + function_ref<bool(GlobalValue::GUID, const GlobalValueSummary *)> + IsPrevailing) { + + if (CachedAttributes.count(VI)) + return CachedAttributes[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. Merge to find the + // conservative set of attributes for propagation. Note that the other + // copies will have been demoted to AvailableExternally + // 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 + + CachedAttributes[VI] = {}; + FunctionSummary::FFlags CombinedFlags; + bool Combined = false; + const FunctionSummary *Local = nullptr; + const FunctionSummary *Prevailing = nullptr; + SmallVector<const FunctionSummary *, 1> CombinedSummaries; + + for (const auto &GVS : VI.getSummaryList()) { + if (!GVS->isLive()) + continue; + + const FunctionSummary *FS = nullptr; + + if (const AliasSummary *AS = dyn_cast<AliasSummary>(GVS.get())) { + assert(AS->hasAliasee()); + FS = dyn_cast<FunctionSummary>(AS->getBaseObject()); + } else { + FS = dyn_cast<FunctionSummary>(GVS.get()); + } + + // Virtual calls are unknown so go conservative + if (!FS || FS->getTypeIdInfo()) + return CachedAttributes[VI]; + + 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 CachedAttributes[VI]; + } + Local = FS; + } else if (GlobalValue::isExternalLinkage(Linkage)) { + assert(IsPrevailing(VI.getGUID(), GVS.get())); + Prevailing = FS; + break; + } else if (GlobalValue::isWeakODRLinkage(Linkage) || + GlobalValue::isLinkOnceODRLinkage(Linkage)) { + if (IsPrevailing(VI.getGUID(), GVS.get())) { + Prevailing = FS; + break; + } + } else if (GlobalValue::isWeakAnyLinkage(Linkage) || + GlobalValue::isLinkOnceAnyLinkage(Linkage) || + (GlobalValue::isAvailableExternallyLinkage(Linkage) && + Combined)) { + if (ConservativeWeakLinkage) + return CachedAttributes[VI]; + LLVM_DEBUG(dbgs() << "ThinLTO FunctionAttrs: Merging " << FS->fflags() + << " from " << VI.name() << " in " << FS->modulePath() + << " \n"); + if (!Combined) + CombinedFlags = FS->fflags(); + else + CombinedFlags &= FS->fflags(); + + Combined = true; + CombinedSummaries.push_back(FS); + } else if (GlobalValue::isAvailableExternallyLinkage(Linkage)) { + // TODO: Handle these cases if they become meaningful + return CachedAttributes[VI]; + } + } + + if (Local) { + assert(!Prevailing && !Combined); + CachedAttributes[VI].FunctionSummaries.push_back(Local); + CachedAttributes[VI].Flags = Local->fflags(); + } else if (Prevailing) { + assert(!Local && !Combined); + CachedAttributes[VI].FunctionSummaries.push_back(Prevailing); + CachedAttributes[VI].Flags = Prevailing->fflags(); + } else if (Combined) { + LLVM_DEBUG(dbgs() << "ThinLTO FunctionAttrs: resolving as Combined " + << VI.name() << "\n"); + LLVM_DEBUG(dbgs() << "ThinLTO FunctionAttrs: merging function flags " + << CombinedFlags << " for " << VI.name() << "\n"); + CachedAttributes[VI].FunctionSummaries = std::move(CombinedSummaries); + CachedAttributes[VI].Flags = CombinedFlags; + } + + return CachedAttributes[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, ThinLTOAttributeSummary> CachedAttributes; + 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) { + ThinLTOAttributeSummary &Summary = + calculateDefinitiveAttributes(V, CachedAttributes, IsPrevailing); + + // Function summaries can fail to contain information such as declarations + if (Summary.FunctionSummaries.empty()) + return; + + ArrayRef<FunctionSummary::EdgeTy> Callees; + std::vector<FunctionSummary::EdgeTy> CalleesVector; + CalleesVector.clear(); + + if (Summary.FunctionSummaries.size() == 1) + Callees = Summary.FunctionSummaries[0]->calls(); + else { + DenseSet<ValueInfo> CallGraphEdgeSet; + for (const auto &FS : Summary.FunctionSummaries) + for (const auto &Callee : FS->calls()) + CallGraphEdgeSet.insert(Callee.first); + + for (const auto &Callee : CallGraphEdgeSet) { + CalleesVector.push_back({Callee, {}}); + } + Callees = CalleesVector; + } + + for (const auto &Callee : Callees) { + ThinLTOAttributeSummary &CalleeSummary = calculateDefinitiveAttributes( + Callee.first, CachedAttributes, IsPrevailing); + + if (CalleeSummary.FunctionSummaries.empty()) + return; + + if (!CalleeSummary.Flags.NoRecurse) + InferredFlags.NoRecurse = false; + + if (!CalleeSummary.Flags.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"); + ++NumThinLTONoRecurse; + CachedAttributes[V].Flags.NoRecurse = true; + } + + if (InferredFlags.NoUnwind) { + LLVM_DEBUG(dbgs() << "ThinLTO FunctionAttrs: Propagated NoUnwind to " + << V.name() << "\n"); + ++NumThinLTONoUnwind; + CachedAttributes[V].Flags.NoUnwind = true; + } + + 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; +} + +/// Insert function attributes in the Index back into the \p TheModule +void llvm::thinLTOInsertFunctionAttrsForModule( + Module &TheModule, const GVSummaryMapTy &DefinedGlobals) { + if (DisableThinLTOPropagation) + return; + + for (Function &F : TheModule) { + const auto &GV = DefinedGlobals.find(F.getGUID()); + if (GV == DefinedGlobals.end()) + continue; + + if (FunctionSummary *FS = dyn_cast<FunctionSummary>(GV->second)) { + // TODO: propagate ReadNone and ReadOnly in thinlink. + 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(); + } + } +} + 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" @@ -506,6 +507,8 @@ // Apply summary-based prevailing-symbol resolution decisions. thinLTOResolvePrevailingInModule(TheModule, DefinedGlobals); + thinLTOInsertFunctionAttrsForModule(TheModule, DefinedGlobals); + // Save temps: after promotion. saveTempBitcode(TheModule, SaveTempsDir, count, ".1.promoted.bc"); } @@ -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 @@ -608,6 +608,8 @@ thinLTOResolvePrevailingInModule(Mod, DefinedGlobals); + thinLTOInsertFunctionAttrsForModule(Mod, DefinedGlobals); + 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 @@ -1516,6 +1516,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 @@ -446,9 +446,10 @@ 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), 0}; return FlagRep; } Index: llvm/lib/IR/AsmWriter.cpp =================================================================== --- llvm/lib/IR/AsmWriter.cpp +++ llvm/lib/IR/AsmWriter.cpp @@ -3199,19 +3199,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,7 @@ RawFlags |= (Flags.ReturnDoesNotAlias << 3); RawFlags |= (Flags.NoInline << 4); RawFlags |= (Flags.AlwaysInline << 5); + RawFlags |= (Flags.NoUnwind << 6); return RawFlags; } Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -933,6 +933,7 @@ Flags.ReturnDoesNotAlias = (RawFlags >> 3) & 0x1; Flags.NoInline = (RawFlags >> 4) & 0x1; Flags.AlwaysInline = (RawFlags >> 5) & 0x1; + Flags.NoUnwind = (RawFlags >> 6) & 0x1; return Flags; } Index: llvm/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/lib/AsmParser/LLParser.cpp +++ llvm/lib/AsmParser/LLParser.cpp @@ -8521,6 +8521,7 @@ /// [',' 'returnDoesNotAlias' ':' Flag]? ')' /// [',' 'noInline' ':' Flag]? ')' /// [',' 'alwaysInline' ':' Flag]? ')' +/// [',' 'noUnwind' ':' Flag]? ')' bool LLParser::parseOptionalFFlags(FunctionSummary::FFlags &FFlags) { assert(Lex.getKind() == lltok::kw_funcFlags); @@ -8569,6 +8570,12 @@ 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; 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,7 @@ KEYWORD(returnDoesNotAlias); KEYWORD(noInline); KEYWORD(alwaysInline); + KEYWORD(noUnwind); KEYWORD(calls); KEYWORD(callee); KEYWORD(params); Index: llvm/lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -480,7 +480,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)}; std::vector<FunctionSummary::ParamAccess> ParamAccesses; if (auto *SSI = GetSSICallback(F)) ParamAccesses = SSI->getParamAccesses(Index); @@ -726,7 +727,8 @@ F->hasFnAttribute(Attribute::NoRecurse), F->returnDoesNotAlias(), /* NoInline = */ false, - F->hasFnAttribute(Attribute::AlwaysInline)}, + F->hasFnAttribute(Attribute::AlwaysInline), + F->hasFnAttribute(Attribute::NoUnwind)}, /*EntryCount=*/0, ArrayRef<ValueInfo>{}, ArrayRef<FunctionSummary::EdgeTy>{}, ArrayRef<GlobalValue::GUID>{}, 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,17 @@ /// 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); + +/// Inserts the FunctionAttr flags from the Index into \p TheModule. +void thinLTOInsertFunctionAttrsForModule(Module &TheModule, + const GVSummaryMapTy &DefinedGlobals); + /// 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 { Index: llvm/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -572,6 +572,39 @@ unsigned NoInline : 1; // Indicate if function should be always inlined. unsigned AlwaysInline : 1; + unsigned NoUnwind : 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; + return *this; + } + + bool anyFlagSet() { + return this->ReadNone | this->ReadOnly | this->NoRecurse | + this->ReturnDoesNotAlias | this->NoInline | this->AlwaysInline | + this->NoUnwind; + } + + 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 << ")"; + return OS.str(); + } }; /// Describes the uses of a parameter by the function. @@ -688,6 +721,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,7 @@ kw_returnDoesNotAlias, kw_noInline, kw_alwaysInline, + kw_noUnwind, 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,39 @@ +; 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 -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 %t/b.bc -o - | FileCheck %s + +; CHECK: ^0 = module: (path: "{{.*}}b.bc", hash: ({{.*}}, {{.*}}, {{.*}}, {{.*}}, {{.*}})) +; CHECK: ^1 = gv: (name: "extern", summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0, alwaysInline: 0, noUnwind: 1)))) ; guid = 14959766916849974397 +; CHECK: ^2 = blockcount: 1 + +;--- 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() + +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 +} \ No newline at end of file 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, \ 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 \
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits