Index: llvm/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/include/llvm/IR/Intrinsics.td +++ llvm/include/llvm/IR/Intrinsics.td @@ -94,6 +94,8 @@ def IntrNoReturn : IntrinsicProperty; +def IntrWillReturn : IntrinsicProperty; + // IntrCold - Calls to this intrinsic are cold. // Parallels the cold attribute on LLVM IR functions. def IntrCold : IntrinsicProperty; Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -740,6 +740,50 @@ /// 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 Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -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 @@ "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 @@ break; } break; + case Attribute::WillReturn: + NumFnWillReturn++; + break; default: return; } @@ -1194,6 +1200,117 @@ 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 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(*this, *I); + if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn()) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + auto *NoRecurseAA = A.getAAFor(*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 @@ 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. Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -744,7 +744,7 @@ 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 } Index: llvm/test/Transforms/FunctionAttrs/willreturn.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/willreturn.ll +++ llvm/test/Transforms/FunctionAttrs/willreturn.ll @@ -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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 } +; 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 } Index: llvm/utils/TableGen/CodeGenIntrinsics.h =================================================================== --- llvm/utils/TableGen/CodeGenIntrinsics.h +++ llvm/utils/TableGen/CodeGenIntrinsics.h @@ -123,6 +123,9 @@ /// 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; Index: llvm/utils/TableGen/CodeGenTarget.cpp =================================================================== --- llvm/utils/TableGen/CodeGenTarget.cpp +++ llvm/utils/TableGen/CodeGenTarget.cpp @@ -557,6 +557,7 @@ isCommutative = false; canThrow = false; isNoReturn = false; + isWillReturn = false; isCold = false; isNoDuplicate = false; isConvergent = false; @@ -721,6 +722,8 @@ 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") Index: llvm/utils/TableGen/IntrinsicEmitter.cpp =================================================================== --- llvm/utils/TableGen/IntrinsicEmitter.cpp +++ llvm/utils/TableGen/IntrinsicEmitter.cpp @@ -545,6 +545,9 @@ 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 @@ if (!intrinsic.canThrow || intrinsic.ModRef != CodeGenIntrinsic::ReadWriteMem || - 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 @@ OS << "Attribute::NoReturn"; addComma = true; } + if (intrinsic.isWillReturn) { + if (addComma) + OS << ","; + OS << "Attribute::WillReturn"; + addComma = true; + } if (intrinsic.isCold) { if (addComma) OS << ",";