Index: lib/Transforms/IPO/FunctionAttrs.cpp =================================================================== --- lib/Transforms/IPO/FunctionAttrs.cpp +++ lib/Transforms/IPO/FunctionAttrs.cpp @@ -74,6 +74,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: @@ -83,6 +84,10 @@ cl::desc("Try to propagate nonnull argument attributes from callsites to " "caller functions.")); +static cl::opt DisableNoUnwindInference( + "disable-nounwind-inference", cl::Hidden, + cl::desc("Stop inferring nounwind attribute during function-attrs pass")); + namespace { using SCCNodeSet = SmallSetVector; @@ -1037,49 +1042,216 @@ 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 { + +/// Collects a set of attribute inference requests and performs them all in one +/// go on a single SCC Node. Inference involves scanning function bodies +/// looking for instructions that violate attribute assumptions. +/// As soon as all the bodies are fine we are free to set the attribute. +/// Customization of inference for individual attributes is performed by +/// providing a handful of predicates for each attribute. +class AttributeInferer { + +public: + /// Describes a request for inference of a single attribute, + /// as well as tracks the state of inference. + struct InferenceDescriptor { + + /// Returns true if this function does not have to be handled. + /// General intent for this predicate is to provide an optimization + /// for functions that do not need this attribute inference at all + /// (say, for functions that already have the attribute). + std::function SkipFunction; + + /// Returns true if this instruction violates attribute assumptions. + std::function InstrBreaksAttribute; + + /// Sets the inferred attribute for this function. + std::function SetAttribute; + + /// Attribute we derive. + Attribute::AttrKind AKind; + + /// If true, only "exact" definitions can be used to infer this attribute. + /// (See GlobalValue::isDefinitionExact) + bool RequiresExactDefinition; + + InferenceDescriptor(Attribute::AttrKind AK, + std::function SkipFunc, + std::function InstrScan, + std::function SetAttr, + bool ReqExactDef) + : SkipFunction(SkipFunc), InstrBreaksAttribute(InstrScan), + SetAttribute(SetAttr), AKind(AK), + RequiresExactDefinition(ReqExactDef) {} + }; + +private: + SmallVector InferenceDescriptors; + +public: + void registerAttrInference(InferenceDescriptor AttrInference) { + InferenceDescriptors.push_back(AttrInference); + } + + bool run(const SCCNodeSet &SCCNodes); +}; + +/// Perform all the requested attribute inference actions according to the +/// attribute predicates stored before. +bool AttributeInferer::run(const SCCNodeSet &SCCNodes) { + + SmallVector InferInSCC = InferenceDescriptors; + // Go through all the functions in SCC and check corresponding attribute + // assumptions for each of them. for (Function *F : SCCNodes) { - if (!F->isConvergent()) continue; - HasConvergentFn = true; - // Can't remove convergent from function declarations. - if (F->isDeclaration()) return false; + // No attributes whose assumptions are still valid - done. + if (InferInSCC.empty()) + return false; - // Can't remove convergent if any of our functions has a convergent call to a - // function not in the SCC. - 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) + // Check if our attributes ever need scanning/can be scanned. + llvm::erase_if(InferInSCC, [F](const InferenceDescriptor &ID) { + // This function is explicitly skipped from inference w/o breaking the + // main per-Function loop. Perhaps it already has the attribute. + if (ID.SkipFunction(*F)) return false; + + // Remove from further inference (invalidate) when meeting a function that + // has no instructions to scan/has unsuitable definition. + return (F->isDeclaration() || + (ID.RequiresExactDefinition && !F->hasExactDefinition())); + }); + + SmallVector InferInThisFunc; + // All the attributes currently available for inference are good to go + // with this function unless explicitly skipped. + llvm::copy_if( + InferInSCC, std::back_inserter(InferInThisFunc), + [F](const InferenceDescriptor &ID) { return !ID.SkipFunction(*F); }); + + if (InferInThisFunc.empty()) + continue; + + // Start instruction scan. + for (Instruction &I : instructions(*F)) { + + llvm::erase_if(InferInThisFunc, [&](const InferenceDescriptor &ID) { + if (!ID.InstrBreaksAttribute(I)) + return false; + // Remove attribute from inference. + llvm::erase_if(InferInSCC, [ID](const InferenceDescriptor &D) { + return D.AKind == ID.AKind; + }); + // Remove attribute from current scan. + return true; + }); + + if (InferInThisFunc.empty()) + break; } } - // If the SCC doesn't have any convergent functions, we have nothing to do. - if (!HasConvergentFn) return false; + if (InferInSCC.empty()) + 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; + for (Function *F : SCCNodes) + // At this point InferInSCC contains only functions that were either: + // - explicitly skipped from scan/inference, or + // - verified to have no instructions that break attribute assumptions. + // Hence we just go and force the attribute for all non-skipped functions. + for (auto &ID : InferInSCC) { + if (ID.SkipFunction(*F)) + continue; + + ID.SetAttribute(*F); + } + return true; +} - DEBUG(dbgs() << "Removing convergent attr from fn " << F->getName() - << "\n"); - F->setNotConvergent(); +} // end anonymous namespace + +/// Helper for non-Convergent inference predicate InstrBreaksAttribute. +static bool InstrBreaksNonConvergent(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; +} + +/// Helper for NoUnwind inference predicate InstrBreaksAttribute. +static bool InstrBreaksNonThrowing(Instruction &I, const SCCNodeSet &SCCNodes) { + if (!I.mayThrow()) + return false; + if (const auto *CI = dyn_cast(&I)) { + if (Function *Callee = CI->getCalledFunction()) { + // I is a may-throw call to a function inside our SCC. This doesn't + // invalidate our current working assumption that the SCC is no-throw; we + // just have to scan that other function. + if (SCCNodes.count(Callee) > 0) + return false; + } } return true; } +/// 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.registerAttrInference(AttributeInferer::InferenceDescriptor{ + Attribute::Convergent, + // Skip non-convergent functions. + [](const Function &F) { return !F.isConvergent(); }, + // Instructions that break non-convergent assumption. + [SCCNodes](Instruction &I) { + return InstrBreaksNonConvergent(I, SCCNodes); + }, + [](Function &F) { + DEBUG(dbgs() << "Removing convergent attr from fn " << F.getName() + << "\n"); + F.setNotConvergent(); + }, + /* RequiresExactDefinition= */ false}); + + if (!DisableNoUnwindInference) + // 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. Thus it can be inferred only from exact definitions. + AI.registerAttrInference(AttributeInferer::InferenceDescriptor{ + Attribute::NoUnwind, + // Skip non-throwing functions. + [](const Function &F) { return F.doesNotThrow(); }, + // Instructions that break non-throwing assumption. + [SCCNodes](Instruction &I) { + return InstrBreaksNonThrowing(I, SCCNodes); + }, + [](Function &F) { + DEBUG(dbgs() << "Adding nounwind attr to fn " << F.getName() << "\n"); + F.setDoesNotThrow(); + ++NumNoUnwind; + }, + /* RequiresExactDefinition= */ true}); + + // Perform all the requested attribute inference actions. + return AI.run(SCCNodes); +} + static bool setDoesNotRecurse(Function &F) { if (F.doesNotRecurse()) return false; @@ -1168,7 +1340,7 @@ if (!HasUnknownCall) { Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes); - Changed |= removeConvergentAttrs(SCCNodes); + Changed |= inferAttrsFromFunctionBodies(SCCNodes); Changed |= addNoRecurseAttrs(SCCNodes); } @@ -1246,7 +1418,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 @@ -13,11 +13,11 @@ declare void @readnone() readnone ; CHECK: Function Attrs: readnone -; CHECK: declare void @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 @@ -26,7 +26,7 @@ define void @test1() { ; BEFORE-NOT: Function Attrs ; AFTER: Function Attrs: readnone -; CHECK: define void @test1() +; CHECK-LABEL: define void @test1() entry: %fptr = alloca void ()* store void ()* @readnone, void ()** %fptr @@ -49,7 +49,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 +76,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 +96,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 +121,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,12 +1,20 @@ -; RUN: opt < %s -prune-eh -S | not grep nounwind +; RUN: opt < %s -prune-eh -S | FileCheck %s +; RUN: opt < %s -passes='function-attrs,function(simplify-cfg)' -S | FileCheck %s +; We should not infer 'nounwind' for/from a weak function, +; since it can be overriden by throwing implementation. +; +; CHECK-LABEL: define weak void @f() define weak void @f() { entry: ret void } +; CHECK-LABEL: define void @g() define void @g() { entry: call void @f() ret void } + +; CHECK-NOT: {{^}}attributes #{{[0-9].*}} nounwind 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,10 @@ -; RUN: opt -prune-eh -S < %s | FileCheck %s +; PruneEH is less powerful than simplify-cfg in terms of cfg simplification, +; so it leaves some of the unreachable stuff hanging around. +; Checking it with CHECK-OLD. +; +; RUN: opt -prune-eh -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-OLD +; RUN: opt -passes='function-attrs,function(simplify-cfg)' -S < %s | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NEW + 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" @@ -20,12 +26,15 @@ } ; CHECK-LABEL: define void @test1( -; CHECK: call void @neverthrows() +; CHECK: call void @neverthrows() +; CHECK-NEW-NEXT: ret void +; CHECK-NEW-NEXT: } +; CHECK-OLD: ret void -; CHECK: %[[cp:.*]] = cleanuppad within none [] -; CHECK-NEXT: unreachable +; CHECK-OLD: %[[cp:.*]] = cleanuppad within none [] +; CHECK-OLD-NEXT: unreachable -; CHECK: cleanupret from %[[cp]] unwind to caller +; CHECK-OLD: cleanupret from %[[cp]] unwind to caller define void @test2() personality i32 (...)* @__CxxFrameHandler3 { invoke void @neverthrows() @@ -46,11 +55,16 @@ } ; CHECK-LABEL: define void @test2( -; CHECK: call void @neverthrows() +; CHECK: call void @neverthrows() +; CHECK-NEW-NEXT: ret void +; CHECK-NEW-NEXT: } +; CHECK-OLD: ret void + +; CHECK-OLD: %[[cs:.*]] = catchswitch within none [label -; CHECK: %[[cs:.*]] = catchswitch within none [label +; CHECK-OLD: catchpad within %[[cs]] [] +; CHECK-OLD-NEXT: unreachable -; CHECK: catchpad within %[[cs]] [] -; CHECK-NEXT: unreachable +; CHECK-OLD:ret void declare i32 @__CxxFrameHandler3(...) Index: test/Transforms/PruneEH/recursivetest.ll =================================================================== --- test/Transforms/PruneEH/recursivetest.ll +++ test/Transforms/PruneEH/recursivetest.ll @@ -1,6 +1,9 @@ -; RUN: opt < %s -prune-eh -S | not grep invoke +; RUN: opt < %s -prune-eh -S | FileCheck %s +; RUN: opt < %s -passes='function-attrs,function(simplify-cfg)' -S | FileCheck %s +; CHECK-LABEL: define internal i32 @foo() define internal i32 @foo() personality i32 (...)* @__gxx_personality_v0 { +; CHECK-NOT: invoke i32 @foo() invoke i32 @foo( ) to label %Normal unwind label %Except ; :1 [#uses=0] Normal: ; preds = %0 @@ -11,7 +14,9 @@ ret i32 123 } +; CHECK-LABEL: define i32 @caller() define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { +; CHECK-NOT: invoke i32 @foo() invoke i32 @foo( ) to label %Normal unwind label %Except ; :1 [#uses=0] Normal: ; preds = %0 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 -prune-eh -S | FileCheck %s +; RUN: opt < %s -passes='function-attrs,function(simplify-cfg)' -S | FileCheck %s declare void @nounwind() nounwind @@ -7,7 +8,9 @@ ret void } +; CHECK-LABEL: define i32 @caller() define i32 @caller() personality i32 (...)* @__gxx_personality_v0 { +; CHECK-NOT: invoke void @foo invoke void @foo( ) to label %Normal unwind label %Except