1
0
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:
Hideto Ueno 2019-07-17 15:15:43 +00:00
parent 6151c4fff5
commit a5786d13db
8 changed files with 274 additions and 9 deletions

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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 }

View File

@ -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 }

View File

@ -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;

View File

@ -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")

View File

@ -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 << ",";