diff options
| author | Fedor Sergeev <fedor.sergeev@azul.com> | 2018-03-23 21:46:16 +0000 |
|---|---|---|
| committer | Fedor Sergeev <fedor.sergeev@azul.com> | 2018-03-23 21:46:16 +0000 |
| commit | 6660fd0f959e2e07db47729fb6112b7a823a67c2 (patch) | |
| tree | 169742b681fa951ba09dbd061f387cb6d8f7c10e | |
| parent | b53180846b7cd2489b1f99271010214de0dc4533 (diff) | |
| download | bcm5719-llvm-6660fd0f959e2e07db47729fb6112b7a823a67c2.tar.gz bcm5719-llvm-6660fd0f959e2e07db47729fb6112b7a823a67c2.zip | |
[PM][FunctionAttrs] add NoUnwind attribute inference to PostOrderFunctionAttrs pass
Summary:
This was motivated by absence of PrunEH functionality in new PM.
It was decided that a proper way to do PruneEH is to add NoUnwind inference
into PostOrderFunctionAttrs and then perform normal SimplifyCFG on top.
This change generalizes attribute handling implemented for (a removal of)
Convergent attribute, by introducing a generic builder-like class
AttributeInferer
It registers all the attribute inference requests, storing per-attribute
predicates into a vector, and then goes through an SCC Node, scanning all
the instructions for not breaking attribute assumptions.
The main idea is that as soon all the instructions from all the functions
of SCC Node conform to attribute assumptions then we are free to infer
the attribute as set for all the functions of SCC Node.
It handles two distinct cases of attributes:
- those that might break due to derefinement of the function code
for these attributes we are allowed to apply inference only if all the
functions are "exact definitions". Example - NoUnwind.
- those that do not care about derefinement
for these attributes we are allowed to apply inference as soon as we see
any function definition. Example - removal of Convergent attribute.
Also in this commit:
* Converted all the FunctionAttrs tests to use FileCheck and added new-PM
invocations to them
* FunctionAttrs/convergent.ll test demonstrates a difference in behavior between
new and old PM implementations. Marked with FIXME.
* PruneEH tests were converted to new-PM as well, using function-attrs+simplify-cfg
combo as intended
* some of "other" tests were updated since function-attrs now infers 'nounwind'
even for old PM pipeline
* -disable-nounwind-inference hidden option added as a possible workaround for a supposedly
rare case when nounwind being inferred by default presents a problem
Reviewers: chandlerc, jlebar
Reviewed By: jlebar
Subscribers: eraman, llvm-commits
Differential Revision: https://reviews.llvm.org/D44415
llvm-svn: 328377
32 files changed, 382 insertions, 97 deletions
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 353daece48e..eef0738197b 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -74,6 +74,7 @@ STATISTIC(NumReadOnlyArg, "Number of arguments marked readonly"); 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 @@ static cl::opt<bool> EnableNonnullArgPropagation( cl::desc("Try to propagate nonnull argument attributes from callsites to " "caller functions.")); +static cl::opt<bool> DisableNoUnwindInference( + "disable-nounwind-inference", cl::Hidden, + cl::desc("Stop inferring nounwind attribute during function-attrs pass")); + namespace { using SCCNodeSet = SmallSetVector<Function *, 8>; @@ -1037,49 +1042,213 @@ static bool addNonNullAttrs(const SCCNodeSet &SCCNodes) { 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. + 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<bool(const Function &)> SkipFunction; + + /// Returns true if this instruction violates attribute assumptions. + std::function<bool(Instruction &)> InstrBreaksAttribute; + + /// Sets the inferred attribute for this function. + std::function<void(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<bool(const Function &)> SkipFunc, + std::function<bool(Instruction &)> InstrScan, + std::function<void(Function &)> SetAttr, + bool ReqExactDef) + : SkipFunction(SkipFunc), InstrBreaksAttribute(InstrScan), + SetAttribute(SetAttr), AKind(AK), + RequiresExactDefinition(ReqExactDef) {} + }; + +private: + SmallVector<InferenceDescriptor, 4> 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<InferenceDescriptor, 4> InferInSCC = InferenceDescriptors; + // Go through all the functions in SCC and check corresponding attribute + // assumptions for each of them. Attributes that are invalid for this SCC + // will be removed from InferInSCC. 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) { + if (ID.SkipFunction(*F)) return false; + + // Remove from further inference (invalidate) when visiting a function + // that has no instructions to scan/has an unsuitable definition. + return F->isDeclaration() || + (ID.RequiresExactDefinition && !F->hasExactDefinition()); + }); + + // For each attribute still in InferInSCC that doesn't explicitly skip F, + // set up the F instructions scan to verify assumptions of the attribute. + SmallVector<InferenceDescriptor, 4> InferInThisFunc; + 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 further inference on any other functions + // because attribute assumptions have just been violated. + llvm::erase_if(InferInSCC, [&ID](const InferenceDescriptor &D) { + return D.AKind == ID.AKind; + }); + // Remove attribute from the rest of current instruction 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; + bool Changed = false; + 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; + Changed = true; + ID.SetAttribute(*F); + } + return Changed; +} - 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<CallInst>(&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 +1337,7 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, if (!HasUnknownCall) { Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes); - Changed |= removeConvergentAttrs(SCCNodes); + Changed |= inferAttrsFromFunctionBodies(SCCNodes); Changed |= addNoRecurseAttrs(SCCNodes); } @@ -1246,7 +1415,7 @@ static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) { if (!ExternalNode) { Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes); - Changed |= removeConvergentAttrs(SCCNodes); + Changed |= inferAttrsFromFunctionBodies(SCCNodes); Changed |= addNoRecurseAttrs(SCCNodes); } diff --git a/llvm/test/Other/cgscc-devirt-iteration.ll b/llvm/test/Other/cgscc-devirt-iteration.ll index 111dac5bcca..204754e8f92 100644 --- a/llvm/test/Other/cgscc-devirt-iteration.ll +++ b/llvm/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 @@ declare void @unknown() 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 @@ entry: 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 @@ define void @test2_b() { ; 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 @@ entry: } 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 @@ define void @test3(i8* %src, i8* %dest, i64 %size) { ; 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 diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll index b0aecfa56f7..6bbd99951ad 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll +++ b/llvm/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( ) ; <i32> [#uses=1] ret i32 %tmp } +; CHECK: Function Attrs +; CHECK-SAME: readnone +; CHECK-NEXT: define i32 @b define i32 @b() { %tmp = call i32 @a( ) ; <i32> [#uses=1] ret i32 %tmp diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll index b62698a776f..d747fe727fa 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll +++ b/llvm/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( ) ; <i32> [#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 ; <i32> [#uses=1] ret i32 %tmp } - -; CHECK: attributes #0 = { readnone } -; CHECK: attributes #1 = { norecurse readnone } diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll index d8256ae8e64..35cb5342d8d 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll +++ b/llvm/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() { diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll index fef872c794a..8212e8945ec 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll +++ b/llvm/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 ; <i32*> [#uses=1] @@ -7,3 +8,5 @@ define i32 @f() { %t = load volatile i32, i32* @g ; <i32> [#uses=1] ret i32 %t } + +; CHECK-NOT: attributes #{{.*}} read diff --git a/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll b/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll index e3a8f0161b8..ee1a8caa20e 100644 --- a/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll +++ b/llvm/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 ; <i8*> [#uses=1] +; CHECK: define i8 @f() #0 define i8 @f() { %tmp = load i8, i8* @s ; <i8> [#uses=1] ret i8 %tmp } + +; CHECK: attributes #0 = { {{.*}} readnone diff --git a/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll b/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll index 0d0231b42ae..ce72c416563 100644 --- a/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll +++ b/llvm/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) { diff --git a/llvm/test/Transforms/FunctionAttrs/2010-10-30-volatile.ll b/llvm/test/Transforms/FunctionAttrs/2010-10-30-volatile.ll index 23bb18e92b4..b9536dce8a4 100644 --- a/llvm/test/Transforms/FunctionAttrs/2010-10-30-volatile.ll +++ b/llvm/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 } diff --git a/llvm/test/Transforms/FunctionAttrs/assume.ll b/llvm/test/Transforms/FunctionAttrs/assume.ll index 58200622eab..d6296624a2d 100644 --- a/llvm/test/Transforms/FunctionAttrs/assume.ll +++ b/llvm/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) diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index dd915a6027f..af87a28770e 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/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 @@ entry: 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 } diff --git a/llvm/test/Transforms/FunctionAttrs/comdat-ipo.ll b/llvm/test/Transforms/FunctionAttrs/comdat-ipo.ll index e82d2fb9215..2a149e436b6 100644 --- a/llvm/test/Transforms/FunctionAttrs/comdat-ipo.ll +++ b/llvm/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 diff --git a/llvm/test/Transforms/FunctionAttrs/convergent.ll b/llvm/test/Transforms/FunctionAttrs/convergent.ll index 37886b82b87..0e4b7515d01 100644 --- a/llvm/test/Transforms/FunctionAttrs/convergent.ll +++ b/llvm/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 @@ define i32 @indirect_convergent_call(i32 ()* %f) convergent { ; "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 diff --git a/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll index 13091e3e8cb..24a145908e0 100644 --- a/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll +++ b/llvm/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 diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll index e137a3800ce..931072e331e 100644 --- a/llvm/test/Transforms/FunctionAttrs/nocapture.ll +++ b/llvm/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 ; <i32**> [#uses=1] ; CHECK: define i32* @c1(i32* readnone returned %q) diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull-global.ll b/llvm/test/Transforms/FunctionAttrs/nonnull-global.ll index 43353e82270..d79a7ae290a 100644 --- a/llvm/test/Transforms/FunctionAttrs/nonnull-global.ll +++ b/llvm/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 diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll index 191e6781900..7de65d8a0fd 100644 --- a/llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ b/llvm/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) diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll index cd9044d0720..0293938e479 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/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 @@ define void @p() norecurse { call void @o() ret void } - -; CHECK: attributes #0 = { norecurse readnone } -; CHECK: attributes #1 = { readnone } diff --git a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll index 2502ea719ca..69808a83c6d 100644 --- a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll +++ b/llvm/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 } diff --git a/llvm/test/Transforms/FunctionAttrs/optnone.ll b/llvm/test/Transforms/FunctionAttrs/optnone.ll index cd08c75d75b..586a6d4a081 100644 --- a/llvm/test/Transforms/FunctionAttrs/optnone.ll +++ b/llvm/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 @@ declare i8 @strlen(i8*) noinline optnone ; CHECK: (i8*) #1 ; CHECK-LABEL: attributes #0 -; CHECK: = { norecurse readnone } +; CHECK: = { norecurse nounwind readnone } ; CHECK-LABEL: attributes #1 ; CHECK: = { noinline optnone } diff --git a/llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll b/llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll index db9a895f97e..f2294fe22ef 100644 --- a/llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll +++ b/llvm/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. diff --git a/llvm/test/Transforms/FunctionAttrs/readnone.ll b/llvm/test/Transforms/FunctionAttrs/readnone.ll index eddcdd2641d..b5a5b30ae21 100644 --- a/llvm/test/Transforms/FunctionAttrs/readnone.ll +++ b/llvm/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) { diff --git a/llvm/test/Transforms/FunctionAttrs/returned.ll b/llvm/test/Transforms/FunctionAttrs/returned.ll index ede9481e433..04ddb7b5ac0 100644 --- a/llvm/test/Transforms/FunctionAttrs/returned.ll +++ b/llvm/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) { diff --git a/llvm/test/Transforms/Inline/cgscc-update.ll b/llvm/test/Transforms/Inline/cgscc-update.ll index 77666c3bb71..b251a5d070c 100644 --- a/llvm/test/Transforms/Inline/cgscc-update.ll +++ b/llvm/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 @@ entry: } ; 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 @@ entry: } ; 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 @@ entry: } ; 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 @@ entry: } ; 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 @@ exit: ; 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 @@ entry: } ; 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: diff --git a/llvm/test/Transforms/PruneEH/2008-06-02-Weak.ll b/llvm/test/Transforms/PruneEH/2008-06-02-Weak.ll index fb97ae87083..6743606b150 100644 --- a/llvm/test/Transforms/PruneEH/2008-06-02-Weak.ll +++ b/llvm/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 diff --git a/llvm/test/Transforms/PruneEH/ipo-nounwind.ll b/llvm/test/Transforms/PruneEH/ipo-nounwind.ll index ba3fab1c0dd..251bc08f3b0 100644 --- a/llvm/test/Transforms/PruneEH/ipo-nounwind.ll +++ b/llvm/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() diff --git a/llvm/test/Transforms/PruneEH/operand-bundles.ll b/llvm/test/Transforms/PruneEH/operand-bundles.ll index efe8f62a8fb..112f4712238 100644 --- a/llvm/test/Transforms/PruneEH/operand-bundles.ll +++ b/llvm/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 diff --git a/llvm/test/Transforms/PruneEH/pr23971.ll b/llvm/test/Transforms/PruneEH/pr23971.ll index 8a8a591fff0..e23e8cb5125 100644 --- a/llvm/test/Transforms/PruneEH/pr23971.ll +++ b/llvm/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" diff --git a/llvm/test/Transforms/PruneEH/pr26263.ll b/llvm/test/Transforms/PruneEH/pr26263.ll index 17fafeb6850..d1232abbc23 100644 --- a/llvm/test/Transforms/PruneEH/pr26263.ll +++ b/llvm/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 @@ cleanupret: } ; 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 @@ ret: } ; 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(...) diff --git a/llvm/test/Transforms/PruneEH/recursivetest.ll b/llvm/test/Transforms/PruneEH/recursivetest.ll index 0b2399a9a29..755f2518854 100644 --- a/llvm/test/Transforms/PruneEH/recursivetest.ll +++ b/llvm/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 ; <i32>:1 [#uses=0] Normal: ; preds = %0 @@ -11,7 +14,9 @@ Except: ; preds = %0 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 ; <i32>:1 [#uses=0] Normal: ; preds = %0 diff --git a/llvm/test/Transforms/PruneEH/seh-nounwind.ll b/llvm/test/Transforms/PruneEH/seh-nounwind.ll index 043a792eb6d..7bc8f8068dc 100644 --- a/llvm/test/Transforms/PruneEH/seh-nounwind.ll +++ b/llvm/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 diff --git a/llvm/test/Transforms/PruneEH/simpletest.ll b/llvm/test/Transforms/PruneEH/simpletest.ll index cbc5592cfb2..720a85a3712 100644 --- a/llvm/test/Transforms/PruneEH/simpletest.ll +++ b/llvm/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 @@ define internal void @foo() { 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 |

