Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -30,6 +30,7 @@ #include "llvm/Analysis/CaptureTracking.h" #include "llvm/Analysis/LazyCallGraph.h" #include "llvm/Analysis/MemoryLocation.h" +#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" @@ -1088,7 +1089,9 @@ return true; } -static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) { +template +static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes, + TTIGetterT TTIGetter) { // Try and identify functions that do not recurse. // If the SCC contains multiple nodes we know for sure there is recursion. @@ -1104,9 +1107,13 @@ // marked norecurse, so any called from F to F will not be marked norecurse. for (Instruction &I : instructions(*F)) if (auto CS = CallSite(&I)) { - Function *Callee = CS.getCalledFunction(); - if (!Callee || Callee == F || !Callee->doesNotRecurse()) - // Function calls a potentially recursive function. + const Function *Callee = CS.getCalledFunction(); + if (Callee && !TTIGetter(*F).isLoweredToCall(Callee)) + // Intrinsics do not set the NoRecurse flag, so + // isLoweredToCall needs to be checked before doesNotRecurse. + continue; + else if (!Callee || Callee == F || !Callee->doesNotRecurse()) + // F calls a potentially recursive function. return false; } @@ -1129,6 +1136,10 @@ return FAM.getResult(F); }; + auto TTIGetter = [&](Function &F) -> const TargetTransformInfo & { + return FAM.getResult(F); + }; + // Fill SCCNodes with the elements of the SCC. Also track whether there are // any external or opt-none nodes that will prevent us from optimizing any // part of the SCC. @@ -1168,7 +1179,7 @@ Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes); Changed |= removeConvergentAttrs(SCCNodes); - Changed |= addNoRecurseAttrs(SCCNodes); + Changed |= addNoRecurseAttrs(SCCNodes, TTIGetter); } return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); @@ -1190,6 +1201,8 @@ void getAnalysisUsage(AnalysisUsage &AU) const override { AU.setPreservesCFG(); AU.addRequired(); + AU.addRequired(); + AU.addPreserved(); getAAResultsAnalysisUsage(AU); CallGraphSCCPass::getAnalysisUsage(AU); } @@ -1202,6 +1215,7 @@ "Deduce function attributes", false, false) INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) +INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) INITIALIZE_PASS_END(PostOrderFunctionAttrsLegacyPass, "functionattrs", "Deduce function attributes", false, false) @@ -1209,8 +1223,9 @@ return new PostOrderFunctionAttrsLegacyPass(); } -template -static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) { +template +static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter, + TTIGetterT TTIGetter) { bool Changed = false; // Fill SCCNodes with the elements of the SCC. Used for quickly looking up @@ -1245,7 +1260,7 @@ Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes); Changed |= removeConvergentAttrs(SCCNodes); - Changed |= addNoRecurseAttrs(SCCNodes); + Changed |= addNoRecurseAttrs(SCCNodes, TTIGetter); } return Changed; @@ -1254,7 +1269,12 @@ bool PostOrderFunctionAttrsLegacyPass::runOnSCC(CallGraphSCC &SCC) { if (skipSCC(SCC)) return false; - return runImpl(SCC, LegacyAARGetter(*this)); + + auto TTIGetter = [&](Function &F) -> const TargetTransformInfo & { + return getAnalysis().getTTI(F); + }; + + return runImpl(SCC, LegacyAARGetter(*this), TTIGetter); } namespace { @@ -1271,6 +1291,7 @@ bool runOnModule(Module &M) override; void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); AU.setPreservesCFG(); AU.addRequired(); AU.addPreserved(); @@ -1284,6 +1305,7 @@ INITIALIZE_PASS_BEGIN(ReversePostOrderFunctionAttrsLegacyPass, "rpo-functionattrs", "Deduce function attributes in RPO", false, false) INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass) +INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) INITIALIZE_PASS_END(ReversePostOrderFunctionAttrsLegacyPass, "rpo-functionattrs", "Deduce function attributes in RPO", false, false) Index: test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll =================================================================== --- test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -43,13 +43,13 @@ ; This is unusual, since the function is memcpy, but as above, this ; isn't necessarily invalid. -; CHECK: define void @test2_yes(i8* nocapture %p, i8* nocapture %q, i64 %n) #4 { +; CHECK: define void @test2_yes(i8* nocapture %p, i8* nocapture %q, i64 %n) #0 { define void @test2_yes(i8* %p, i8* %q, i64 %n) nounwind { call void @llvm.memcpy.p0i8.p0i8.i64(i8* %p, i8* %q, i64 %n, i32 1, i1 false), !tbaa !1 ret void } -; CHECK: define void @test2_no(i8* nocapture %p, i8* nocapture readonly %q, i64 %n) #3 { +; CHECK: define void @test2_no(i8* nocapture %p, i8* nocapture readonly %q, i64 %n) #1 { define void @test2_no(i8* %p, i8* %q, i64 %n) nounwind { call void @llvm.memcpy.p0i8.p0i8.i64(i8* %p, i8* %q, i64 %n, i32 1, i1 false), !tbaa !2 ret void @@ -76,8 +76,7 @@ ; CHECK: attributes #1 = { norecurse nounwind } ; CHECK: attributes #2 = { nounwind readonly } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { nounwind readnone } -; CHECK: attributes #5 = { argmemonly nounwind } +; CHECK: attributes #4 = { argmemonly nounwind } ; Root note. !0 = !{ } Index: test/CodeGen/Generic/dbg-influenced-attrs.ll =================================================================== --- test/CodeGen/Generic/dbg-influenced-attrs.ll +++ test/CodeGen/Generic/dbg-influenced-attrs.ll @@ -0,0 +1,34 @@ +; RUN: opt -S -functionattrs < %s | FileCheck %s + +define void @f0() #0 { + call void @llvm.dbg.value(metadata i16 0, metadata !19, metadata !DIExpression()), !dbg !21 + ret void +} + +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { nounwind } +attributes #1 = { norecurse nounwind readnone speculatable } + +; CHECK: attributes #0 = { norecurse nounwind readnone } +; CHECK: attributes #1 = { nounwind readnone speculatable } + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3, !4, !5} +!llvm.ident = !{!6} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2) +!1 = !DIFile(filename: "foo.c", directory: "/") +!2 = !{} +!3 = !{i32 2, !"Dwarf Version", i32 4} +!4 = !{i32 2, !"Debug Info Version", i32 3} +!5 = !{i32 1, !"wchar_size", i32 1} +!6 = !{!"clang version 6.0.0"} +!7 = distinct !DISubprogram(name: "f2", scope: !1, file: !1, line: 33, type: !8, isLocal: true, isDefinition: true, scopeLine: 33, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2) +!8 = !DISubroutineType(types: !9) +!9 = !{null} +!17 = distinct !DISubprogram(name: "f0", scope: !1, file: !1, line: 31, type: !8, isLocal: true, isDefinition: true, scopeLine: 31, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !18) +!18 = !{!19} +!19 = !DILocalVariable(name: "a", scope: !17, file: !1, line: 31, type: !20) +!20 = !DIBasicType(name: "int", size: 16, encoding: DW_ATE_signed) +!21 = !DILocation(line: 31, column: 36, scope: !17) Index: test/Other/cgscc-devirt-iteration.ll =================================================================== --- test/Other/cgscc-devirt-iteration.ll +++ test/Other/cgscc-devirt-iteration.ll @@ -104,7 +104,7 @@ ; creates the memcpy intrinsic call, and we rely on the count of indirect calls ; decreasing and the count of direct calls increasing. define void @test3(i8* %src, i8* %dest, i64 %size) { -; CHECK-NOT: Function Attrs +; AFTER: Function Attrs: norecurse ; BEFORE: define void @test3(i8* %src, i8* %dest, i64 %size) ; AFTER: define void @test3(i8* nocapture readonly %src, i8* nocapture %dest, i64 %size) %fptr = alloca i8* (i8*, i8*, i64)* Index: test/Other/new-pm-lto-defaults.ll =================================================================== --- test/Other/new-pm-lto-defaults.ll +++ test/Other/new-pm-lto-defaults.ll @@ -48,6 +48,7 @@ ; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph{{.*}}> ; CHECK-O-NEXT: Running analysis: AAManager ; CHECK-O1-NEXT: Running analysis: TargetLibraryAnalysis +; CHECK-O-NEXT: Running analysis: TargetIRAnalysis ; CHECK-O-NEXT: Running pass: ReversePostOrderFunctionAttrsPass ; CHECK-O-NEXT: Running analysis: CallGraphAnalysis ; CHECK-O-NEXT: Running pass: GlobalSplitPass @@ -77,7 +78,6 @@ ; CHECK-O2-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}PostOrderFunctionAttrsPass> ; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}> ; CHECK-O2-NEXT: Running analysis: MemoryDependenceAnalysis -; CHECK-O2-NEXT: Running analysis: TargetIRAnalysis ; CHECK-O2-NEXT: Running analysis: DemandedBitsAnalysis ; CHECK-O2-NEXT: Running pass: CrossDSOCFIPass ; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}SimplifyCFGPass> Index: test/Transforms/FunctionAttrs/int_sideeffect.ll =================================================================== --- test/Transforms/FunctionAttrs/int_sideeffect.ll +++ test/Transforms/FunctionAttrs/int_sideeffect.ll @@ -5,13 +5,13 @@ ; Don't add readnone or similar attributes when an @llvm.sideeffect() intrinsic ; is present. -; CHECK: define void @test() { +; CHECK: define void @test() #1 { define void @test() { call void @llvm.sideeffect() ret void } -; CHECK: define void @loop() { +; CHECK: define void @loop() #1 { define void @loop() { br label %loop @@ -19,3 +19,5 @@ call void @llvm.sideeffect() br label %loop } + +; CHECK: attributes #1 = { norecurse } Index: test/Transforms/FunctionAttrs/norecurse.ll =================================================================== --- test/Transforms/FunctionAttrs/norecurse.ll +++ test/Transforms/FunctionAttrs/norecurse.ll @@ -30,8 +30,8 @@ } declare i32 @k() readnone -; CHECK: define void @intrinsic(i8* nocapture %dest, i8* nocapture readonly %src, i32 %len) { -define void @intrinsic(i8* %dest, i8* %src, i32 %len) { +; CHECK: define void @intrinsic(i8* nocapture %dest, i8* nocapture readonly %src, i32 %len) #2 { +define void @intrinsic(i8* %dest, i8* %src, i32 %len) #2 { call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i32 1, i1 false) ret void } @@ -63,3 +63,4 @@ ; CHECK: attributes #0 = { norecurse readnone } ; CHECK: attributes #1 = { readnone } +; CHECK: attributes #2 = { norecurse }