mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2025-01-31 12:41:49 +01:00
[Attributor] Deduce "willreturn" function attribute
Summary: Deduce the "willreturn" attribute for functions. For now, intrinsics are not willreturn. More annotation will be done in another patch. Reviewers: jdoerfert Subscribers: jvesely, nhaehnle, nicholas, hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D63046 llvm-svn: 366335
This commit is contained in:
parent
6151c4fff5
commit
a5786d13db
@ -94,6 +94,8 @@ class ReadNone<int argNo> : IntrinsicProperty {
|
||||
|
||||
def IntrNoReturn : IntrinsicProperty;
|
||||
|
||||
def IntrWillReturn : IntrinsicProperty;
|
||||
|
||||
// IntrCold - Calls to this intrinsic are cold.
|
||||
// Parallels the cold attribute on LLVM IR functions.
|
||||
def IntrCold : IntrinsicProperty;
|
||||
|
@ -740,6 +740,50 @@ struct AANonNull : public AbstractAttribute {
|
||||
/// The identifier used by the Attributor for this class of attributes.
|
||||
static constexpr Attribute::AttrKind ID = Attribute::NonNull;
|
||||
};
|
||||
|
||||
/// An abstract attribute for norecurse.
|
||||
struct AANoRecurse : public AbstractAttribute {
|
||||
|
||||
/// See AbstractAttribute::AbstractAttribute(...).
|
||||
AANoRecurse(Value &V, InformationCache &InfoCache)
|
||||
: AbstractAttribute(V, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getAttrKind()
|
||||
virtual Attribute::AttrKind getAttrKind() const override {
|
||||
return Attribute::NoRecurse;
|
||||
}
|
||||
|
||||
/// Return true if "norecurse" is known.
|
||||
virtual bool isKnownNoRecurse() const = 0;
|
||||
|
||||
/// Return true if "norecurse" is assumed.
|
||||
virtual bool isAssumedNoRecurse() const = 0;
|
||||
|
||||
/// The identifier used by the Attributor for this class of attributes.
|
||||
static constexpr Attribute::AttrKind ID = Attribute::NoRecurse;
|
||||
};
|
||||
|
||||
/// An abstract attribute for willreturn.
|
||||
struct AAWillReturn : public AbstractAttribute {
|
||||
|
||||
/// See AbstractAttribute::AbstractAttribute(...).
|
||||
AAWillReturn(Value &V, InformationCache &InfoCache)
|
||||
: AbstractAttribute(V, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getAttrKind()
|
||||
virtual Attribute::AttrKind getAttrKind() const override {
|
||||
return Attribute::WillReturn;
|
||||
}
|
||||
|
||||
/// Return true if "willreturn" is known.
|
||||
virtual bool isKnownWillReturn() const = 0;
|
||||
|
||||
/// Return true if "willreturn" is assumed.
|
||||
virtual bool isAssumedWillReturn() const = 0;
|
||||
|
||||
/// The identifier used by the Attributor for this class of attributes.
|
||||
static constexpr Attribute::AttrKind ID = Attribute::WillReturn;
|
||||
};
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "llvm/Transforms/IPO/Attributor.h"
|
||||
|
||||
#include "llvm/ADT/DepthFirstIterator.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
@ -23,6 +24,7 @@
|
||||
#include "llvm/Analysis/ValueTracking.h"
|
||||
#include "llvm/IR/Argument.h"
|
||||
#include "llvm/IR/Attributes.h"
|
||||
#include "llvm/IR/CFG.h"
|
||||
#include "llvm/IR/InstIterator.h"
|
||||
#include "llvm/IR/IntrinsicInst.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
@ -56,6 +58,7 @@ STATISTIC(NumFnReturnedNonNull,
|
||||
"Number of function return values marked nonnull");
|
||||
STATISTIC(NumFnArgumentNonNull, "Number of function arguments marked nonnull");
|
||||
STATISTIC(NumCSArgumentNonNull, "Number of call site arguments marked nonnull");
|
||||
STATISTIC(NumFnWillReturn, "Number of functions marked willreturn");
|
||||
|
||||
// TODO: Determine a good default value.
|
||||
//
|
||||
@ -128,6 +131,9 @@ static void bookkeeping(AbstractAttribute::ManifestPosition MP,
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Attribute::WillReturn:
|
||||
NumFnWillReturn++;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@ -1194,6 +1200,117 @@ ChangeStatus AANonNullCallSiteArgument::updateImpl(Attributor &A) {
|
||||
return ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
/// ------------------------ Will-Return Attributes ----------------------------
|
||||
|
||||
struct AAWillReturnImpl : public AAWillReturn, BooleanState {
|
||||
|
||||
/// See AbstractAttribute::AbstractAttribute(...).
|
||||
AAWillReturnImpl(Function &F, InformationCache &InfoCache)
|
||||
: AAWillReturn(F, InfoCache) {}
|
||||
|
||||
/// See AAWillReturn::isKnownWillReturn().
|
||||
bool isKnownWillReturn() const override { return getKnown(); }
|
||||
|
||||
/// See AAWillReturn::isAssumedWillReturn().
|
||||
bool isAssumedWillReturn() const override { return getAssumed(); }
|
||||
|
||||
/// See AbstractAttribute::getState(...).
|
||||
AbstractState &getState() override { return *this; }
|
||||
|
||||
/// See AbstractAttribute::getState(...).
|
||||
const AbstractState &getState() const override { return *this; }
|
||||
|
||||
/// See AbstractAttribute::getAsStr()
|
||||
const std::string getAsStr() const override {
|
||||
return getAssumed() ? "willreturn" : "may-noreturn";
|
||||
}
|
||||
};
|
||||
|
||||
struct AAWillReturnFunction final : AAWillReturnImpl {
|
||||
|
||||
/// See AbstractAttribute::AbstractAttribute(...).
|
||||
AAWillReturnFunction(Function &F, InformationCache &InfoCache)
|
||||
: AAWillReturnImpl(F, InfoCache) {}
|
||||
|
||||
/// See AbstractAttribute::getManifestPosition().
|
||||
ManifestPosition getManifestPosition() const override {
|
||||
return MP_FUNCTION;
|
||||
}
|
||||
|
||||
/// See AbstractAttribute::initialize(...).
|
||||
void initialize(Attributor &A) override;
|
||||
|
||||
/// See AbstractAttribute::updateImpl(...).
|
||||
ChangeStatus updateImpl(Attributor &A) override;
|
||||
};
|
||||
|
||||
// Helper function that checks whether a function has any cycle.
|
||||
// TODO: Replace with more efficent code
|
||||
bool containsCycle(Function &F) {
|
||||
SmallPtrSet<BasicBlock *, 32> Visited;
|
||||
|
||||
// Traverse BB by dfs and check whether successor is already visited.
|
||||
for (BasicBlock *BB : depth_first(&F)) {
|
||||
Visited.insert(BB);
|
||||
for (auto *SuccBB : successors(BB)) {
|
||||
if (Visited.count(SuccBB))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Helper function that checks the function have a loop which might become an
|
||||
// endless loop
|
||||
// FIXME: Any cycle is regarded as endless loop for now.
|
||||
// We have to allow some patterns.
|
||||
bool containsPossiblyEndlessLoop(Function &F) { return containsCycle(F); }
|
||||
|
||||
void AAWillReturnFunction::initialize(Attributor &A) {
|
||||
Function &F = getAnchorScope();
|
||||
|
||||
if (containsPossiblyEndlessLoop(F))
|
||||
indicatePessimisticFixpoint();
|
||||
}
|
||||
|
||||
ChangeStatus AAWillReturnFunction::updateImpl(Attributor &A) {
|
||||
Function &F = getAnchorScope();
|
||||
|
||||
// The map from instruction opcodes to those instructions in the function.
|
||||
auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
|
||||
|
||||
for (unsigned Opcode :
|
||||
{(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr,
|
||||
(unsigned)Instruction::Call}) {
|
||||
for (Instruction *I : OpcodeInstMap[Opcode]) {
|
||||
auto ICS = ImmutableCallSite(I);
|
||||
|
||||
if (ICS.hasFnAttr(Attribute::WillReturn))
|
||||
continue;
|
||||
|
||||
auto *WillReturnAA = A.getAAFor<AAWillReturn>(*this, *I);
|
||||
if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn()) {
|
||||
indicatePessimisticFixpoint();
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
|
||||
auto *NoRecurseAA = A.getAAFor<AANoRecurse>(*this, *I);
|
||||
|
||||
// FIXME: (i) Prohibit any recursion for now.
|
||||
// (ii) AANoRecurse isn't implemented yet so currently any call is
|
||||
// regarded as having recursion.
|
||||
// Code below should be
|
||||
// if ((!NoRecurseAA || !NoRecurseAA->isAssumedNoRecurse()) &&
|
||||
if (!NoRecurseAA && !ICS.hasFnAttr(Attribute::NoRecurse)) {
|
||||
indicatePessimisticFixpoint();
|
||||
return ChangeStatus::CHANGED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ChangeStatus::UNCHANGED;
|
||||
}
|
||||
|
||||
/// ----------------------------------------------------------------------------
|
||||
/// Attributor
|
||||
/// ----------------------------------------------------------------------------
|
||||
@ -1403,6 +1520,9 @@ void Attributor::identifyDefaultAbstractAttributes(
|
||||
registerAA(*new AANonNullArgument(Arg, InfoCache));
|
||||
}
|
||||
|
||||
// Every function might be "will-return".
|
||||
registerAA(*new AAWillReturnFunction(F, InfoCache));
|
||||
|
||||
// Walk all instructions to find more attribute opportunities and also
|
||||
// interesting instructions that might be queried by abstract attributes
|
||||
// during their initialization or update.
|
||||
|
@ -744,7 +744,7 @@ unreachableblock2:
|
||||
attributes #0 = { noinline nounwind uwtable }
|
||||
|
||||
; BOTH-NOT: attributes #
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline norecurse nosync nounwind readnone uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline norecurse nosync nounwind readnone uwtable willreturn }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind readnone uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind readonly uwtable }
|
||||
; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
|
||||
|
@ -1,4 +1,6 @@
|
||||
; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
|
||||
; RUN: opt -attributor --attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
|
||||
|
||||
|
||||
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
|
||||
@ -7,9 +9,10 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
|
||||
|
||||
|
||||
; TEST 1 (positive case)
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define void @only_return()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @only_return()
|
||||
define void @only_return() #0 {
|
||||
ret void
|
||||
}
|
||||
@ -22,9 +25,11 @@ define void @only_return() #0 {
|
||||
; return n<=1? n : fib(n-1) + fib(n-2);
|
||||
; }
|
||||
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define i32 @fib(i32)
|
||||
; FIXME: missing willreturn
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @fib(i32) local_unnamed_addr
|
||||
define i32 @fib(i32) local_unnamed_addr #0 {
|
||||
%2 = icmp slt i32 %0, 2
|
||||
br i1 %2, label %9, label %3
|
||||
@ -54,6 +59,9 @@ define i32 @fib(i32) local_unnamed_addr #0 {
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define i32 @fact_maybe_not_halt(i32) local_unnamed_addr
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define i32 @fact_maybe_not_halt(i32) local_unnamed_addr
|
||||
define i32 @fact_maybe_not_halt(i32) local_unnamed_addr #0 {
|
||||
%2 = icmp eq i32 %0, 0
|
||||
br i1 %2, label %11, label %3
|
||||
@ -87,6 +95,8 @@ define i32 @fact_maybe_not_halt(i32) local_unnamed_addr #0 {
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define i32 @fact_loop(i32)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @fact_loop(i32) local_unnamed_addr
|
||||
define i32 @fact_loop(i32) local_unnamed_addr #0 {
|
||||
%2 = icmp slt i32 %0, 1
|
||||
br i1 %2, label %3, label %5
|
||||
@ -116,6 +126,9 @@ define i32 @fact_loop(i32) local_unnamed_addr #0 {
|
||||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @mutual_recursion1()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @mutual_recursion1()
|
||||
define void @mutual_recursion1() #0 {
|
||||
call void @mutual_recursion2()
|
||||
ret void
|
||||
@ -125,6 +138,9 @@ define void @mutual_recursion1() #0 {
|
||||
; FNATTR: Function Attrs: noinline nounwind readnone uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @mutual_recursion2()
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @mutual_recursion2()
|
||||
define void @mutual_recursion2() #0 {
|
||||
call void @mutual_recursion1()
|
||||
ret void
|
||||
@ -135,11 +151,16 @@ define void @mutual_recursion2() #0 {
|
||||
; call exit/abort (has noreturn attribute)
|
||||
; FNATTR: Function Attrs: noreturn
|
||||
; FNATTR-NEXT: declare void @exit(i32) local_unnamed_addr
|
||||
; ATTRIBUTOR: Function Attrs: noreturn
|
||||
; ATTRIBUTOR-NEXT: declare void @exit(i32) local_unnamed_add
|
||||
declare void @exit(i32) local_unnamed_addr noreturn
|
||||
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @only_exit()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @only_exit() local_unnamed_addr
|
||||
define void @only_exit() local_unnamed_addr #0 {
|
||||
tail call void @exit(i32 0)
|
||||
unreachable
|
||||
@ -158,6 +179,9 @@ define void @only_exit() local_unnamed_addr #0 {
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @conditional_exit(i32, i32* nocapture readonly) local_unnamed_addr
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @conditional_exit(i32, i32* nocapture readonly) local_unnamed_addr
|
||||
define void @conditional_exit(i32, i32* nocapture readonly) local_unnamed_addr #0 {
|
||||
%3 = icmp eq i32 %0, 0
|
||||
br i1 %3, label %5, label %4
|
||||
@ -181,13 +205,18 @@ define void @conditional_exit(i32, i32* nocapture readonly) local_unnamed_addr #
|
||||
|
||||
; TEST 6 (positive case)
|
||||
; Call intrinsic function
|
||||
; FIXME: missing willreturn
|
||||
; FNATTRS: Function Attrs: noinline readnone speculatable
|
||||
; FNATTRS-NEXT: declare float @llvm.floor.f32(float)
|
||||
; ATTRIBUTOR: Function Attrs: nounwind readnone speculatable
|
||||
; ATTRIBUTOR-NEXT: declare float @llvm.floor.f32(float)
|
||||
declare float @llvm.floor.f32(float)
|
||||
|
||||
; FIXME: missing willreturn
|
||||
; FNATTRS: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTRS-NEXT: define void @call_floor(float %a)
|
||||
; FIXME: missing willreturn
|
||||
; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @call_floor(float %a)
|
||||
define void @call_floor(float %a) #0 {
|
||||
tail call float @llvm.floor.f32(float %a)
|
||||
ret void
|
||||
@ -200,11 +229,17 @@ define void @call_floor(float %a) #0 {
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: declare void @maybe_noreturn()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: declare void @maybe_noreturn()
|
||||
declare void @maybe_noreturn() #0
|
||||
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @call_maybe_noreturn()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @call_maybe_noreturn()
|
||||
define void @call_maybe_noreturn() #0 {
|
||||
tail call void @maybe_noreturn()
|
||||
ret void
|
||||
@ -216,11 +251,15 @@ define void @call_maybe_noreturn() #0 {
|
||||
|
||||
; FNATTR: Function Attrs: willreturn
|
||||
; FNATTR-NEXT: declare void @will_return()
|
||||
; ATTRIBUTOR: Function Attrs: willreturn
|
||||
; ATTRIBUTOR-NEXT: declare void @will_return()
|
||||
declare void @will_return() willreturn
|
||||
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define void @f1()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @f1()
|
||||
define void @f1() #0 {
|
||||
tail call void @will_return()
|
||||
ret void
|
||||
@ -229,6 +268,9 @@ define void @f1() #0 {
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define void @f2()
|
||||
; FIXME: missing willreturn
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @f2()
|
||||
define void @f2() #0 {
|
||||
tail call void @f1()
|
||||
ret void
|
||||
@ -241,6 +283,9 @@ define void @f2() #0 {
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @call_will_return_but_has_loop()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @call_will_return_but_has_loop()
|
||||
define void @call_will_return_but_has_loop() #0 {
|
||||
br label %label1
|
||||
label1:
|
||||
@ -256,14 +301,17 @@ label2:
|
||||
|
||||
; FNATTR: Function Attrs: noinline uwtable willreturn
|
||||
; FNATTR-NEXT: declare i1 @maybe_raise_exception()
|
||||
; ATTRIBUTOR: Function Attrs: noinline uwtable willreturn
|
||||
; ATTRIBUTOR-NEXT: declare i1 @maybe_raise_exception()
|
||||
declare i1 @maybe_raise_exception() #1 willreturn
|
||||
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: nounwind
|
||||
; FNATTR-NEXT: define void @invoke_test()
|
||||
; ATTRIBUTOR: Function Attrs: nounwind willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @invoke_test()
|
||||
define void @invoke_test() personality i32 (...)* @__gxx_personality_v0 {
|
||||
invoke i1 @maybe_raise_exception()
|
||||
to label %N unwind label %F
|
||||
to label %N unwind label %F
|
||||
N:
|
||||
ret void
|
||||
F:
|
||||
@ -288,6 +336,8 @@ declare i32 @__gxx_personality_v0(...)
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
|
||||
; FNATTR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture readonly)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture readonly)
|
||||
define i32 @loop_constant_trip_count(i32* nocapture readonly) #0 {
|
||||
br label %3
|
||||
|
||||
@ -319,6 +369,9 @@ define i32 @loop_constant_trip_count(i32* nocapture readonly) #0 {
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define i32 @loop_trip_count_unbound(i32, i32, i32* nocapture readonly, i32) local_unnamed_addr
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define i32 @loop_trip_count_unbound(i32, i32, i32* nocapture readonly, i32) local_unnamed_addr
|
||||
define i32 @loop_trip_count_unbound(i32, i32, i32* nocapture readonly, i32) local_unnamed_addr #0 {
|
||||
%5 = icmp eq i32 %0, %1
|
||||
br i1 %5, label %6, label %8
|
||||
@ -354,6 +407,8 @@ define i32 @loop_trip_count_unbound(i32, i32, i32* nocapture readonly, i32) loca
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readonly uwtable
|
||||
; FNATTR-NEXT: define i32 @loop_trip_dec(i32, i32* nocapture readonly)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @loop_trip_dec(i32, i32* nocapture readonly) local_unnamed_addr
|
||||
|
||||
define i32 @loop_trip_dec(i32, i32* nocapture readonly) local_unnamed_addr #0 {
|
||||
%3 = icmp sgt i32 %0, -1
|
||||
@ -381,9 +436,10 @@ define i32 @loop_trip_dec(i32, i32* nocapture readonly) local_unnamed_addr #0 {
|
||||
; TEST 14 (positive case)
|
||||
; multiple return
|
||||
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable
|
||||
; FNATTR-NEXT: define i32 @multiple_return(i32 %a)
|
||||
; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable willreturn
|
||||
; ATTRIBUTOR-NEXT: define i32 @multiple_return(i32 %a)
|
||||
define i32 @multiple_return(i32 %a) #0 {
|
||||
%b = icmp eq i32 %a, 0
|
||||
br i1 %b, label %t, label %f
|
||||
@ -401,6 +457,8 @@ f:
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define void @unreachable_exit_positive1()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define void @unreachable_exit_positive1()
|
||||
define void @unreachable_exit_positive1() #0 {
|
||||
tail call void @will_return()
|
||||
ret void
|
||||
@ -413,6 +471,8 @@ unreachable_label:
|
||||
; FIXME: missing willreturn
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NEXT: define i32 @unreachable_exit_positive2(i32)
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NEXT: define i32 @unreachable_exit_positive2(i32)
|
||||
define i32 @unreachable_exit_positive2(i32) local_unnamed_addr #0 {
|
||||
%2 = icmp slt i32 %0, 1
|
||||
br i1 %2, label %3, label %5
|
||||
@ -440,6 +500,9 @@ unreachable_label:
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @unreachable_exit_negative1()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative1()
|
||||
define void @unreachable_exit_negative1() #0 {
|
||||
tail call void @exit(i32 0)
|
||||
ret void
|
||||
@ -452,6 +515,9 @@ unreachable_label:
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @unreachable_exit_negative2()
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative2()
|
||||
define void @unreachable_exit_negative2() #0 {
|
||||
|
||||
br label %L1
|
||||
@ -465,6 +531,23 @@ unreachable_label:
|
||||
unreachable
|
||||
}
|
||||
|
||||
; FNATTR: Function Attrs: noreturn nounwind
|
||||
; FNATTR-NEXT: declare void @llvm.eh.sjlj.longjmp(i8*)
|
||||
; ATTRIBUTOR: Function Attrs: noreturn nounwind
|
||||
; ATTRIBUTOR-NEXT: declare void @llvm.eh.sjlj.longjmp(i8*)
|
||||
declare void @llvm.eh.sjlj.longjmp(i8*)
|
||||
|
||||
; FNATTR: Function Attrs: noinline nounwind uwtable
|
||||
; FNATTR-NOT: willreturn
|
||||
; FNATTR-NEXT: define void @call_longjmp(i8* nocapture readnone) local_unnamed_addr #3 {
|
||||
; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
|
||||
; ATTRIBUTOR-NOT: willreturn
|
||||
; ATTRIBUTOR-NEXT: define void @call_longjmp(i8* nocapture readnone) local_unnamed_addr
|
||||
define void @call_longjmp(i8* nocapture readnone) local_unnamed_addr #0 {
|
||||
tail call void @llvm.eh.sjlj.longjmp(i8* %0)
|
||||
ret void
|
||||
}
|
||||
|
||||
|
||||
attributes #0 = { nounwind uwtable noinline }
|
||||
attributes #1 = { uwtable noinline }
|
||||
|
@ -123,6 +123,9 @@ struct CodeGenIntrinsic {
|
||||
/// True if the intrinsic is no-return.
|
||||
bool isNoReturn;
|
||||
|
||||
/// True if the intrinsic is will-return.
|
||||
bool isWillReturn;
|
||||
|
||||
/// True if the intrinsic is cold.
|
||||
bool isCold;
|
||||
|
||||
|
@ -557,6 +557,7 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
|
||||
isCommutative = false;
|
||||
canThrow = false;
|
||||
isNoReturn = false;
|
||||
isWillReturn = false;
|
||||
isCold = false;
|
||||
isNoDuplicate = false;
|
||||
isConvergent = false;
|
||||
@ -721,6 +722,8 @@ CodeGenIntrinsic::CodeGenIntrinsic(Record *R) {
|
||||
isConvergent = true;
|
||||
else if (Property->getName() == "IntrNoReturn")
|
||||
isNoReturn = true;
|
||||
else if (Property->getName() == "IntrWillReturn")
|
||||
isWillReturn = true;
|
||||
else if (Property->getName() == "IntrCold")
|
||||
isCold = true;
|
||||
else if (Property->getName() == "IntrSpeculatable")
|
||||
|
@ -545,6 +545,9 @@ struct AttributeComparator {
|
||||
if (L->isNoReturn != R->isNoReturn)
|
||||
return R->isNoReturn;
|
||||
|
||||
if (L->isWillReturn != R->isWillReturn)
|
||||
return R->isWillReturn;
|
||||
|
||||
if (L->isCold != R->isCold)
|
||||
return R->isCold;
|
||||
|
||||
@ -686,8 +689,9 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints,
|
||||
|
||||
if (!intrinsic.canThrow ||
|
||||
(intrinsic.ModRef != CodeGenIntrinsic::ReadWriteMem && !intrinsic.hasSideEffects) ||
|
||||
intrinsic.isNoReturn || intrinsic.isCold || intrinsic.isNoDuplicate ||
|
||||
intrinsic.isConvergent || intrinsic.isSpeculatable) {
|
||||
intrinsic.isNoReturn || intrinsic.isWillReturn || intrinsic.isCold ||
|
||||
intrinsic.isNoDuplicate || intrinsic.isConvergent ||
|
||||
intrinsic.isSpeculatable) {
|
||||
OS << " const Attribute::AttrKind Atts[] = {";
|
||||
bool addComma = false;
|
||||
if (!intrinsic.canThrow) {
|
||||
@ -700,6 +704,12 @@ void IntrinsicEmitter::EmitAttributes(const CodeGenIntrinsicTable &Ints,
|
||||
OS << "Attribute::NoReturn";
|
||||
addComma = true;
|
||||
}
|
||||
if (intrinsic.isWillReturn) {
|
||||
if (addComma)
|
||||
OS << ",";
|
||||
OS << "Attribute::WillReturn";
|
||||
addComma = true;
|
||||
}
|
||||
if (intrinsic.isCold) {
|
||||
if (addComma)
|
||||
OS << ",";
|
||||
|
Loading…
x
Reference in New Issue
Block a user