modimo updated this revision to Diff 368174.
modimo added a comment.

Minor test fixups


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
  llvm/test/ThinLTO/X86/weak_externals.ll

Index: llvm/test/ThinLTO/X86/weak_externals.ll
===================================================================
--- llvm/test/ThinLTO/X86/weak_externals.ll
+++ llvm/test/ThinLTO/X86/weak_externals.ll
@@ -40,4 +40,3 @@
 define linkonce_odr dso_local dereferenceable(16) %struct.S* @_ZN9SingletonI1SE11getInstanceEv() #0 comdat align 2 {
   ret %struct.S* @_ZZN9SingletonI1SE11getInstanceEvE8instance
 }
-
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

Reply via email to