Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" @@ -74,6 +75,7 @@ STATISTIC(NumNoAlias, "Number of function returns marked noalias"); STATISTIC(NumNonNullReturn, "Number of function returns marked nonnull"); STATISTIC(NumNoRecurse, "Number of functions marked as norecurse"); +STATISTIC(NumNoUnwind, "Number of functions marked as nounwind"); // FIXME: This is disabled by default to avoid exposing security vulnerabilities // in C/C++ code compiled by clang: @@ -1037,49 +1039,198 @@ return MadeChange; } -/// Remove the convergent attribute from all functions in the SCC if every -/// callsite within the SCC is not convergent (except for calls to functions -/// within the SCC). Returns true if changes were made. -static bool removeConvergentAttrs(const SCCNodeSet &SCCNodes) { - // For every function in SCC, ensure that either - // * it is not convergent, or - // * we can remove its convergent attribute. - bool HasConvergentFn = false; +namespace { + +class AttributeInferer { + // We provide a generic facility to infer many attributes at once + // customizing individual attributes handling by a handful of predicates. + using SkipFunctionPredicate = std::function; + using InvalidInstructionPredicate = std::function; + using SetAttributeFunc = std::function; + + SmallVector, + 4> + AttributePredicates; + +public: + /// setup to infer attribute that might be subject to derefinement + void inferDerefinableAttr(SkipFunctionPredicate SkipFunc, + InvalidInstructionPredicate InvalidInstr, + SetAttributeFunc SetAttr) { + AttributePredicates.push_back( + std::make_tuple(true, SkipFunc, InvalidInstr, SetAttr)); + } + + /// setup to infer attribute that does not suffer from derefinement + void inferNonDerefinableAttr(SkipFunctionPredicate SkipFunc, + InvalidInstructionPredicate InvalidInstr, + SetAttributeFunc SetAttr) { + AttributePredicates.push_back( + std::make_tuple(false, SkipFunc, InvalidInstr, SetAttr)); + } + + bool run(const SCCNodeSet &SCCNodes) const; +}; + +/// Perform all the requested attribute inference actions according to the +/// attribute predicates stored before. +bool AttributeInferer::run(const SCCNodeSet &SCCNodes) const { + unsigned AttrCount = AttributePredicates.size(); + SmallBitVector ValidAttrs(AttrCount, true); + SmallBitVector ReadyAttrs(AttrCount, false); + for (Function *F : SCCNodes) { - if (!F->isConvergent()) continue; - HasConvergentFn = true; + if (ValidAttrs.count() == 0) + return false; + + SmallBitVector ScanAttrsHere = ValidAttrs; - // Can't remove convergent from function declarations. - if (F->isDeclaration()) return false; + for (unsigned A = 0; A < AttrCount; A++) { + if (!ScanAttrsHere[A]) + continue; + // check if this attribute needs scanning + auto &AP = AttributePredicates[A]; + SkipFunctionPredicate SkipFuncPred = std::get<1>(AP); + if (SkipFuncPred(*F)) + ScanAttrsHere[A] = false; + else { + bool Derefinable = std::get<0>(AP); + if ((!Derefinable && F->isDeclaration()) || + (Derefinable && !F->hasExactDefinition())) { + // no instructions to scan, cant handle this attribute + ScanAttrsHere[A] = false; + ValidAttrs[A] = false; + } + } + } - // Can't remove convergent if any of our functions has a convergent call to a - // function not in the SCC. + // nothing to scan in this func + if (ScanAttrsHere.count() == 0) + continue; + + // Start instruction scan for (Instruction &I : instructions(*F)) { - CallSite CS(&I); - // Bail if CS is a convergent call to a function not in the SCC. - if (CS && CS.isConvergent() && - SCCNodes.count(CS.getCalledFunction()) == 0) - return false; + for (unsigned A = 0; A < AttrCount; A++) { + if (!ScanAttrsHere[A]) + continue; + + InvalidInstructionPredicate InvalidInstr = + std::get<2>(AttributePredicates[A]); + if (InvalidInstr(I)) { + // invalid instruction that breaks attribute assumptions + ScanAttrsHere[A] = false; + ValidAttrs[A] = false; + } + } + + if (ScanAttrsHere.count() == 0) + break; } + ReadyAttrs |= ScanAttrsHere; } - // If the SCC doesn't have any convergent functions, we have nothing to do. - if (!HasConvergentFn) return false; + // If the SCC doesn't have functions that we have successfully scanned then we + // have nothing to do + if (ReadyAttrs.count() == 0) + return false; - // If we got here, all of the calls the SCC makes to functions not in the SCC - // are non-convergent. Therefore all of the SCC's functions can also be made - // non-convergent. We'll remove the attr from the callsites in - // InstCombineCalls. - for (Function *F : SCCNodes) { - if (!F->isConvergent()) continue; + // If we got here, all of the SCC's functions adhere to the attribute + // assumptions being checked above, so we go and set all the attributes that + // are still valid. + for (Function *F : SCCNodes) + for (unsigned A = 0; A < AttrCount; A++) { + if (!ReadyAttrs[A]) + continue; - DEBUG(dbgs() << "Removing convergent attr from fn " << F->getName() - << "\n"); - F->setNotConvergent(); - } + auto &AP = AttributePredicates[A]; + SkipFunctionPredicate SkipFuncPred = std::get<1>(AP); + if (SkipFuncPred(*F)) + continue; + + SetAttributeFunc SetAttr = std::get<3>(AP); + SetAttr(*F); + } return true; } +} // end anonymous namespace + +static bool InvalidInstrNonConvergent(Instruction &I, + const SCCNodeSet &SCCNodes) { + const CallSite CS(&I); + // Breaks non-convergent assumption if CS is a convergent call to a function + // not in the SCC. + return CS && CS.isConvergent() && SCCNodes.count(CS.getCalledFunction()) == 0; +} + +static bool InvalidInstrNonThrowing(Instruction &I, + const SCCNodeSet &SCCNodes) { + bool InstMightUnwind = I.mayThrow(); + if (InstMightUnwind) { + if (const auto *CI = dyn_cast(&I)) { + if (Function *Callee = CI->getCalledFunction()) { + // If the callee is outside our current SCC then we may throw + // because it might. If it is inside, do nothing. + if (SCCNodes.count(Callee) > 0) + InstMightUnwind = false; + } + } + } + return InstMightUnwind; +} + +/// Infer attributes from all functions in the SCC by scanning every +/// instruction for compliance to the attribute assumptions. Currently it +/// does: +/// - removal of Convergent attribute +/// - addition of NoUnwind attribute +/// +/// Returns true if any changes to function attributes were made. +static bool inferAttrsFromFunctionBodies(const SCCNodeSet &SCCNodes) { + + AttributeInferer AI; + + // Request to remove the convergent attribute from all functions in the SCC + // if every callsite within the SCC is not convergent (except for calls + // to functions within the SCC). + // Note: Removal of the attr from the callsites will happen in + // InstCombineCalls separately. + AI.inferNonDerefinableAttr( + // skip non-convergent functions + [](const Function &F) { return !F.isConvergent(); }, + // instructions that break non-convergent assumption + [SCCNodes](Instruction &I) { + return InvalidInstrNonConvergent(I, SCCNodes); + }, + [](Function &F) { + DEBUG(dbgs() << "Removing convergent attr from fn " << F.getName() + << "\n"); + F.setNotConvergent(); + }); + + // Request to infer nounwind attribute for all the functions in the SCC if + // every callsite within the SCC is not throwing (except for calls to + // functions within the SCC). Note, that nounwind attribute suffers from + // derefinement (results may change depending on how functions are + // optimized). + AI.inferDerefinableAttr( + // skip non-throwing functions + [](const Function &F) { return F.doesNotThrow(); }, + // instructions that break non-throwing assumption + [SCCNodes](Instruction &I) { + return InvalidInstrNonThrowing(I, SCCNodes); + }, + [](Function &F) { + DEBUG(dbgs() << "Adding nounwind attr to fn " << F.getName() << "\n"); + F.setDoesNotThrow(); + ++NumNoUnwind; + }); + + // Perform all the requested attribute inference actions. + return AI.run(SCCNodes); +} + static bool setDoesNotRecurse(Function &F) { if (F.doesNotRecurse()) return false; @@ -1168,7 +1319,7 @@ if (!HasUnknownCall) { Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes); - Changed |= removeConvergentAttrs(SCCNodes); + Changed |= inferAttrsFromFunctionBodies(SCCNodes); Changed |= addNoRecurseAttrs(SCCNodes); } @@ -1246,7 +1397,7 @@ if (!ExternalNode) { Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes); - Changed |= removeConvergentAttrs(SCCNodes); + Changed |= inferAttrsFromFunctionBodies(SCCNodes); Changed |= addNoRecurseAttrs(SCCNodes); } Index: test/Other/cgscc-devirt-iteration.ll =================================================================== --- test/Other/cgscc-devirt-iteration.ll +++ test/Other/cgscc-devirt-iteration.ll @@ -11,13 +11,13 @@ ; We also verify that the real O2 pipeline catches these cases. ; RUN: opt -aa-pipeline=basic-aa -passes='default' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=AFTER --check-prefix=AFTER2 -declare void @readnone() readnone -; CHECK: Function Attrs: readnone -; CHECK: declare void @readnone() +declare void @readnone() readnone nounwind +; CHECK: Function Attrs: nounwind readnone +; CHECK-NEXT: declare void @readnone() declare void @unknown() ; CHECK-NOT: Function Attrs -; CHECK: declare void @unknown() +; CHECK-LABEL: declare void @unknown(){{ *$}} ; The @test1 function checks that when we refine an indirect call to a direct ; call we revisit the SCC passes to reflect the more precise information. This @@ -25,8 +25,9 @@ define void @test1() { ; BEFORE-NOT: Function Attrs -; AFTER: Function Attrs: readnone -; CHECK: define void @test1() +; FIXME: +; FIXME-AFTER: Function Attrs: readnone +; CHECK-LABEL: define void @test1() entry: %fptr = alloca void ()* store void ()* @readnone, void ()** %fptr @@ -49,7 +50,7 @@ declare void @readnone_with_arg(void ()**) readnone ; CHECK: Function Attrs: readnone -; CHECK: declare void @readnone_with_arg(void ()**) +; CHECK-LABEL: declare void @readnone_with_arg(void ()**) define void @test2_a(void ()** %ignore) { ; BEFORE-NOT: Function Attrs @@ -76,7 +77,7 @@ ; BEFORE-NOT: Function Attrs ; AFTER1: Function Attrs: readonly ; AFTER2: Function Attrs: readnone -; CHECK: define void @test2_b() +; CHECK-LABEL: define void @test2_b() entry: %f2ptr = alloca void ()* store void ()* @readnone, void ()** %f2ptr @@ -96,17 +97,20 @@ } declare i8* @memcpy(i8*, i8*, i64) -; CHECK: declare i8* @memcpy( +; CHECK-LABEL: declare i8* @memcpy( ; The @test3 function checks that when we refine an indirect call to an ; intrinsic we still revisit the SCC pass. This also covers cases where the ; value handle itself doesn't persist due to the nature of how instcombine ; 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 -; BEFORE: define void @test3(i8* %src, i8* %dest, i64 %size) -; AFTER: define void @test3(i8* nocapture readonly %src, i8* nocapture %dest, i64 %size) +; Adding 'noinline' attribute to force attributes for improved matching. +define void @test3(i8* %src, i8* %dest, i64 %size) noinline { +; CHECK: Function Attrs +; CHECK-NOT: read +; CHECK-SAME: noinline +; BEFORE-LABEL: define void @test3(i8* %src, i8* %dest, i64 %size) +; AFTER-LABEL: define void @test3(i8* nocapture readonly %src, i8* nocapture %dest, i64 %size) %fptr = alloca i8* (i8*, i8*, i64)* store i8* (i8*, i8*, i64)* @memcpy, i8* (i8*, i8*, i64)** %fptr %f = load i8* (i8*, i8*, i64)*, i8* (i8*, i8*, i64)** %fptr @@ -118,7 +122,7 @@ ; A boring function that just keeps our declarations around. define void @keep(i8** %sink) { ; CHECK-NOT: Function Attrs -; CHECK: define void @keep( +; CHECK-LABEL: define void @keep({{.*}}) { entry: store volatile i8* bitcast (void ()* @readnone to i8*), i8** %sink store volatile i8* bitcast (void ()* @unknown to i8*), i8** %sink Index: test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll =================================================================== --- test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll +++ test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll @@ -1,10 +1,17 @@ -; RUN: opt < %s -functionattrs -S | grep readnone +; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NEXT: define i32 @a define i32 @a() { %tmp = call i32 @b( ) ; [#uses=1] ret i32 %tmp } +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NEXT: define i32 @b define i32 @b() { %tmp = call i32 @a( ) ; [#uses=1] ret i32 %tmp Index: test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll =================================================================== --- test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll +++ test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll @@ -1,25 +1,32 @@ ; RUN: opt < %s -basicaa -functionattrs -S | FileCheck %s +; RUN: opt < %s -aa-pipeline=basic-aa -passes=function-attrs -S | FileCheck %s + @x = global i32 0 -; CHECK: declare i32 @e() #0 +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NEXT: declare i32 @e declare i32 @e() readnone -; CHECK: define i32 @f() #0 +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NEXT: define i32 @f define i32 @f() { %tmp = call i32 @e( ) ; [#uses=1] ret i32 %tmp } -; CHECK: define i32 @g() #1 +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NEXT: define i32 @g define i32 @g() readonly { ret i32 0 } -; CHECK: define i32 @h() #1 +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NEXT: define i32 @h define i32 @h() readnone { %tmp = load i32, i32* @x ; [#uses=1] ret i32 %tmp } - -; CHECK: attributes #0 = { readnone } -; CHECK: attributes #1 = { norecurse readnone } Index: test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll =================================================================== --- test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll +++ test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -basicaa -functionattrs -S | FileCheck %s +; RUN: opt < %s -aa-pipeline=basic-aa -passes=function-attrs -S | FileCheck %s ; CHECK: define i32 @f() #0 define i32 @f() { Index: test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll =================================================================== --- test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll +++ test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll @@ -1,4 +1,5 @@ -; RUN: opt < %s -functionattrs -S | not grep read +; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s ; PR2792 @g = global i32 0 ; [#uses=1] @@ -7,3 +8,5 @@ %t = load volatile i32, i32* @g ; [#uses=1] ret i32 %t } + +; CHECK-NOT: attributes #{{.*}} read Index: test/Transforms/FunctionAttrs/2008-12-29-Constant.ll =================================================================== --- test/Transforms/FunctionAttrs/2008-12-29-Constant.ll +++ test/Transforms/FunctionAttrs/2008-12-29-Constant.ll @@ -1,8 +1,12 @@ -; RUN: opt < %s -basicaa -functionattrs -S | grep readnone +; RUN: opt < %s -basicaa -functionattrs -S | FileCheck %s +; RUN: opt < %s -aa-pipeline=basic-aa -passes=function-attrs -S | FileCheck %s @s = external constant i8 ; [#uses=1] +; CHECK: define i8 @f() #0 define i8 @f() { %tmp = load i8, i8* @s ; [#uses=1] ret i8 %tmp } + +; CHECK: attributes #0 = { {{.*}} readnone Index: test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll =================================================================== --- test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll +++ test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s ; CHECK: define i32* @a(i32** nocapture readonly %p) define i32* @a(i32** %p) { Index: test/Transforms/FunctionAttrs/2010-10-30-volatile.ll =================================================================== --- test/Transforms/FunctionAttrs/2010-10-30-volatile.ll +++ test/Transforms/FunctionAttrs/2010-10-30-volatile.ll @@ -1,12 +1,14 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s ; PR8279 @g = constant i32 1 +; CHECK: Function Attrs +; CHECK-SAME: norecurse +; CHECK-NOT: readonly +; CHECK-NEXT: void @foo() define void @foo() { -; CHECK: void @foo() #0 { %tmp = load volatile i32, i32* @g ret void } - -; CHECK: attributes #0 = { norecurse } Index: test/Transforms/FunctionAttrs/assume.ll =================================================================== --- test/Transforms/FunctionAttrs/assume.ll +++ test/Transforms/FunctionAttrs/assume.ll @@ -1,4 +1,5 @@ ; RUN: opt -S -o - -functionattrs %s | FileCheck %s +; RUN: opt -S -o - -passes=function-attrs %s | FileCheck %s ; CHECK-NOT: readnone declare void @llvm.assume(i1) Index: test/Transforms/FunctionAttrs/atomic.ll =================================================================== --- test/Transforms/FunctionAttrs/atomic.ll +++ test/Transforms/FunctionAttrs/atomic.ll @@ -1,4 +1,5 @@ ; RUN: opt -basicaa -functionattrs -S < %s | FileCheck %s +; RUN: opt -aa-pipeline=basic-aa -passes=function-attrs -S < %s | FileCheck %s ; Atomic load/store to local doesn't affect whether a function is ; readnone/readonly. @@ -19,5 +20,5 @@ ret i32 %r } -; CHECK: attributes #0 = { norecurse readnone ssp uwtable } -; CHECK: attributes #1 = { norecurse ssp uwtable } +; CHECK: attributes #0 = { norecurse nounwind readnone ssp uwtable } +; CHECK: attributes #1 = { norecurse nounwind ssp uwtable } Index: test/Transforms/FunctionAttrs/comdat-ipo.ll =================================================================== --- test/Transforms/FunctionAttrs/comdat-ipo.ll +++ test/Transforms/FunctionAttrs/comdat-ipo.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s ; See PR26774 Index: test/Transforms/FunctionAttrs/convergent.ll =================================================================== --- test/Transforms/FunctionAttrs/convergent.ll +++ test/Transforms/FunctionAttrs/convergent.ll @@ -1,4 +1,8 @@ -; RUN: opt -functionattrs -S < %s | FileCheck %s +; FIXME: convert CHECK-INDIRECT into CHECK (and remove -check-prefixes) as soon +; FIXME: as new-pass-manager's handling of indirect_non_convergent_call is fixed +; +; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-INDIRECT +; RUN: opt -passes=function-attrs -S < %s | FileCheck %s ; CHECK: Function Attrs ; CHECK-NOT: convergent @@ -50,8 +54,8 @@ ; "Function Attrs" comment in the output. ; ; CHECK: Function Attrs -; CHECK-NOT: convergent -; CHECK-NEXT: define i32 @indirect_non_convergent_call( +; CHECK-INDIRECT-NOT: convergent +; CHECK-INDIRECT-NEXT: define i32 @indirect_non_convergent_call( define i32 @indirect_non_convergent_call(i32 ()* %f) convergent norecurse { %a = call i32 %f() ret i32 %a Index: test/Transforms/FunctionAttrs/int_sideeffect.ll =================================================================== --- test/Transforms/FunctionAttrs/int_sideeffect.ll +++ test/Transforms/FunctionAttrs/int_sideeffect.ll @@ -1,17 +1,25 @@ ; RUN: opt -S < %s -functionattrs | FileCheck %s +; RUN: opt -S < %s -passes=function-attrs | FileCheck %s +; CHECK: Function Attrs +; CHECK-SAME: inaccessiblememonly +; CHECK-NEXT: declare void @llvm.sideeffect() declare void @llvm.sideeffect() ; Don't add readnone or similar attributes when an @llvm.sideeffect() intrinsic ; is present. -; CHECK: define void @test() { +; CHECK: Function Attrs +; CHECK-NOT: readnone +; CHECK: define void @test() define void @test() { call void @llvm.sideeffect() ret void } -; CHECK: define void @loop() { +; CHECK: Function Attrs +; CHECK-NOT: readnone +; CHECK: define void @loop() define void @loop() { br label %loop Index: test/Transforms/FunctionAttrs/nocapture.ll =================================================================== --- test/Transforms/FunctionAttrs/nocapture.ll +++ test/Transforms/FunctionAttrs/nocapture.ll @@ -1,4 +1,6 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s + @g = global i32* null ; [#uses=1] ; CHECK: define i32* @c1(i32* readnone returned %q) Index: test/Transforms/FunctionAttrs/nonnull-global.ll =================================================================== --- test/Transforms/FunctionAttrs/nonnull-global.ll +++ test/Transforms/FunctionAttrs/nonnull-global.ll @@ -1,4 +1,5 @@ ; RUN: opt -S -functionattrs %s | FileCheck %s +; RUN: opt -S -passes=function-attrs %s | FileCheck %s @a = external global i8, !absolute_symbol !0 Index: test/Transforms/FunctionAttrs/nonnull.ll =================================================================== --- test/Transforms/FunctionAttrs/nonnull.ll +++ test/Transforms/FunctionAttrs/nonnull.ll @@ -1,4 +1,6 @@ ; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s +; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s + declare nonnull i8* @ret_nonnull() ; Return a pointer trivially nonnull (call return attribute) Index: test/Transforms/FunctionAttrs/norecurse.ll =================================================================== --- test/Transforms/FunctionAttrs/norecurse.ll +++ test/Transforms/FunctionAttrs/norecurse.ll @@ -1,53 +1,82 @@ ; RUN: opt < %s -basicaa -functionattrs -rpo-functionattrs -S | FileCheck %s ; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-functionattrs' -S | FileCheck %s -; CHECK: define i32 @leaf() #0 +; CHECK: Function Attrs +; CHECK-SAME: norecurse nounwind readnone +; CHECK-NEXT: define i32 @leaf() define i32 @leaf() { ret i32 1 } -; CHECK: define i32 @self_rec() #1 +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NOT: norecurse +; CHECK-NEXT: define i32 @self_rec() define i32 @self_rec() { %a = call i32 @self_rec() ret i32 4 } -; CHECK: define i32 @indirect_rec() #1 +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NOT: norecurse +; CHECK-NEXT: define i32 @indirect_rec() define i32 @indirect_rec() { %a = call i32 @indirect_rec2() ret i32 %a } -; CHECK: define i32 @indirect_rec2() #1 +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NOT: norecurse +; CHECK-NEXT: define i32 @indirect_rec2() define i32 @indirect_rec2() { %a = call i32 @indirect_rec() ret i32 %a } -; CHECK: define i32 @extern() #1 +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NOT: norecurse +; CHECK-NEXT: define i32 @extern() define i32 @extern() { %a = call i32 @k() ret i32 %a } + +; CHECK: Function Attrs +; CHECK-NEXT: declare i32 @k() declare i32 @k() readnone -; CHECK: define void @intrinsic(i8* nocapture %dest, i8* nocapture readonly %src, i32 %len) { +; CHECK: Function Attrs +; CHECK-SAME: nounwind +; CHECK-NOT: norecurse +; CHECK-NEXT: define void @intrinsic(i8* nocapture %dest, i8* nocapture readonly %src, i32 %len) define void @intrinsic(i8* %dest, i8* %src, i32 %len) { call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 false) ret void } + +; CHECK: Function Attrs +; CHECK-NEXT: declare void @llvm.memcpy.p0i8.p0i8.i32 declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1) -; CHECK: define internal i32 @called_by_norecurse() #0 +; CHECK: Function Attrs +; CHECK-SAME: norecurse readnone +; CHECK-NEXT: define internal i32 @called_by_norecurse() define internal i32 @called_by_norecurse() { %a = call i32 @k() ret i32 %a } +; CHECK: Function Attrs +; CHECK-NEXT: define void @m() define void @m() norecurse { %a = call i32 @called_by_norecurse() ret void } -; CHECK: define internal i32 @called_by_norecurse_indirectly() #0 +; CHECK: Function Attrs +; CHECK-SAME: norecurse readnone +; CHECK-NEXT: define internal i32 @called_by_norecurse_indirectly() define internal i32 @called_by_norecurse_indirectly() { %a = call i32 @k() ret i32 %a @@ -60,6 +89,3 @@ call void @o() ret void } - -; CHECK: attributes #0 = { norecurse readnone } -; CHECK: attributes #1 = { readnone } Index: test/Transforms/FunctionAttrs/operand-bundles-scc.ll =================================================================== --- test/Transforms/FunctionAttrs/operand-bundles-scc.ll +++ test/Transforms/FunctionAttrs/operand-bundles-scc.ll @@ -1,13 +1,17 @@ ; RUN: opt -S -functionattrs < %s | FileCheck %s +; RUN: opt -S -passes=function-attrs < %s | FileCheck %s define void @f() { -; CHECK-LABEL: define void @f() { +; CHECK-LABEL: define void @f() #0 { call void @g() [ "unknown"() ] ret void } define void @g() { -; CHECK-LABEL: define void @g() { +; CHECK-LABEL: define void @g() #0 { call void @f() ret void } + + +; CHECK: attributes #0 = { nounwind } Index: test/Transforms/FunctionAttrs/optnone.ll =================================================================== --- test/Transforms/FunctionAttrs/optnone.ll +++ test/Transforms/FunctionAttrs/optnone.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s @x = global i32 0 @@ -19,6 +20,6 @@ ; CHECK: (i8*) #1 ; CHECK-LABEL: attributes #0 -; CHECK: = { norecurse readnone } +; CHECK: = { norecurse nounwind readnone } ; CHECK-LABEL: attributes #1 ; CHECK: = { noinline optnone } Index: test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll =================================================================== --- test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll +++ test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll @@ -1,4 +1,5 @@ ; RUN: opt -functionattrs -S < %s | FileCheck %s +; RUN: opt -passes=function-attrs -S < %s | FileCheck %s ; This checks for an iterator wraparound bug in FunctionAttrs. The previous ; "incorrect" behavior was inferring readonly for the %x argument in @caller. Index: test/Transforms/FunctionAttrs/readnone.ll =================================================================== --- test/Transforms/FunctionAttrs/readnone.ll +++ test/Transforms/FunctionAttrs/readnone.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s ; CHECK: define void @bar(i8* nocapture readnone) define void @bar(i8* readonly) { Index: test/Transforms/FunctionAttrs/returned.ll =================================================================== --- test/Transforms/FunctionAttrs/returned.ll +++ test/Transforms/FunctionAttrs/returned.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -functionattrs -S | FileCheck %s +; RUN: opt < %s -passes=function-attrs -S | FileCheck %s ; CHECK: define i32 @test1(i32 %p, i32 %q) define i32 @test1(i32 %p, i32 %q) { Index: test/Transforms/Inline/cgscc-update.ll =================================================================== --- test/Transforms/Inline/cgscc-update.ll +++ test/Transforms/Inline/cgscc-update.ll @@ -10,9 +10,9 @@ declare void @unknown() ; Sanity check: this should get annotated as readnone. -; CHECK: Function Attrs: readnone +; CHECK: Function Attrs: nounwind readnone ; CHECK-NEXT: declare void @readnone() -declare void @readnone() readnone +declare void @readnone() readnone nounwind ; The 'test1_' prefixed functions are designed to trigger forming a new direct ; call in the inlined body of the function. After that, we form a new SCC and @@ -27,7 +27,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline readnone +; CHECK: Function Attrs: noinline nounwind readnone ; CHECK-NEXT: define void @test1_g() define void @test1_g() noinline { entry: @@ -36,7 +36,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline readnone +; CHECK: Function Attrs: noinline nounwind readnone ; CHECK-NEXT: define void @test1_h() define void @test1_h() noinline { entry: @@ -59,7 +59,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline readnone +; CHECK: Function Attrs: noinline nounwind readnone ; CHECK-NEXT: define void @test2_g() define void @test2_g() noinline { entry: @@ -69,7 +69,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline readnone +; CHECK: Function Attrs: noinline nounwind readnone ; CHECK-NEXT: define void @test2_h() define void @test2_h() noinline { entry: @@ -152,7 +152,7 @@ ; form a new SCC and should use that can deduce precise function attrs. ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline readnone +; CHECK: Function Attrs: noinline nounwind readnone ; CHECK-NEXT: define void @test4_f1() define void @test4_f1() noinline { entry: @@ -175,7 +175,7 @@ } ; This function should have had 'readnone' deduced for its SCC. -; CHECK: Function Attrs: noinline readnone +; CHECK: Function Attrs: noinline nounwind readnone ; CHECK-NEXT: define void @test4_h() define void @test4_h() noinline { entry: Index: test/Transforms/PruneEH/2008-06-02-Weak.ll =================================================================== --- test/Transforms/PruneEH/2008-06-02-Weak.ll +++ test/Transforms/PruneEH/2008-06-02-Weak.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -prune-eh -S | not grep nounwind +; RUN: opt < %s -passes='function-attrs,function(simplify-cfg)' -S | not grep nounwind define weak void @f() { entry: Index: test/Transforms/PruneEH/ipo-nounwind.ll =================================================================== --- test/Transforms/PruneEH/ipo-nounwind.ll +++ test/Transforms/PruneEH/ipo-nounwind.ll @@ -1,4 +1,5 @@ ; RUN: opt -S -prune-eh < %s | FileCheck %s +; RUN: opt -S -passes='function-attrs,function(simplify-cfg)' < %s | FileCheck %s declare void @may_throw() Index: test/Transforms/PruneEH/operand-bundles.ll =================================================================== --- test/Transforms/PruneEH/operand-bundles.ll +++ test/Transforms/PruneEH/operand-bundles.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -prune-eh -S | FileCheck %s +; RUN: opt < %s -passes='function-attrs,function(simplify-cfg)' -S | FileCheck %s declare void @nounwind() nounwind Index: test/Transforms/PruneEH/pr23971.ll =================================================================== --- test/Transforms/PruneEH/pr23971.ll +++ test/Transforms/PruneEH/pr23971.ll @@ -1,4 +1,5 @@ ; RUN: opt -S -prune-eh < %s | FileCheck %s +; RUN: opt -S -passes='function-attrs,function(simplify-cfg)' < %s | FileCheck %s target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" Index: test/Transforms/PruneEH/pr26263.ll =================================================================== --- test/Transforms/PruneEH/pr26263.ll +++ test/Transforms/PruneEH/pr26263.ll @@ -1,4 +1,9 @@ ; RUN: opt -prune-eh -S < %s | FileCheck %s +; +; TODO: Enable new-pass-manager run below as soon as all the PruneEH functionality +; TODO: is implemented through function-attrs+simplify-cfg combo. +; TODO: opt -passes='function-attrs,function(simplify-cfg)' -S < %s | FileCheck %s + target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32" target triple = "i386-pc-windows-msvc" Index: test/Transforms/PruneEH/recursivetest.ll =================================================================== --- test/Transforms/PruneEH/recursivetest.ll +++ test/Transforms/PruneEH/recursivetest.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -prune-eh -S | not grep invoke +; RUN: opt < %s -passes='function-attrs,function(simplify-cfg)' -S | not grep invoke define internal i32 @foo() personality i32 (...)* @__gxx_personality_v0 { invoke i32 @foo( ) Index: test/Transforms/PruneEH/seh-nounwind.ll =================================================================== --- test/Transforms/PruneEH/seh-nounwind.ll +++ test/Transforms/PruneEH/seh-nounwind.ll @@ -1,4 +1,5 @@ ; RUN: opt -S -prune-eh < %s | FileCheck %s +; RUN: opt -S -passes='function-attrs,function(simplify-cfg)' < %s | FileCheck %s ; Don't remove invokes of nounwind functions if the personality handles async ; exceptions. The @div function in this test can fault, even though it can't Index: test/Transforms/PruneEH/simpletest.ll =================================================================== --- test/Transforms/PruneEH/simpletest.ll +++ test/Transforms/PruneEH/simpletest.ll @@ -1,4 +1,5 @@ ; RUN: opt < %s -prune-eh -S | not grep invoke +; RUN: opt < %s -passes='function-attrs,function(simplify-cfg)' -S | not grep invoke declare void @nounwind() nounwind