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