llvm.org GIT mirror llvm / 52d4c04
[Attributor] Deduce "nosync" function attribute. Introduce and deduce "nosync" function attribute to indicate that a function does not synchronize with another thread in a way that other thread might free memory. Reviewers: jdoerfert, jfb, nhaehnle, arsenm Subscribers: wdng, hfinkel, nhaenhle, mehdi_amini, steven_wu, dexonsmith, arsenm, uenoku, hiraditya, jfb, llvm-commits Differential Revision: https://reviews.llvm.org/D62766 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@365830 91177308-0d34-0410-b5e6-96231b3b80d8 Stefan Stipanovic a month ago
19 changed file(s) with 679 addition(s) and 64 deletion(s). Raw diff Collapse all Expand all
14921492 Annotated functions may still raise an exception, i.a., ``nounwind`` is not implied.
14931493 If an invocation of an annotated function does not return control back
14941494 to a point in the call stack, the behavior is undefined.
1495 ``nosync``
1496 This function attribute indicates that the function does not communicate
1497 (synchronize) with another thread through memory or other well-defined means.
1498 Synchronization is considered possible in the presence of `atomic` accesses
1499 that enforce an order, thus not "unordered" and "monotonic", `volatile` accesses,
1500 as well as `convergent` function calls. Note that through `convergent` function calls
1501 non-memory communication, e.g., cross-lane operations, are possible and are also
1502 considered synchronization. However `convergent` does not contradict `nosync`.
1503 If an annotated function does ever synchronize with another thread,
1504 the behavior is undefined.
14951505 ``nounwind``
14961506 This function attribute indicates that the function never raises an
14971507 exception. If the function does raise an exception, its runtime
628628 ATTR_KIND_SPECULATIVE_LOAD_HARDENING = 59,
629629 ATTR_KIND_IMMARG = 60,
630630 ATTR_KIND_WILLRETURN = 61,
631 ATTR_KIND_NOFREE = 62
631 ATTR_KIND_NOFREE = 62,
632 ATTR_KIND_NOSYNC = 63
632633 };
633634
634635 enum ComdatSelectionKindCodes {
107107
108108 /// Mark the function as not returning.
109109 def NoReturn : EnumAttr<"noreturn">;
110
111 /// Function does not synchronize.
112 def NoSync : EnumAttr<"nosync">;
110113
111114 /// Disable Indirect Branch Tracking.
112115 def NoCfCheck : EnumAttr<"nocf_check">;
376376 /// state will catch up with the assumed one, for a pessimistic fixpoint it is
377377 /// the other way around.
378378 struct IntegerState : public AbstractState {
379 /// Undrlying integer type, we assume 32 bits to be enough.
379 /// Underlying integer type, we assume 32 bits to be enough.
380380 using base_t = uint32_t;
381381
382382 /// Initialize the (best) state.
663663 };
664664
665665 struct AANoUnwind : public AbstractAttribute {
666 /// An abstract interface for all nosync attributes.
667 AANoUnwind(Value &V, InformationCache &InfoCache)
668 : AbstractAttribute(V, InfoCache) {}
669
670 /// See AbstractAttribute::getAttrKind()/
671 virtual Attribute::AttrKind getAttrKind() const override { return ID; }
672
673 static constexpr Attribute::AttrKind ID = Attribute::NoUnwind;
674
675 /// Returns true if nounwind is assumed.
676 virtual bool isAssumedNoUnwind() const = 0;
677
678 /// Returns true if nounwind is known.
679 virtual bool isKnownNoUnwind() const = 0;
666 /// An abstract interface for all nosync attributes.
667 AANoUnwind(Value &V, InformationCache &InfoCache)
668 : AbstractAttribute(V, InfoCache) {}
669
670 /// See AbstractAttribute::getAttrKind()/
671 virtual Attribute::AttrKind getAttrKind() const override { return ID; }
672
673 static constexpr Attribute::AttrKind ID = Attribute::NoUnwind;
674
675 /// Returns true if nounwind is assumed.
676 virtual bool isAssumedNoUnwind() const = 0;
677
678 /// Returns true if nounwind is known.
679 virtual bool isKnownNoUnwind() const = 0;
680 };
681
682 struct AANoSync : public AbstractAttribute {
683 /// An abstract interface for all nosync attributes.
684 AANoSync(Value &V, InformationCache &InfoCache)
685 : AbstractAttribute(V, InfoCache) {}
686
687 /// See AbstractAttribute::getAttrKind().
688 virtual Attribute::AttrKind getAttrKind() const override {
689 return ID;
690 }
691
692 static constexpr Attribute::AttrKind ID =
693 Attribute::AttrKind(Attribute::NoSync);
694
695 /// Returns true if "nosync" is assumed.
696 virtual bool isAssumedNoSync() const = 0;
697
698 /// Returns true if "nosync" is known.
699 virtual bool isKnownNoSync() const = 0;
680700 };
681701
682702 } // end namespace llvm
657657 KEYWORD(nonnull);
658658 KEYWORD(noredzone);
659659 KEYWORD(noreturn);
660 KEYWORD(nosync);
660661 KEYWORD(nocf_check);
661662 KEYWORD(nounwind);
662663 KEYWORD(optforfuzzing);
12861286 case lltok::kw_nonlazybind: B.addAttribute(Attribute::NonLazyBind); break;
12871287 case lltok::kw_noredzone: B.addAttribute(Attribute::NoRedZone); break;
12881288 case lltok::kw_noreturn: B.addAttribute(Attribute::NoReturn); break;
1289 case lltok::kw_nosync: B.addAttribute(Attribute::NoSync); break;
12891290 case lltok::kw_nocf_check: B.addAttribute(Attribute::NoCfCheck); break;
12901291 case lltok::kw_norecurse: B.addAttribute(Attribute::NoRecurse); break;
12911292 case lltok::kw_nounwind: B.addAttribute(Attribute::NoUnwind); break;
202202 kw_nonnull,
203203 kw_noredzone,
204204 kw_noreturn,
205 kw_nosync,
205206 kw_nocf_check,
206207 kw_nounwind,
207208 kw_optforfuzzing,
12791279 return 1ULL << 62;
12801280 case Attribute::NoFree:
12811281 return 1ULL << 63;
1282 case Attribute::NoSync:
1283 llvm_unreachable("nosync attribute not supported in raw format");
1284 break;
12821285 case Attribute::Dereferenceable:
12831286 llvm_unreachable("dereferenceable attribute not supported in raw format");
12841287 break;
13041307 if (I == Attribute::Dereferenceable ||
13051308 I == Attribute::DereferenceableOrNull ||
13061309 I == Attribute::ArgMemOnly ||
1307 I == Attribute::AllocSize)
1310 I == Attribute::AllocSize ||
1311 I == Attribute::NoSync)
13081312 continue;
13091313 if (uint64_t A = (Val & getRawAttributeMask(I))) {
13101314 if (I == Attribute::Alignment)
14651469 return Attribute::NoRedZone;
14661470 case bitc::ATTR_KIND_NO_RETURN:
14671471 return Attribute::NoReturn;
1472 case bitc::ATTR_KIND_NOSYNC:
1473 return Attribute::NoSync;
14681474 case bitc::ATTR_KIND_NOCF_CHECK:
14691475 return Attribute::NoCfCheck;
14701476 case bitc::ATTR_KIND_NO_UNWIND:
658658 return bitc::ATTR_KIND_NO_RED_ZONE;
659659 case Attribute::NoReturn:
660660 return bitc::ATTR_KIND_NO_RETURN;
661 case Attribute::NoSync:
662 return bitc::ATTR_KIND_NOSYNC;
661663 case Attribute::NoCfCheck:
662664 return bitc::ATTR_KIND_NOCF_CHECK;
663665 case Attribute::NoUnwind:
334334 return "noredzone";
335335 if (hasAttribute(Attribute::NoReturn))
336336 return "noreturn";
337 if (hasAttribute(Attribute::NoSync))
338 return "nosync";
337339 if (hasAttribute(Attribute::WillReturn))
338340 return "willreturn";
339341 if (hasAttribute(Attribute::NoCfCheck))
14921492 static bool isFuncOnlyAttr(Attribute::AttrKind Kind) {
14931493 switch (Kind) {
14941494 case Attribute::NoReturn:
1495 case Attribute::NoSync:
14951496 case Attribute::WillReturn:
14961497 case Attribute::NoCfCheck:
14971498 case Attribute::NoUnwind:
2222 #include "llvm/IR/Argument.h"
2323 #include "llvm/IR/Attributes.h"
2424 #include "llvm/IR/InstIterator.h"
25 #include "llvm/IR/IntrinsicInst.h"
2526 #include "llvm/Support/CommandLine.h"
2627 #include "llvm/Support/Debug.h"
2728 #include "llvm/Support/raw_ostream.h"
4748 STATISTIC(NumFnKnownReturns, "Number of function with known return values");
4849 STATISTIC(NumFnArgumentReturned,
4950 "Number of function arguments marked returned");
51 STATISTIC(NumFnNoSync, "Number of functions marked nosync");
5052
5153 // TODO: Determine a good default value.
5254 //
98100 case Attribute::Returned:
99101 NumFnArgumentReturned++;
100102 return;
103 case Attribute::NoSync:
104 NumFnNoSync++;
105 break;
101106 default:
102107 return;
103108 }
718723 return Changed;
719724 }
720725
726 /// ------------------------ NoSync Function Attribute -------------------------
727
728 struct AANoSyncFunction : AANoSync, BooleanState {
729
730 AANoSyncFunction(Function &F, InformationCache &InfoCache)
731 : AANoSync(F, InfoCache) {}
732
733 /// See AbstractAttribute::getState()
734 /// {
735 AbstractState &getState() override { return *this; }
736 const AbstractState &getState() const override { return *this; }
737 /// }
738
739 /// See AbstractAttribute::getManifestPosition().
740 virtual ManifestPosition getManifestPosition() const override {
741 return MP_FUNCTION;
742 }
743
744 virtual const std::string getAsStr() const override {
745 return getAssumed() ? "nosync" : "may-sync";
746 }
747
748 /// See AbstractAttribute::updateImpl(...).
749 virtual ChangeStatus updateImpl(Attributor &A) override;
750
751 /// See AANoSync::isAssumedNoSync()
752 virtual bool isAssumedNoSync() const override { return getAssumed(); }
753
754 /// See AANoSync::isKnownNoSync()
755 virtual bool isKnownNoSync() const override { return getKnown(); }
756
757 /// Helper function used to determine whether an instruction is non-relaxed
758 /// atomic. In other words, if an atomic instruction does not have unordered
759 /// or monotonic ordering
760 static bool isNonRelaxedAtomic(Instruction *I);
761
762 /// Helper function used to determine whether an instruction is volatile.
763 static bool isVolatile(Instruction *I);
764
765 /// Helper function uset to check if intrinsic is volatile (memcpy, memmove, memset).
766 static bool isNoSyncIntrinsic(Instruction *I);
767 };
768
769 bool AANoSyncFunction::isNonRelaxedAtomic(Instruction *I) {
770 if (!I->isAtomic())
771 return false;
772
773 AtomicOrdering Ordering;
774 switch (I->getOpcode()) {
775 case Instruction::AtomicRMW:
776 Ordering = cast(I)->getOrdering();
777 break;
778 case Instruction::Store:
779 Ordering = cast(I)->getOrdering();
780 break;
781 case Instruction::Load:
782 Ordering = cast(I)->getOrdering();
783 break;
784 case Instruction::Fence: {
785 auto *FI = cast(I);
786 if (FI->getSyncScopeID() == SyncScope::SingleThread)
787 return false;
788 Ordering = FI->getOrdering();
789 break;
790 }
791 case Instruction::AtomicCmpXchg: {
792 AtomicOrdering Success = cast(I)->getSuccessOrdering();
793 AtomicOrdering Failure = cast(I)->getFailureOrdering();
794 // Only if both are relaxed, than it can be treated as relaxed.
795 // Otherwise it is non-relaxed.
796 if (Success != AtomicOrdering::Unordered &&
797 Success != AtomicOrdering::Monotonic)
798 return true;
799 if (Failure != AtomicOrdering::Unordered &&
800 Failure != AtomicOrdering::Monotonic)
801 return true;
802 return false;
803 }
804 default:
805 llvm_unreachable(
806 "New atomic operations need to be known in the attributor.");
807 }
808
809 // Relaxed.
810 if (Ordering == AtomicOrdering::Unordered ||
811 Ordering == AtomicOrdering::Monotonic)
812 return false;
813 return true;
814 }
815
816 /// Checks if an intrinsic is nosync. Currently only checks mem* intrinsics.
817 /// FIXME: We should ipmrove the handling of intrinsics.
818 bool AANoSyncFunction::isNoSyncIntrinsic(Instruction *I) {
819 if (auto *II = dyn_cast(I)) {
820 switch (II->getIntrinsicID()) {
821 /// Element wise atomic memory intrinsics are can only be unordered,
822 /// therefore nosync.
823 case Intrinsic::memset_element_unordered_atomic:
824 case Intrinsic::memmove_element_unordered_atomic:
825 case Intrinsic::memcpy_element_unordered_atomic:
826 return true;
827 case Intrinsic::memset:
828 case Intrinsic::memmove:
829 case Intrinsic::memcpy:
830 if (!cast(II)->isVolatile())
831 return true;
832 return false;
833 default:
834 return false;
835 }
836 }
837 return false;
838 }
839
840 bool AANoSyncFunction::isVolatile(Instruction *I) {
841 assert(!ImmutableCallSite(I) && !isa(I) &&
842 "Calls should not be checked here");
843
844 switch (I->getOpcode()) {
845 case Instruction::AtomicRMW:
846 return cast(I)->isVolatile();
847 case Instruction::Store:
848 return cast(I)->isVolatile();
849 case Instruction::Load:
850 return cast(I)->isVolatile();
851 case Instruction::AtomicCmpXchg:
852 return cast(I)->isVolatile();
853 default:
854 return false;
855 }
856 }
857
858 ChangeStatus AANoSyncFunction::updateImpl(Attributor &A) {
859 Function &F = getAnchorScope();
860
861 /// We are looking for volatile instructions or Non-Relaxed atomics.
862 /// FIXME: We should ipmrove the handling of intrinsics.
863 for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(F)) {
864 ImmutableCallSite ICS(I);
865 auto *NoSyncAA = A.getAAFor(*this, *I);
866
867 if (isa(I) && isNoSyncIntrinsic(I))
868 continue;
869
870 if (ICS && (!NoSyncAA || !NoSyncAA->isAssumedNoSync()) &&
871 !ICS.hasFnAttr(Attribute::NoSync)) {
872 indicatePessimisticFixpoint();
873 return ChangeStatus::CHANGED;
874 }
875
876 if(ICS)
877 continue;
878
879 if (!isVolatile(I) && !isNonRelaxedAtomic(I))
880 continue;
881
882 indicatePessimisticFixpoint();
883 return ChangeStatus::CHANGED;
884 }
885
886 auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
887 auto Opcodes = {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr,
888 (unsigned)Instruction::Call};
889
890 for (unsigned Opcode : Opcodes) {
891 for (Instruction *I : OpcodeInstMap[Opcode]) {
892 // At this point we handled all read/write effects and they are all
893 // nosync, so they can be skipped.
894 if (I->mayReadOrWriteMemory())
895 continue;
896
897 ImmutableCallSite ICS(I);
898
899 // non-convergent and readnone imply nosync.
900 if (!ICS.isConvergent())
901 continue;
902
903 indicatePessimisticFixpoint();
904 return ChangeStatus::CHANGED;
905 }
906 }
907
908 return ChangeStatus::UNCHANGED;
909 }
910
721911 /// ----------------------------------------------------------------------------
722912 /// Attributor
723913 /// ----------------------------------------------------------------------------
8621052
8631053 // Every function can be nounwind.
8641054 registerAA(*new AANoUnwindFunction(F, InfoCache));
1055
1056 // Every function might be marked "nosync"
1057 registerAA(*new AANoSyncFunction(F, InfoCache));
8651058
8661059 // Return attributes are only appropriate if the return type is non void.
8671060 Type *ReturnType = F.getReturnType();
808808 case Attribute::NoBuiltin:
809809 case Attribute::NoCapture:
810810 case Attribute::NoReturn:
811 case Attribute::NoSync:
811812 case Attribute::None:
812813 case Attribute::NonNull:
813814 case Attribute::ReadNone:
202202 define void @f34()
203203 ; CHECK: define void @f34()
204204 {
205 call void @nobuiltin() nobuiltin
206 ; CHECK: call void @nobuiltin() #38
205 call void @nobuiltin() nobuiltin
206 ; CHECK: call void @nobuiltin() #39
207207 ret void;
208208 }
209209
358358
359359 ; CHECK: define void @f61() #37
360360 define void @f61() nofree {
361 ret void
362 }
363
364 ; CHECK: define void @f62() #38
365 define void @f62() nosync
366 {
361367 ret void
362368 }
363369
399405 ; CHECK: attributes #35 = { shadowcallstack }
400406 ; CHECK: attributes #36 = { willreturn }
401407 ; CHECK: attributes #37 = { nofree }
402 ; CHECK: attributes #38 = { nobuiltin }
408 ; CHECK: attributes #38 = { nosync }
409 ; CHECK: attributes #39 = { nobuiltin }
77
88 ; TEST SCC test returning an integer value argument
99 ;
10 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
10 ; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
1111 ; BOTH-NEXT: define i32 @sink_r0(i32 returned %r)
12 ; BOTH: Function Attrs: noinline nounwind readnone uwtable
12 ; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
1313 ; BOTH-NEXT: define i32 @scc_r1(i32 %a, i32 returned %r, i32 %b)
14 ; BOTH: Function Attrs: noinline nounwind readnone uwtable
14 ; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
1515 ; BOTH-NEXT: define i32 @scc_r2(i32 %a, i32 %b, i32 returned %r)
16 ; BOTH: Function Attrs: noinline nounwind readnone uwtable
16 ; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
1717 ; BOTH-NEXT: define i32 @scc_rX(i32 %a, i32 %b, i32 %r)
1818 ;
1919 ; FNATTR: define i32 @sink_r0(i32 returned %r)
158158
159159 ; TEST SCC test returning a pointer value argument
160160 ;
161 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
161 ; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
162162 ; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned %r)
163 ; BOTH: Function Attrs: noinline nounwind readnone uwtable
163 ; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
164164 ; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b)
165 ; BOTH: Function Attrs: noinline nounwind readnone uwtable
165 ; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
166166 ; BOTH-NEXT: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r)
167167 ;
168168 ; FNATTR: define double* @ptr_sink_r0(double* readnone returned %r)
169169 ; FNATTR: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b)
170170 ; FNATTR: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r)
171171 ;
172 ; ATTRIBUTOR: define double* @ptr_sink_r0(double* returned %r)
173 ; ATTRIBUTOR: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
174 ; ATTRIBUTOR: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
172 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
173 ; ATTRIBUTOR-NEXT: define double* @ptr_sink_r0(double* returned %r)
174 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
175 ; ATTRIBUTOR-NEXT: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
176 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
177 ; ATTRIBUTOR-NEXT: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
175178 ;
176179 ; double* ptr_scc_r1(double* a, double* b, double* r);
177180 ; double* ptr_scc_r2(double* a, double* b, double* r);
257260 ;
258261 ; FIXME: no-return missing
259262 ; FNATTR: define i32* @rt0(i32* readonly %a)
260 ; BOTH: Function Attrs: noinline nounwind readonly uwtable
263 ; BOTH: Function Attrs: noinline nosync nounwind readonly uwtable
261264 ; BOTH-NEXT: define i32* @rt0(i32* readonly returned %a)
262265 define i32* @rt0(i32* %a) #0 {
263266 entry:
276279 ;
277280 ; FIXME: no-return missing
278281 ; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a)
279 ; BOTH: Function Attrs: noinline nounwind readonly uwtable
282 ; BOTH: Function Attrs: noinline nosync nounwind readonly uwtable
280283 ; BOTH-NEXT: define noalias i32* @rt1(i32* nocapture readonly %a)
281284 define i32* @rt1(i32* %a) #0 {
282285 entry:
437440 ; return b == 0? b : x;
438441 ; }
439442 ;
440 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
443 ; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
441444 ; BOTH-NEXT: define double @select_and_phi(double returned %b)
442445 ;
443446 ; FNATTR: define double @select_and_phi(double %b)
444 ; ATTRIBUTOR: define double @select_and_phi(double returned %b)
447 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
448 ; ATTRIBUTOR-NEXT: define double @select_and_phi(double returned %b)
445449 define double @select_and_phi(double %b) #0 {
446450 entry:
447451 %cmp = fcmp ogt double %b, 0.000000e+00
467471 ; return b == 0? b : x;
468472 ; }
469473 ;
470 ; BOTH: Function Attrs: noinline nounwind readnone uwtable
474 ; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable
471475 ; BOTH-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
472476 ;
473477 ; FNATTR: define double @recursion_select_and_phi(i32 %a, double %b)
474 ; ATTRIBUTOR: define double @recursion_select_and_phi(i32 %a, double returned %b)
478 ;
479 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
480 ; ATTRIBUTOR-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b)
475481 define double @recursion_select_and_phi(i32 %a, double %b) #0 {
476482 entry:
477483 %dec = add nsw i32 %a, -1
496502 ; return (double*)b;
497503 ; }
498504 ;
499 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
505 ; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
500506 ; BOTH-NEXT: define double* @bitcast(i32* readnone returned %b)
501507 ;
502508 ; FNATTR: define double* @bitcast(i32* readnone %b)
503 ; ATTRIBUTOR: define double* @bitcast(i32* returned %b)
509 ;
510 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
511 ; ATTRIBUTOR-NEXT: define double* @bitcast(i32* returned %b)
504512 define double* @bitcast(i32* %b) #0 {
505513 entry:
506514 %bc0 = bitcast i32* %b to double*
517525 ; return b != 0 ? b : x;
518526 ; }
519527 ;
520 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
528 ; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
521529 ; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* readnone returned %b)
522530 ;
523531 ; FNATTR: define double* @bitcasts_select_and_phi(i32* readnone %b)
524 ; ATTRIBUTOR: define double* @bitcasts_select_and_phi(i32* returned %b)
532 ;
533 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
534 ; ATTRIBUTOR-NEXT: define double* @bitcasts_select_and_phi(i32* returned %b)
525535 define double* @bitcasts_select_and_phi(i32* %b) #0 {
526536 entry:
527537 %bc0 = bitcast i32* %b to double*
553563 ; /* return undef */
554564 ; }
555565 ;
556 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
566 ; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
557567 ; BOTH-NEXT: define double* @ret_arg_arg_undef(i32* readnone returned %b)
558568 ;
559569 ; FNATTR: define double* @ret_arg_arg_undef(i32* readnone %b)
560 ; ATTRIBUTOR: define double* @ret_arg_arg_undef(i32* returned %b)
570 ;
571 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
572 ; ATTRIBUTOR-NEXT: define double* @ret_arg_arg_undef(i32* returned %b)
561573 define double* @ret_arg_arg_undef(i32* %b) #0 {
562574 entry:
563575 %bc0 = bitcast i32* %b to double*
589601 ; /* return undef */
590602 ; }
591603 ;
592 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
604 ; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
593605 ; BOTH-NEXT: define double* @ret_undef_arg_arg(i32* readnone returned %b)
594606 ;
595607 ; FNATTR: define double* @ret_undef_arg_arg(i32* readnone %b)
596 ; ATTRIBUTOR: define double* @ret_undef_arg_arg(i32* returned %b)
608 ;
609 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
610 ; ATTRIBUTOR-NEXT: define double* @ret_undef_arg_arg(i32* returned %b)
597611 define double* @ret_undef_arg_arg(i32* %b) #0 {
598612 entry:
599613 %bc0 = bitcast i32* %b to double*
625639 ; /* return undef */
626640 ; }
627641 ;
628 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
642 ; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
629643 ; BOTH-NEXT: define double* @ret_undef_arg_undef(i32* readnone returned %b)
630644 ;
631645 ; FNATTR: define double* @ret_undef_arg_undef(i32* readnone %b)
729743 attributes #0 = { noinline nounwind uwtable }
730744
731745 ; BOTH-NOT: attributes #
732 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nounwind readnone uwtable }
733 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind readnone uwtable }
734 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind readonly uwtable }
746 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nosync nounwind readnone uwtable }
747 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readnone uwtable }
748 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readonly uwtable }
735749 ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable }
736750 ; BOTH-NOT: attributes #
1919 ; }
2020 ;
2121 ; FIXME: no-return missing
22 ; CHECK: Function Attrs: noinline nounwind readnone uwtable
22 ; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable
2323 ; CHECK: define void @srec0()
2424 ;
2525 define void @srec0() #0 {
3636 ; }
3737 ;
3838 ; FIXME: no-return missing
39 ; CHECK: Function Attrs: noinline nounwind readnone uwtable
39 ; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable
4040 ; CHECK: define i32 @srec16(i32 %a)
4141 ;
4242 define i32 @srec16(i32 %a) #0 {
6868 ; }
6969 ;
7070 ; FIXME: no-return missing
71 ; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
71 ; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
7272 ; CHECK: define i32 @endless_loop(i32 %a)
7373 ;
7474 define i32 @endless_loop(i32 %a) #0 {
8888 ; }
8989 ;
9090 ; FIXME: no-return missing
91 ; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable
91 ; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable
9292 ; CHECK: define i32 @dead_return(i32 returned %a)
9393 ;
9494 define i32 @dead_return(i32 %a) #0 {
110110 ; }
111111 ;
112112 ; FIXME: no-return missing
113 ; CHECK: Function Attrs: noinline nounwind readnone uwtable
113 ; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable
114114 ; CHECK: define i32 @multiple_noreturn_calls(i32 %a)
115115 ;
116116 define i32 @multiple_noreturn_calls(i32 %a) #0 {
0 ; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR
1 ; RUN: opt -attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
2 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
3
4 ; Test cases designed for the nosync function attribute.
5 ; FIXME's are used to indicate problems and missing attributes.
6
7 ; struct RT {
8 ; char A;
9 ; int B[10][20];
10 ; char C;
11 ; };
12 ; struct ST {
13 ; int X;
14 ; double Y;
15 ; struct RT Z;
16 ; };
17 ;
18 ; int *foo(struct ST *s) {
19 ; return &s[1].Z.B[5][13];
20 ; }
21
22 ; TEST 1
23 ; non-convergent and readnone implies nosync
24 %struct.RT = type { i8, [10 x [20 x i32]], i8 }
25 %struct.ST = type { i32, double, %struct.RT }
26
27 ; FNATTR: Function Attrs: norecurse nounwind optsize readnone ssp uwtable
28 ; FNATTR-NEXT: define nonnull i32* @foo(%struct.ST* readnone %s)
29 ; ATTRIBUTOR: Function Attrs: nosync nounwind optsize readnone ssp uwtable
30 ; ATTRIBUTOR-NEXT: define i32* @foo(%struct.ST* %s)
31 define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp {
32 entry:
33 %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
34 ret i32* %arrayidx
35 }
36
37 ; TEST 2
38 ; atomic load with monotonic ordering
39 ; int load_monotonic(_Atomic int *num) {
40 ; int n = atomic_load_explicit(num, memory_order_relaxed);
41 ; return n;
42 ; }
43
44 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
45 ; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly)
46 ; ATTRIBUTOR: Function Attrs: norecurse nosync nounwind uwtable
47 ; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture readonly)
48 define i32 @load_monotonic(i32* nocapture readonly) norecurse nounwind uwtable {
49 %2 = load atomic i32, i32* %0 monotonic, align 4
50 ret i32 %2
51 }
52
53
54 ; TEST 3
55 ; atomic store with monotonic ordering.
56 ; void store_monotonic(_Atomic int *num) {
57 ; atomic_load_explicit(num, memory_order_relaxed);
58 ; }
59
60 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
61 ; FNATTR-NEXT: define void @store_monotonic(i32* nocapture)
62 ; ATTRIBUTOR: Function Attrs: norecurse nosync nounwind uwtable
63 ; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture)
64 define void @store_monotonic(i32* nocapture) norecurse nounwind uwtable {
65 store atomic i32 10, i32* %0 monotonic, align 4
66 ret void
67 }
68
69 ; TEST 4 - negative, should not deduce nosync
70 ; atomic load with acquire ordering.
71 ; int load_acquire(_Atomic int *num) {
72 ; int n = atomic_load_explicit(num, memory_order_acquire);
73 ; return n;
74 ; }
75
76 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
77 ; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly)
78 ; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
79 ; ATTRIBUTOR-NOT: nosync
80 ; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture readonly)
81 define i32 @load_acquire(i32* nocapture readonly) norecurse nounwind uwtable {
82 %2 = load atomic i32, i32* %0 acquire, align 4
83 ret i32 %2
84 }
85
86 ; TEST 5 - negative, should not deduce nosync
87 ; atomic load with release ordering
88 ; void load_release(_Atomic int *num) {
89 ; atomic_store_explicit(num, 10, memory_order_release);
90 ; }
91
92 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
93 ; FNATTR-NEXT: define void @load_release(i32* nocapture)
94 ; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
95 ; ATTRIBUTOR-NOT: nosync
96 ; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture)
97 define void @load_release(i32* nocapture) norecurse nounwind uwtable {
98 store atomic volatile i32 10, i32* %0 release, align 4
99 ret void
100 }
101
102 ; TEST 6 - negative volatile, relaxed atomic
103
104 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
105 ; FNATTR-NEXT: define void @load_volatile_release(i32* nocapture)
106 ; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
107 ; ATTRIBUTOR-NOT: nosync
108 ; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture)
109 define void @load_volatile_release(i32* nocapture) norecurse nounwind uwtable {
110 store atomic volatile i32 10, i32* %0 release, align 4
111 ret void
112 }
113
114 ; TEST 7 - negative, should not deduce nosync
115 ; volatile store.
116 ; void volatile_store(volatile int *num) {
117 ; *num = 14;
118 ; }
119
120 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
121 ; FNATTR-NEXT: define void @volatile_store(i32*)
122 ; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
123 ; ATTRIBUTOR-NOT: nosync
124 ; ATTRIBUTOR-NEXT: define void @volatile_store(i32*)
125 define void @volatile_store(i32*) norecurse nounwind uwtable {
126 store volatile i32 14, i32* %0, align 4
127 ret void
128 }
129
130 ; TEST 8 - negative, should not deduce nosync
131 ; volatile load.
132 ; int volatile_load(volatile int *num) {
133 ; int n = *num;
134 ; return n;
135 ; }
136
137 ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable
138 ; FNATTR-NEXT: define i32 @volatile_load(i32*)
139 ; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable
140 ; ATTRIBUTOR-NOT: nosync
141 ; ATTRIBUTOR-NEXT: define i32 @volatile_load(i32*)
142 define i32 @volatile_load(i32*) norecurse nounwind uwtable {
143 %2 = load volatile i32, i32* %0, align 4
144 ret i32 %2
145 }
146
147 ; TEST 9
148
149 ; FNATTR: Function Attrs: noinline nosync nounwind uwtable
150 ; FNATTR-NEXT: declare void @nosync_function()
151 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
152 ; ATTRIBUTOR-NEXT: declare void @nosync_function()
153 declare void @nosync_function() noinline nounwind uwtable nosync
154
155 ; FNATTR: Function Attrs: noinline nounwind uwtable
156 ; FNATTR-NEXT: define void @call_nosync_function()
157 ; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable
158 ; ATTRIBUTOR-next: define void @call_nosync_function()
159 define void @call_nosync_function() nounwind uwtable noinline {
160 tail call void @nosync_function() noinline nounwind uwtable
161 ret void
162 }
163
164 ; TEST 10 - negative, should not deduce nosync
165
166 ; FNATTR: Function Attrs: noinline nounwind uwtable
167 ; FNATTR-NEXT: declare void @might_sync()
168 ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
169 ; ATTRIBUTOR-NEXT: declare void @might_sync()
170 declare void @might_sync() noinline nounwind uwtable
171
172 ; FNATTR: Function Attrs: noinline nounwind uwtable
173 ; FNATTR-NEXT: define void @call_might_sync()
174 ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
175 ; ATTRIBUTOR-NOT: nosync
176 ; ATTRIBUTOR-NEXT: define void @call_might_sync()
177 define void @call_might_sync() nounwind uwtable noinline {
178 tail call void @might_sync() noinline nounwind uwtable
179 ret void
180 }
181
182 ; TEST 11 - negative, should not deduce nosync
183 ; volatile operation in same scc. Call volatile_load defined in TEST 8.
184
185 ; FNATTR: Function Attrs: nofree noinline nounwind uwtable
186 ; FNATTR-NEXT: define i32 @scc1(i32*)
187 ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
188 ; ATTRIBUTOR-NOT: nosync
189 ; ATTRIBUTOR-NEXT: define i32 @scc1(i32*)
190 define i32 @scc1(i32*) noinline nounwind uwtable {
191 tail call void @scc2(i32* %0);
192 %val = tail call i32 @volatile_load(i32* %0);
193 ret i32 %val;
194 }
195
196 ; FNATTR: Function Attrs: nofree noinline nounwind uwtable
197 ; FNATTR-NEXT: define void @scc2(i32*)
198 ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable
199 ; ATTRIBUTOR-NOT: nosync
200 ; ATTRIBUTOR-NEXT: define void @scc2(i32*)
201 define void @scc2(i32*) noinline nounwind uwtable {
202 tail call i32 @scc1(i32* %0);
203 ret void;
204 }
205
206 ; TEST 12 - fences, negative
207 ;
208 ; void foo1(int *a, std::atomic flag){
209 ; *a = 100;
210 ; atomic_thread_fence(std::memory_order_release);
211 ; flag.store(true, std::memory_order_relaxed);
212 ; }
213 ;
214 ; void bar(int *a, std::atomic flag){
215 ; while(!flag.load(std::memory_order_relaxed))
216 ; ;
217 ;
218 ; atomic_thread_fence(std::memory_order_acquire);
219 ; int b = *a;
220 ; }
221
222 %"struct.std::atomic" = type { %"struct.std::__atomic_base" }
223 %"struct.std::__atomic_base" = type { i8 }
224
225 ; FNATTR: Function Attrs: nofree norecurse nounwind
226 ; FNATTR-NEXT: define void @foo1(i32* nocapture, %"struct.std::atomic"* nocapture)
227 ; ATTRIBUTOR-NOT: nosync
228 ; ATTRIBUTOR: define void @foo1(i32*, %"struct.std::atomic"*)
229 define void @foo1(i32*, %"struct.std::atomic"*) {
230 store i32 100, i32* %0, align 4
231 fence release
232 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
233 store atomic i8 1, i8* %3 monotonic, align 1
234 ret void
235 }
236
237 ; FNATTR: Function Attrs: nofree norecurse nounwind
238 ; FNATTR-NEXT: define void @bar(i32* nocapture readnone, %"struct.std::atomic"* nocapture readonly)
239 ; ATTRIBUTOR-NOT: nosync
240 ; ATTRIBUTOR: define void @bar(i32*, %"struct.std::atomic"*)
241 define void @bar(i32 *, %"struct.std::atomic"*) {
242 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
243 br label %4
244
245 4: ; preds = %4, %2
246 %5 = load atomic i8, i8* %3 monotonic, align 1
247 %6 = and i8 %5, 1
248 %7 = icmp eq i8 %6, 0
249 br i1 %7, label %4, label %8
250
251 8: ; preds = %4
252 fence acquire
253 ret void
254 }
255
256 ; TEST 13 - Fence syncscope("singlethread") seq_cst
257 ; FNATTR: Function Attrs: nofree norecurse nounwind
258 ; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture, %"struct.std::atomic"* nocapture)
259 ; ATTRIBUTOR: Function Attrs: nosync
260 ; ATTRIBUTOR: define void @foo1_singlethread(i32*, %"struct.std::atomic"*)
261 define void @foo1_singlethread(i32*, %"struct.std::atomic"*) {
262 store i32 100, i32* %0, align 4
263 fence syncscope("singlethread") release
264 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
265 store atomic i8 1, i8* %3 monotonic, align 1
266 ret void
267 }
268
269 ; FNATTR: Function Attrs: nofree norecurse nounwind
270 ; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone, %"struct.std::atomic"* nocapture readonly)
271 ; ATTRIBUTOR: Function Attrs: nosync
272 ; ATTRIBUTOR: define void @bar_singlethread(i32*, %"struct.std::atomic"*)
273 define void @bar_singlethread(i32 *, %"struct.std::atomic"*) {
274 %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
275 br label %4
276
277 4: ; preds = %4, %2
278 %5 = load atomic i8, i8* %3 monotonic, align 1
279 %6 = and i8 %5, 1
280 %7 = icmp eq i8 %6, 0
281 br i1 %7, label %4, label %8
282
283 8: ; preds = %4
284 fence syncscope("singlethread") acquire
285 ret void
286 }
287
288 declare void @llvm.memcpy(i8* %dest, i8* %src, i32 %len, i1 %isvolatile)
289 declare void @llvm.memset(i8* %dest, i8 %val, i32 %len, i1 %isvolatile)
290
291 ; TEST 14 - negative, checking volatile intrinsics.
292
293 ; ATTRIBUTOR: Function Attrs: nounwind
294 ; ATTRIBUTOR-NOT: nosync
295 ; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2)
296 define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
297 call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1)
298 ret i32 4
299 }
300
301 ; TEST 15 - positive, non-volatile intrinsic.
302
303 ; ATTRIBUTOR: Function Attrs: nosync
304 ; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* %ptr1, i8 %val)
305 define i32 @memset_non_volatile(i8* %ptr1, i8 %val) {
306 call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0)
307 ret i32 4
308 }
309
310 ; TEST 16 - negative, inline assembly.
311
312 ; ATTRIBUTOR: define i32 @inline_asm_test(i32 %x)
313 define i32 @inline_asm_test(i32 %x) {
314 call i32 asm "bswap $0", "=r,r"(i32 %x)
315 ret i32 4
316 }
317
318 declare void @readnone_test() convergent readnone
319
320 ; ATTRIBUTOR: define void @convergent_readnone()
321 ; TEST 17 - negative. Convergent
322 define void @convergent_readnone(){
323 call void @readnone_test()
324 ret void
325 }
326
327 ; ATTRIBUTOR: Function Attrs: nounwind
328 ; ATTRIBUTOR-NEXT: declare void @llvm.x86.sse2.clflush(i8*)
329 declare void @llvm.x86.sse2.clflush(i8*)
330 @a = common global i32 0, align 4
331
332 ; TEST 18 - negative. Synchronizing intrinsic
333
334 ; ATTRIBUTOR: Function Attrs: nounwind
335 ; ATTRIBUTOR-NOT: nosync
336 ; ATTRIBUTOR-NEXT: define void @i_totally_sync()
337 define void @i_totally_sync() {
338 tail call void @llvm.x86.sse2.clflush(i8* bitcast (i32* @a to i8*))
339 ret void
340 }
341
342 declare float @llvm.cos(float %val) readnone
343
344 ; TEST 19 - positive, readnone & non-convergent intrinsic.
345
346 ; ATTRIBUTOR: Function Attrs: nosync nounwind
347 ; ATTRIBUTOR-NEXT: define i32 @cos_test(float %x)
348 define i32 @cos_test(float %x) {
349 call float @llvm.cos(float %x)
350 ret i32 4
351 }
33 ; TEST 1
44 ; CHECK: Function Attrs: norecurse nounwind readnone
55 ; CHECK-NEXT: define i32 @foo1()
6 ; ATTRIBUTOR: Function Attrs: nounwind
6 ; ATTRIBUTOR: Function Attrs: nosync nounwind
77 ; ATTRIBUTOR-NEXT: define i32 @foo1()
88 define i32 @foo1() {
99 ret i32 1
1212 ; TEST 2
1313 ; CHECK: Function Attrs: nounwind readnone
1414 ; CHECK-NEXT: define i32 @scc1_foo()
15 ; ATTRIBUTOR: Function Attrs: nounwind
15 ; ATTRIBUTOR: Function Attrs: nosync nounwind
1616 ; ATTRIBUTOR-NEXT: define i32 @scc1_foo()
1717 define i32 @scc1_foo() {
1818 %1 = call i32 @scc1_bar()
2323 ; TEST 3
2424 ; CHECK: Function Attrs: nounwind readnone
2525 ; CHECK-NEXT: define i32 @scc1_bar()
26 ; ATTRIBUTOR: Function Attrs: nounwind
26 ; ATTRIBUTOR: Function Attrs: nosync nounwind
2727 ; ATTRIBUTOR-NEXT: define i32 @scc1_bar()
2828 define i32 @scc1_bar() {
2929 %1 = call i32 @scc1_foo()
2929 ;
3030 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
3131
32 ; CHECK: Function Attrs: nofree nounwind
32 ; CHECK: Function Attrs: nofree nosync nounwind
3333 ; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
3434 define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
3535 entry:
4040 ret i32* %call3
4141 }
4242
43 ; CHECK: Function Attrs: nofree nounwind
43 ; CHECK: Function Attrs: nofree nosync nounwind
4444 ; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0)
4545 define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
4646 entry:
6969 ret i32* %retval.0
7070 }
7171
72 ; CHECK: Function Attrs: nofree nounwind
72 ; CHECK: Function Attrs: nofree nosync nounwind
7373 ; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0)
7474 define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
7575 entry:
101101 ret i32* %retval.0
102102 }
103103
104 ; CHECK: Function Attrs: nofree norecurse nounwind
104 ; CHECK: Function Attrs: nofree norecurse nosync nounwind
105105 ; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0)
106106 define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
107107 entry:
120120 ret i32* %w0
121121 }
122122
123 ; CHECK: Function Attrs: nofree nounwind
123 ; CHECK: Function Attrs: nofree nosync nounwind
124124 ; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0)
125125 define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
126126 entry:
146146 ret i32* %retval.0
147147 }
148148
149 ; CHECK: Function Attrs: nofree nounwind
149 ; CHECK: Function Attrs: nofree nosync nounwind
150150 ; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
151151 define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
152152 entry:
159159 ; for a subset relation.
160160 ;
161161 ; CHECK-NOT: attributes #
162 ; CHECK: attributes #{{.*}} = { nofree nounwind }
163 ; CHECK: attributes #{{.*}} = { nofree norecurse nounwind }
162 ; CHECK: attributes #{{.*}} = { nofree nosync nounwind }
163 ; CHECK: attributes #{{.*}} = { nofree norecurse nosync nounwind }
164164 ; CHECK-NOT: attributes #