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:
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>;
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;
if (!HasUnknownCall) {
Changed |= addNoAliasAttrs(SCCNodes);
Changed |= addNonNullAttrs(SCCNodes);
- Changed |= removeConvergentAttrs(SCCNodes);
+ Changed |= inferAttrsFromFunctionBodies(SCCNodes);
Changed |= addNoRecurseAttrs(SCCNodes);
}
if (!ExternalNode) {
Changed |= addNoAliasAttrs(SCCNodes);
Changed |= addNonNullAttrs(SCCNodes);
- Changed |= removeConvergentAttrs(SCCNodes);
+ Changed |= inferAttrsFromFunctionBodies(SCCNodes);
Changed |= addNoRecurseAttrs(SCCNodes);
}
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
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
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
; 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
}
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
; 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
-; 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
; 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 }
; 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() {
-; 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]
%t = load volatile i32, i32* @g ; <i32> [#uses=1]
ret i32 %t
}
+
+; CHECK-NOT: attributes #{{.*}} read
-; 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
; 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) {
; 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 }
; 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)
; 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.
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 }
; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
; See PR26774
-; 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
; "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
; 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
; 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)
; RUN: opt -S -functionattrs %s | FileCheck %s
+; RUN: opt -S -passes=function-attrs %s | FileCheck %s
@a = external global i8, !absolute_symbol !0
; 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)
; 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
call void @o()
ret void
}
-
-; CHECK: attributes #0 = { norecurse readnone }
-; CHECK: attributes #1 = { readnone }
; 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 }
; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
@x = global i32 0
; CHECK: (i8*) #1
; CHECK-LABEL: attributes #0
-; CHECK: = { norecurse readnone }
+; CHECK: = { norecurse nounwind readnone }
; CHECK-LABEL: attributes #1
; CHECK: = { noinline optnone }
; 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.
; 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) {
; 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) {
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
}
; 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:
}
; 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:
}
; 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:
}
; 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:
; 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:
}
; 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:
-; 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
; RUN: opt -S -prune-eh < %s | FileCheck %s
+; RUN: opt -S -passes='function-attrs,function(simplify-cfg)' < %s | FileCheck %s
declare void @may_throw()
; RUN: opt < %s -prune-eh -S | FileCheck %s
+; RUN: opt < %s -passes='function-attrs,function(simplify-cfg)' -S | FileCheck %s
declare void @nounwind() nounwind
; 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"
-; 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"
}
; 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()
}
; 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(...)
-; 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
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
; 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
-; 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
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