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; @@ -490,23 +492,23 @@ def int_memcpy : Intrinsic<[], [llvm_anyptr_ty, llvm_anyptr_ty, llvm_anyint_ty, llvm_i1_ty], - [IntrArgMemOnly, NoCapture<0>, NoCapture<1>, + [IntrArgMemOnly, IntrWillReturn, NoCapture<0>, NoCapture<1>, WriteOnly<0>, ReadOnly<1>, ImmArg<3>]>; def int_memmove : Intrinsic<[], [llvm_anyptr_ty, llvm_anyptr_ty, llvm_anyint_ty, llvm_i1_ty], - [IntrArgMemOnly, NoCapture<0>, NoCapture<1>, + [IntrArgMemOnly, IntrWillReturn, NoCapture<0>, NoCapture<1>, ReadOnly<1>, ImmArg<3>]>; def int_memset : Intrinsic<[], [llvm_anyptr_ty, llvm_i8_ty, llvm_anyint_ty, llvm_i1_ty], - [IntrArgMemOnly, NoCapture<0>, WriteOnly<0>, + [IntrArgMemOnly, IntrWillReturn, NoCapture<0>, WriteOnly<0>, ImmArg<3>]>; // FIXME: Add version of these floating point intrinsics which allow non-default // rounding modes and FP exception handling. -let IntrProperties = [IntrNoMem, IntrSpeculatable] in { +let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrWillReturn] in { def int_fma : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>; @@ -548,38 +550,38 @@ def int_minnum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable, Commutative] + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative] >; def int_maxnum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable, Commutative] + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative] >; def int_minimum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable, Commutative] + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative] >; def int_maximum : Intrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable, Commutative] + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative] >; // NOTE: these are internal interfaces. -def int_setjmp : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty]>; +def int_setjmp : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty], [IntrWillReturn]>; def int_longjmp : Intrinsic<[], [llvm_ptr_ty, llvm_i32_ty], [IntrNoReturn]>; -def int_sigsetjmp : Intrinsic<[llvm_i32_ty] , [llvm_ptr_ty, llvm_i32_ty]>; +def int_sigsetjmp : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_i32_ty], [IntrWillReturn]>; def int_siglongjmp : Intrinsic<[], [llvm_ptr_ty, llvm_i32_ty], [IntrNoReturn]>; // Internal interface for object size checking def int_objectsize : Intrinsic<[llvm_anyint_ty], [llvm_anyptr_ty, llvm_i1_ty, llvm_i1_ty, llvm_i1_ty], - [IntrNoMem, IntrSpeculatable, ImmArg<1>, ImmArg<2>, ImmArg<3>]>, + [IntrNoMem, IntrSpeculatable, IntrWillReturn, ImmArg<1>, ImmArg<2>, ImmArg<3>]>, GCCBuiltin<"__builtin_object_size">; //===--------------- Constrained Floating Point Intrinsics ----------------===// // -let IntrProperties = [IntrInaccessibleMemOnly] in { +let IntrProperties = [IntrInaccessibleMemOnly, IntrWillReturn] in { def int_experimental_constrained_fadd : Intrinsic<[ llvm_anyfloat_ty ], [ LLVMMatchType<0>, LLVMMatchType<0>, @@ -708,13 +710,13 @@ //===------------------------- Expect Intrinsics --------------------------===// // def int_expect : Intrinsic<[llvm_anyint_ty], - [LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>; + [LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem, IntrWillReturn]>; //===-------------------- Bit Manipulation Intrinsics ---------------------===// // // None of these intrinsics accesses memory at all. -let IntrProperties = [IntrNoMem, IntrSpeculatable] in { +let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrWillReturn] in { def int_bswap: Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>]>; def int_ctpop: Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>]>; def int_bitreverse : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>]>; @@ -724,7 +726,7 @@ [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>; } -let IntrProperties = [IntrNoMem, IntrSpeculatable, ImmArg<1>] in { +let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrWillReturn, ImmArg<1>] in { def int_ctlz : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, llvm_i1_ty]>; def int_cttz : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, llvm_i1_ty]>; } @@ -736,7 +738,7 @@ // mean the optimizers can change them aggressively. Special handling // needed in a few places. These synthetic intrinsics have no // side-effects and just mark information about their operands. -let IntrProperties = [IntrNoMem, IntrSpeculatable] in { +let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrWillReturn] in { def int_dbg_declare : Intrinsic<[], [llvm_metadata_ty, llvm_metadata_ty, @@ -825,63 +827,59 @@ // // Expose the carry flag from add operations on two integrals. -def int_sadd_with_overflow : Intrinsic<[llvm_anyint_ty, - LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], - [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable]>; -def int_uadd_with_overflow : Intrinsic<[llvm_anyint_ty, - LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], - [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable]>; - -def int_ssub_with_overflow : Intrinsic<[llvm_anyint_ty, - LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], - [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable]>; -def int_usub_with_overflow : Intrinsic<[llvm_anyint_ty, - LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], - [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable]>; - -def int_smul_with_overflow : Intrinsic<[llvm_anyint_ty, - LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], - [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable]>; -def int_umul_with_overflow : Intrinsic<[llvm_anyint_ty, - LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], - [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable]>; +let IntrProperties = [IntrNoMem, IntrSpeculatable, IntrWillReturn] in { + def int_sadd_with_overflow : Intrinsic<[llvm_anyint_ty, + LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [LLVMMatchType<0>, LLVMMatchType<0>]>; + def int_uadd_with_overflow : Intrinsic<[llvm_anyint_ty, + LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [LLVMMatchType<0>, LLVMMatchType<0>]>; + + def int_ssub_with_overflow : Intrinsic<[llvm_anyint_ty, + LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [LLVMMatchType<0>, LLVMMatchType<0>]>; + def int_usub_with_overflow : Intrinsic<[llvm_anyint_ty, + LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [LLVMMatchType<0>, LLVMMatchType<0>]>; + + def int_smul_with_overflow : Intrinsic<[llvm_anyint_ty, + LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [LLVMMatchType<0>, LLVMMatchType<0>]>; + def int_umul_with_overflow : Intrinsic<[llvm_anyint_ty, + LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>], + [LLVMMatchType<0>, LLVMMatchType<0>]>; +} //===------------------------- Saturation Arithmetic Intrinsics ---------------------===// // def int_sadd_sat : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable, Commutative]>; + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]>; def int_uadd_sat : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable, Commutative]>; + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative]>; def int_ssub_sat : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable]>; + [IntrNoMem, IntrSpeculatable, IntrWillReturn]>; def int_usub_sat : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>], - [IntrNoMem, IntrSpeculatable]>; + [IntrNoMem, IntrSpeculatable, IntrWillReturn]>; //===------------------------- Fixed Point Arithmetic Intrinsics ---------------------===// // def int_smul_fix : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty], - [IntrNoMem, IntrSpeculatable, Commutative, ImmArg<2>]>; + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative, ImmArg<2>]>; def int_umul_fix : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty], - [IntrNoMem, IntrSpeculatable, Commutative, ImmArg<2>]>; + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative, ImmArg<2>]>; //===------------------- Fixed Point Saturation Arithmetic Intrinsics ----------------===// // def int_smul_fix_sat : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty], - [IntrNoMem, IntrSpeculatable, Commutative, ImmArg<2>]>; + [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative, ImmArg<2>]>; //===------------------------- Memory Use Markers -------------------------===// // Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -680,6 +680,28 @@ virtual bool isKnownNoUnwind() const = 0; }; +/// An abstract attribute for willreturn. +struct AAWillReturn : public AbstractAttribute { + + /// See AbstractAttribute::AbstractAttribute(...). + AAWillReturn(Function &F, InformationCache &InfoCache) + : AbstractAttribute(F, InfoCache) {} + + /// See AbstractAttribute::getAttrKind() + virtual Attribute::AttrKind getAttrKind() const override { + return Attribute::WillReturn; + } + + /// Return true if "willreturn" is known. + bool isKnownWillReturn() const; + + /// Return true if "willreturn" is assumed. + bool isAssumedWillReturn() const; + + /// 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" @@ -22,7 +23,9 @@ #include "llvm/Analysis/GlobalsModRef.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" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" @@ -825,6 +828,115 @@ return Changed; } +/// ------------------------ 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 { return getKnown(); } + + /// See AAWillReturn::isAssumedWillReturn(). + bool isAssumedWillReturn() const { return getAssumed(); } + + /// See AbstractAttribute::getState(...). + virtual AbstractState &getState() override { return *this; } + + /// See AbstractAttribute::getState(...). + virtual const AbstractState &getState() const override { return *this; } + + /// See AbstractAttribute::getAsStr() + virtual 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(). + virtual ManifestPosition getManifestPosition() const override { + return MP_FUNCTION; + } + + /// See AbstractAttribute::initialize(...). + virtual void initialize(Attributor &A) override; + + /// See AbstractAttribute::updateImpl(...). + virtual 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->isValidState() || + !WillReturnAA->isAssumedWillReturn()) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + // FIXME: Prohibit any recursion for now + auto *NoRecurseAA = A.getAAFor(*this, *I); + if ((!NoRecurseAA || !NoRecurseAA->isValidState() || + !NoRecurseAA->isAssumedNoRecurse()) && + !ICS.hasFnAttr(Attribute::NoRecurse)) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + } + } + + return ChangeStatus::UNCHANGED; +} + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -982,6 +1094,9 @@ registerAA(*new AAReturnedValuesImpl(F, 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/Analysis/BasicAA/cs-cs.ll =================================================================== --- llvm/test/Analysis/BasicAA/cs-cs.ll +++ llvm/test/Analysis/BasicAA/cs-cs.ll @@ -364,26 +364,26 @@ call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] ret void -; CHECK: Just Ref: Ptr: i8* %p <-> call void @a_readonly_func(i8* %p) #6 [ "unknown"() ] -; CHECK: Just Ref: Ptr: i8* %q <-> call void @a_readonly_func(i8* %p) #6 [ "unknown"() ] -; CHECK: NoModRef: Ptr: i8* %p <-> call void @an_inaccessiblememonly_func() #7 [ "unknown"() ] -; CHECK: NoModRef: Ptr: i8* %q <-> call void @an_inaccessiblememonly_func() #7 [ "unknown"() ] -; CHECK: NoModRef: Ptr: i8* %p <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #8 [ "unknown"() ] -; CHECK: Both ModRef (MustAlias): Ptr: i8* %q <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #8 [ "unknown"() ] -; CHECK: NoModRef: Ptr: i8* %p <-> call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] -; CHECK: Both ModRef (MustAlias): Ptr: i8* %q <-> call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] -; CHECK: Just Ref: call void @a_readonly_func(i8* %p) #6 [ "unknown"() ] <-> call void @an_inaccessiblememonly_func() #7 [ "unknown"() ] -; CHECK: Just Ref: call void @a_readonly_func(i8* %p) #6 [ "unknown"() ] <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #8 [ "unknown"() ] -; CHECK: Just Ref: call void @a_readonly_func(i8* %p) #6 [ "unknown"() ] <-> call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] -; CHECK: Both ModRef: call void @an_inaccessiblememonly_func() #7 [ "unknown"() ] <-> call void @a_readonly_func(i8* %p) #6 [ "unknown"() ] -; CHECK: Both ModRef: call void @an_inaccessiblememonly_func() #7 [ "unknown"() ] <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #8 [ "unknown"() ] -; CHECK: NoModRef: call void @an_inaccessiblememonly_func() #7 [ "unknown"() ] <-> call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] -; CHECK: Both ModRef: call void @an_inaccessibleorargmemonly_func(i8* %q) #8 [ "unknown"() ] <-> call void @a_readonly_func(i8* %p) #6 [ "unknown"() ] -; CHECK: Both ModRef: call void @an_inaccessibleorargmemonly_func(i8* %q) #8 [ "unknown"() ] <-> call void @an_inaccessiblememonly_func() #7 [ "unknown"() ] -; CHECK: Both ModRef (MustAlias): call void @an_inaccessibleorargmemonly_func(i8* %q) #8 [ "unknown"() ] <-> call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] -; CHECK: Both ModRef: call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] <-> call void @a_readonly_func(i8* %p) #6 [ "unknown"() ] -; CHECK: NoModRef: call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] <-> call void @an_inaccessiblememonly_func() #7 [ "unknown"() ] -; CHECK: Both ModRef (MustAlias): call void @an_argmemonly_func(i8* %q) #9 [ "unknown"() ] <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #8 [ "unknown"() ] +; CHECK: Just Ref: Ptr: i8* %p <-> call void @a_readonly_func(i8* %p) #7 [ "unknown"() ] +; CHECK: Just Ref: Ptr: i8* %q <-> call void @a_readonly_func(i8* %p) #7 [ "unknown"() ] +; CHECK: NoModRef: Ptr: i8* %p <-> call void @an_inaccessiblememonly_func() #8 [ "unknown"() ] +; CHECK: NoModRef: Ptr: i8* %q <-> call void @an_inaccessiblememonly_func() #8 [ "unknown"() ] +; CHECK: NoModRef: Ptr: i8* %p <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #9 [ "unknown"() ] +; CHECK: Both ModRef (MustAlias): Ptr: i8* %q <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #9 [ "unknown"() ] +; CHECK: NoModRef: Ptr: i8* %p <-> call void @an_argmemonly_func(i8* %q) #10 [ "unknown"() ] +; CHECK: Both ModRef (MustAlias): Ptr: i8* %q <-> call void @an_argmemonly_func(i8* %q) #10 [ "unknown"() ] +; CHECK: Just Ref: call void @a_readonly_func(i8* %p) #7 [ "unknown"() ] <-> call void @an_inaccessiblememonly_func() #8 [ "unknown"() ] +; CHECK: Just Ref: call void @a_readonly_func(i8* %p) #7 [ "unknown"() ] <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #9 [ "unknown"() ] +; CHECK: Just Ref: call void @a_readonly_func(i8* %p) #7 [ "unknown"() ] <-> call void @an_argmemonly_func(i8* %q) #10 [ "unknown"() ] +; CHECK: Both ModRef: call void @an_inaccessiblememonly_func() #8 [ "unknown"() ] <-> call void @a_readonly_func(i8* %p) #7 [ "unknown"() ] +; CHECK: Both ModRef: call void @an_inaccessiblememonly_func() #8 [ "unknown"() ] <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #9 [ "unknown"() ] +; CHECK: NoModRef: call void @an_inaccessiblememonly_func() #8 [ "unknown"() ] <-> call void @an_argmemonly_func(i8* %q) #10 [ "unknown"() ] +; CHECK: Both ModRef: call void @an_inaccessibleorargmemonly_func(i8* %q) #9 [ "unknown"() ] <-> call void @a_readonly_func(i8* %p) #7 [ "unknown"() ] +; CHECK: Both ModRef: call void @an_inaccessibleorargmemonly_func(i8* %q) #9 [ "unknown"() ] <-> call void @an_inaccessiblememonly_func() #8 [ "unknown"() ] +; CHECK: Both ModRef (MustAlias): call void @an_inaccessibleorargmemonly_func(i8* %q) #9 [ "unknown"() ] <-> call void @an_argmemonly_func(i8* %q) #10 [ "unknown"() ] +; CHECK: Both ModRef: call void @an_argmemonly_func(i8* %q) #10 [ "unknown"() ] <-> call void @a_readonly_func(i8* %p) #7 [ "unknown"() ] +; CHECK: NoModRef: call void @an_argmemonly_func(i8* %q) #10 [ "unknown"() ] <-> call void @an_inaccessiblememonly_func() #8 [ "unknown"() ] +; CHECK: Both ModRef (MustAlias): call void @an_argmemonly_func(i8* %q) #10 [ "unknown"() ] <-> call void @an_inaccessibleorargmemonly_func(i8* %q) #9 [ "unknown"() ] } attributes #0 = { argmemonly nounwind } Index: llvm/test/Bindings/llvm-c/debug_info.ll =================================================================== --- llvm/test/Bindings/llvm-c/debug_info.ll +++ llvm/test/Bindings/llvm-c/debug_info.ll @@ -18,7 +18,7 @@ ; CHECK: ; Function Attrs: nounwind readnone speculatable ; CHECK-NEXT: declare void @llvm.dbg.value(metadata, metadata, metadata) #0 -; CHECK: attributes #0 = { nounwind readnone speculatable } +; CHECK: attributes #0 = { nounwind readnone speculatable willreturn } ; CHECK: !llvm.dbg.cu = !{!0} ; CHECK-NEXT: !FooType = !{!16} Index: llvm/test/Feature/intrinsics.ll =================================================================== --- llvm/test/Feature/intrinsics.ll +++ llvm/test/Feature/intrinsics.ll @@ -69,5 +69,5 @@ ret void } -; CHECK: attributes #0 = { nounwind readnone speculatable } +; CHECK: attributes #0 = { nounwind readnone speculatable willreturn } ; CHECK: attributes #1 = { cold noreturn nounwind } Index: llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll =================================================================== --- llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll +++ llvm/test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll @@ -39,7 +39,7 @@ declare void @llvm.dbg.value(metadata, metadata, metadata) nounwind readnone ; CHECK: attributes #0 = { nounwind ssp } -; CHECK: attributes #1 = { nounwind readnone speculatable } +; CHECK: attributes #1 = { nounwind readnone speculatable willreturn } ; CHECK: attributes #2 = { noinline nounwind ssp } ; CHECK: attributes [[NUW]] = { nounwind } Index: llvm/test/Transforms/DeadStoreElimination/simple.ll =================================================================== --- llvm/test/Transforms/DeadStoreElimination/simple.ll +++ llvm/test/Transforms/DeadStoreElimination/simple.ll @@ -508,7 +508,7 @@ ; CHECK-NEXT: store i8 97, i8* [[ARRAYIDX]], align 1 ; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [2 x i8], [2 x i8]* [[X]], i64 0, i64 1 ; CHECK-NEXT: store i8 0, i8* [[ARRAYIDX1]], align 1 -; CHECK-NEXT: [[CALL:%.*]] = call i8* @strdup(i8* [[ARRAYIDX]]) #1 +; CHECK-NEXT: [[CALL:%.*]] = call i8* @strdup(i8* [[ARRAYIDX]]) #2 ; CHECK-NEXT: ret i8* [[CALL]] ; %x = alloca [2 x i8], align 1 @@ -546,7 +546,7 @@ ; CHECK-NEXT: [[P_4:%.*]] = getelementptr i8, i8* [[P:%.*]], i64 4 ; CHECK-NEXT: [[TMP:%.*]] = load i8, i8* [[P_4]], align 1 ; CHECK-NEXT: store i8 0, i8* [[P_4]], align 1 -; CHECK-NEXT: [[Q:%.*]] = call i8* @strdup(i8* [[P]]) #4 +; CHECK-NEXT: [[Q:%.*]] = call i8* @strdup(i8* [[P]]) #5 ; CHECK-NEXT: store i8 [[TMP]], i8* [[P_4]], align 1 ; CHECK-NEXT: ret i8* [[Q]] ; Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -8,7 +8,7 @@ ; TEST SCC test returning an integer value argument ; -; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable +; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable willreturn ; BOTH-NEXT: define i32 @sink_r0(i32 returned %r) ; BOTH: Function Attrs: noinline nounwind readnone uwtable ; BOTH-NEXT: define i32 @scc_r1(i32 %a, i32 returned %r, i32 %b) @@ -438,7 +438,7 @@ ; return b == 0? b : x; ; } ; -; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable +; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable willreturn ; BOTH-NEXT: define double @select_and_phi(double returned %b) ; ; FNATTR: define double @select_and_phi(double %b) @@ -497,7 +497,7 @@ ; return (double*)b; ; } ; -; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable +; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable willreturn ; BOTH-NEXT: define double* @bitcast(i32* readnone returned %b) ; ; FNATTR: define double* @bitcast(i32* readnone %b) @@ -518,7 +518,7 @@ ; return b != 0 ? b : x; ; } ; -; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable +; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable willreturn ; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* readnone returned %b) ; ; FNATTR: define double* @bitcasts_select_and_phi(i32* readnone %b) @@ -554,7 +554,7 @@ ; /* return undef */ ; } ; -; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable +; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable willreturn ; BOTH-NEXT: define double* @ret_arg_arg_undef(i32* readnone returned %b) ; ; FNATTR: define double* @ret_arg_arg_undef(i32* readnone %b) @@ -590,7 +590,7 @@ ; /* return undef */ ; } ; -; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable +; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable willreturn ; BOTH-NEXT: define double* @ret_undef_arg_arg(i32* readnone returned %b) ; ; FNATTR: define double* @ret_undef_arg_arg(i32* readnone %b) @@ -626,7 +626,7 @@ ; /* return undef */ ; } ; -; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable +; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable willreturn ; BOTH-NEXT: define double* @ret_undef_arg_undef(i32* readnone returned %b) ; ; FNATTR: define double* @ret_undef_arg_undef(i32* readnone %b) @@ -730,7 +730,7 @@ attributes #0 = { noinline nounwind uwtable } ; BOTH-NOT: attributes # -; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nounwind readnone uwtable } +; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nounwind readnone uwtable willreturn } ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind readnone uwtable } ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind readonly uwtable } ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable } Index: llvm/test/Transforms/FunctionAttrs/nounwind.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nounwind.ll +++ llvm/test/Transforms/FunctionAttrs/nounwind.ll @@ -4,7 +4,7 @@ ; TEST 1 ; CHECK: Function Attrs: norecurse nounwind readnone ; CHECK-NEXT: define i32 @foo1() -; ATTRIBUTOR: Function Attrs: nounwind +; ATTRIBUTOR: Function Attrs: norecurse nounwind willreturn ; ATTRIBUTOR-NEXT: define i32 @foo1() define i32 @foo1() { ret i32 1 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: noinline norecurse nounwind uwtable willreturn +; ATTRIBUTOR-NEXT: define void @only_return() define void @only_return() #0 { ret void } @@ -25,6 +28,8 @@ ; FIXME: missing willreturn ; FNATTR: Function Attrs: noinline nounwind readnone uwtable ; FNATTR-NEXT: define i32 @fib(i32) +; ATTRIBUTOR: Function Attrs: noinline 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: noinline norecurse 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: noinline norecurse 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: noinline 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: noinline 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,16 @@ ; TEST 6 (positive case) ; Call intrinsic function -; FNATTRS: Function Attrs: noinline readnone speculatable +; FNATTRS: Function Attrs: noinline readnone speculatable willreturn ; FNATTRS-NEXT: declare float @llvm.floor.f32(float) +; ATTRIBUTOR: Function Attrs: nounwind readnone speculatable willreturn +; 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) +; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn +; 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 +227,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 +249,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 +266,8 @@ ; FIXME: missing willreturn ; FNATTR: Function Attrs: noinline nounwind uwtable ; FNATTR-NEXT: define void @f2() +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NEXT: define void @f2() define void @f2() #0 { tail call void @f1() ret void @@ -241,6 +280,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 +298,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: 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 +333,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: noinline norecurse 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 +366,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: noinline norecurse 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 +404,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: noinline norecurse 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 +433,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: noinline norecurse 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 +454,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 +468,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 @@ -439,6 +496,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 @@ -451,6 +511,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 @@ -464,6 +527,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/test/Transforms/Inline/noalias-calls.ll =================================================================== --- llvm/test/Transforms/Inline/noalias-calls.ll +++ llvm/test/Transforms/Inline/noalias-calls.ll @@ -22,13 +22,13 @@ ret void } -; CHECK: define void @foo(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #2 { +; CHECK: define void @foo(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #3 { ; CHECK: entry: -; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %a, i8* align 16 %b, i64 16, i1 false) #1, !noalias !0 -; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %b, i8* align 16 %c, i64 16, i1 false) #1, !noalias !3 -; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %a, i8* align 16 %c, i64 16, i1 false) #1, !alias.scope !5 -; CHECK: call void @hey() #1, !noalias !5 -; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %{{.*}}, i8* align 16 %c, i64 16, i1 false) #1, !noalias !3 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %a, i8* align 16 %b, i64 16, i1 false) #2, !noalias !0 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %b, i8* align 16 %c, i64 16, i1 false) #2, !noalias !3 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %a, i8* align 16 %c, i64 16, i1 false) #2, !alias.scope !5 +; CHECK: call void @hey() #2, !noalias !5 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 16 %{{.*}}, i8* align 16 %c, i64 16, i1 false) #2, !noalias !3 ; CHECK: ret void ; CHECK: } Index: llvm/test/Transforms/ObjCARC/basic.ll =================================================================== --- llvm/test/Transforms/ObjCARC/basic.ll +++ llvm/test/Transforms/ObjCARC/basic.ll @@ -3070,5 +3070,5 @@ !5 = !{i32 2, !"Debug Info Version", i32 3} ; CHECK: attributes [[NUW]] = { nounwind } -; CHECK: attributes #1 = { nounwind readnone speculatable } +; CHECK: attributes #1 = { nounwind readnone speculatable willreturn } ; CHECK: ![[RELEASE]] = !{} Index: llvm/test/Transforms/ObjCARC/ensure-that-exception-unwind-path-is-visited.ll =================================================================== --- llvm/test/Transforms/ObjCARC/ensure-that-exception-unwind-path-is-visited.ll +++ llvm/test/Transforms/ObjCARC/ensure-that-exception-unwind-path-is-visited.ll @@ -105,7 +105,7 @@ declare void @llvm.dbg.value(metadata, metadata, metadata) nounwind readnone ; CHECK: attributes #0 = { ssp uwtable } -; CHECK: attributes #1 = { nounwind readnone speculatable } +; CHECK: attributes #1 = { nounwind readnone speculatable willreturn } ; CHECK: attributes #2 = { nonlazybind } ; CHECK: attributes [[NUW]] = { nounwind } ; CHECK: attributes #4 = { noinline ssp uwtable } Index: llvm/test/Transforms/ObjCARC/nested.ll =================================================================== --- llvm/test/Transforms/ObjCARC/nested.ll +++ llvm/test/Transforms/ObjCARC/nested.ll @@ -821,5 +821,5 @@ ; CHECK: attributes [[NUW]] = { nounwind } -; CHECK: attributes #1 = { argmemonly nounwind } +; CHECK: attributes #1 = { argmemonly nounwind willreturn } ; CHECK: attributes #2 = { nonlazybind } Index: llvm/test/Transforms/SLPVectorizer/X86/call.ll =================================================================== --- llvm/test/Transforms/SLPVectorizer/X86/call.ll +++ llvm/test/Transforms/SLPVectorizer/X86/call.ll @@ -173,5 +173,5 @@ ; CHECK: declare <2 x double> @llvm.pow.v2f64(<2 x double>, <2 x double>) [[ATTR0]] ; CHECK: declare <2 x double> @llvm.exp2.v2f64(<2 x double>) [[ATTR0]] -; CHECK: attributes [[ATTR0]] = { nounwind readnone speculatable } +; CHECK: attributes [[ATTR0]] = { nounwind readnone speculatable willreturn } Index: llvm/test/Verifier/fp-intrinsics.ll =================================================================== --- llvm/test/Verifier/fp-intrinsics.ll +++ llvm/test/Verifier/fp-intrinsics.ll @@ -12,7 +12,7 @@ ; attached to the FP intrinsic. ; CHECK1: declare double @llvm.experimental.constrained.fadd.f64(double, double, metadata, metadata) #[[ATTR:[0-9]+]] ; CHECK1: declare double @llvm.experimental.constrained.sqrt.f64(double, metadata, metadata) #[[ATTR]] -; CHECK1: attributes #[[ATTR]] = { inaccessiblememonly nounwind } +; CHECK1: attributes #[[ATTR]] = { inaccessiblememonly nounwind willreturn } ; Note: FP exceptions aren't usually caught through normal unwind mechanisms, ; but we may want to revisit this for asynchronous exception handling. define double @f1(double %a, double %b) { 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 @@ -602,6 +602,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; @@ -746,8 +749,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) { @@ -760,6 +764,12 @@ OS << "Attribute::NoReturn"; addComma = true; } + if (intrinsic.isWillReturn) { + if (addComma) + OS << ","; + OS << "Attribute::WillReturn"; + addComma = true; + } if (intrinsic.isCold) { if (addComma) OS << ",";