diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -1125,7 +1125,8 @@ // allow partial specialization, which is needed in this case. So instead, we // use a constexpr bool to perform the SFINAE, and then use this information // inside the function template. - template static constexpr bool HasLegacyWrapper = false; + template + static constexpr bool HasLegacyWrapper = false; template typename Analysis::Result *getAnalysis(const Function &F, @@ -1168,7 +1169,7 @@ template constexpr bool AnalysisGetter::HasLegacyWrapper< - Analysis, std::void_t> = true; + Analysis, std::void_t> = true; /// Data structure to hold cached (LLVM-IR) information. /// @@ -5523,7 +5524,8 @@ AANonConvergent(const IRPosition &IRP, Attributor &A) : Base(IRP) {} /// Create an abstract attribute view for the position \p IRP. - static AANonConvergent &createForPosition(const IRPosition &IRP, Attributor &A); + static AANonConvergent &createForPosition(const IRPosition &IRP, + Attributor &A); /// Return true if "non-convergent" is assumed. bool isAssumedNotConvergent() const { return getAssumed(); } @@ -5537,7 +5539,8 @@ /// See AbstractAttribute::getIdAddr() const char *getIdAddr() const override { return &ID; } - /// This function should return true if the type of the \p AA is AANonConvergent. + /// This function should return true if the type of the \p AA is + /// AANonConvergent. static bool classof(const AbstractAttribute *AA) { return (AA->getIdAddr() == &ID); } @@ -6014,6 +6017,46 @@ AA::ValueScope Scope = AA::Interprocedural) const = 0; }; +/// An abstract interface for address space information. +struct AAAddressSpace : public StateWrapper { + AAAddressSpace(const IRPosition &IRP, Attributor &A) + : StateWrapper(IRP) {} + + /// See AbstractAttribute::isValidIRPositionForInit + static bool isValidIRPositionForInit(Attributor &A, const IRPosition &IRP) { + if (!IRP.getAssociatedType()->isPtrOrPtrVectorTy()) + return false; + return AbstractAttribute::isValidIRPositionForInit(A, IRP); + } + + /// Return the address space of the associated value. \p NoAddressSpace is + /// returned if the associated value is dead. This functions is not supposed + /// to be called if the AA is invalid. + virtual int32_t getAddressSpace() const = 0; + + /// Create an abstract attribute view for the position \p IRP. + static AAAddressSpace &createForPosition(const IRPosition &IRP, + Attributor &A); + + /// See AbstractAttribute::getName() + const std::string getName() const override { return "AAAddressSpace"; } + + /// See AbstractAttribute::getIdAddr() + const char *getIdAddr() const override { return &ID; } + + /// This function should return true if the type of the \p AA is + /// AAAssumptionInfo + static bool classof(const AbstractAttribute *AA) { + return (AA->getIdAddr() == &ID); + } + + // No address space which indicates the associated value is dead. + static const int32_t NoAddressSpace = -1; + + /// Unique ID (due to the unique address) + static const char ID; +}; + raw_ostream &operator<<(raw_ostream &, const AAPointerInfo::Access &); /// Run options, used by the pass manager. diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -3501,18 +3501,21 @@ assert(Success && "Expected the check call to be successful!"); auto LoadStorePred = [&](Instruction &I) -> bool { - if (isa(I)) { - getOrCreateAAFor( - IRPosition::value(*cast(I).getPointerOperand())); + if (auto *LI = dyn_cast(&I)) { + getOrCreateAAFor(IRPosition::value(*LI->getPointerOperand())); if (SimplifyAllLoads) getAssumedSimplified(IRPosition::value(I), nullptr, UsedAssumedInformation, AA::Intraprocedural); + getOrCreateAAFor( + IRPosition::value(*LI->getPointerOperand())); } else { auto &SI = cast(I); getOrCreateAAFor(IRPosition::inst(I)); getAssumedSimplified(IRPosition::value(*SI.getValueOperand()), nullptr, UsedAssumedInformation, AA::Intraprocedural); getOrCreateAAFor(IRPosition::value(*SI.getPointerOperand())); + getOrCreateAAFor( + IRPosition::value(*SI.getPointerOperand())); } return true; }; diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp --- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp +++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp @@ -188,6 +188,7 @@ PIPE_OPERATOR(AAPointerInfo) PIPE_OPERATOR(AAAssumptionInfo) PIPE_OPERATOR(AAUnderlyingObjects) +PIPE_OPERATOR(AAAddressSpace) #undef PIPE_OPERATOR @@ -957,10 +958,8 @@ } auto AddToBins = [&](const AAPointerInfo::RangeList &ToAdd) { - LLVM_DEBUG( - if (ToAdd.size()) - dbgs() << "[AAPointerInfo] Inserting access in new offset bins\n"; - ); + LLVM_DEBUG(if (ToAdd.size()) dbgs() + << "[AAPointerInfo] Inserting access in new offset bins\n";); for (auto Key : ToAdd) { LLVM_DEBUG(dbgs() << " key " << Key << "\n"); @@ -993,10 +992,8 @@ // from the offset bins. AAPointerInfo::RangeList ToRemove; AAPointerInfo::RangeList::set_difference(ExistingRanges, NewRanges, ToRemove); - LLVM_DEBUG( - if (ToRemove.size()) - dbgs() << "[AAPointerInfo] Removing access from old offset bins\n"; - ); + LLVM_DEBUG(if (ToRemove.size()) dbgs() + << "[AAPointerInfo] Removing access from old offset bins\n";); for (auto Key : ToRemove) { LLVM_DEBUG(dbgs() << " key " << Key << "\n"); @@ -3119,7 +3116,8 @@ /// See AbstractAttribute::updateImpl(...). ChangeStatus updateImpl(Attributor &A) override { - // If all function calls are known to not be convergent, we are not convergent. + // If all function calls are known to not be convergent, we are not + // convergent. auto CalleeIsNotConvergent = [&](Instruction &Inst) { CallBase &CB = cast(Inst); auto *Callee = dyn_cast_if_present(CB.getCalledOperand()); @@ -3747,8 +3745,9 @@ QueryCache.erase(&RQI); // Insert a plain RQI (w/o exclusion set) if that makes sense. Two options: - // 1) If it is reachable, it doesn't matter if we have an exclusion set for this query. - // 2) We did not use the exclusion set, potentially because there is none. + // 1) If it is reachable, it doesn't matter if we have an exclusion set for + // this query. 2) We did not use the exclusion set, potentially because + // there is none. if (Result == RQITy::Reachable::Yes || !UsedExclusionSet) { RQITy PlainRQI(RQI.From, RQI.To); if (!QueryCache.count(&PlainRQI)) { @@ -8297,8 +8296,8 @@ ME = MemoryEffects::writeOnly(); A.removeAttrs(getIRPosition(), AttrKinds); - return A.manifestAttrs(getIRPosition(), - Attribute::getWithMemoryEffects(CB.getContext(), ME)); + return A.manifestAttrs( + getIRPosition(), Attribute::getWithMemoryEffects(CB.getContext(), ME)); } /// See AbstractAttribute::trackStatistics() @@ -8666,9 +8665,8 @@ return ChangeStatus::UNCHANGED; MemoryEffects ME = DeducedAttrs[0].getMemoryEffects(); - return A.manifestAttrs( - IRP, - Attribute::getWithMemoryEffects(IRP.getAnchorValue().getContext(), ME)); + return A.manifestAttrs(IRP, Attribute::getWithMemoryEffects( + IRP.getAnchorValue().getContext(), ME)); } /// See AAMemoryLocation::checkForAllAccessesToMemoryKind(...). @@ -12023,7 +12021,198 @@ AAUnderlyingObjectsFunction(const IRPosition &IRP, Attributor &A) : AAUnderlyingObjectsImpl(IRP, A) {} }; -} +} // namespace + +/// ------------------------ Address Space Propagation ------------------------- +namespace { +struct AAAddressSpaceImpl : public AAAddressSpace { + AAAddressSpaceImpl(const IRPosition &IRP, Attributor &A) + : AAAddressSpace(IRP, A) {} + + int32_t getAddressSpace() const override { + assert(isValidState() && "the AA is invalid"); + return AssumedAddressSpace; + } + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + assert(getAssociatedType()->isPtrOrPtrVectorTy() && + "Associated value is not a pointer"); + } + + ChangeStatus updateImpl(Attributor &A) override { + int32_t OldAddressSpace = AssumedAddressSpace; + auto *AUO = A.getOrCreateAAFor(getIRPosition(), this, + DepClassTy::OPTIONAL); + auto Pred = [&](Value &Obj) { + if (isa(&Obj)) + return true; + return takeAddressSpace(Obj.getType()->getPointerAddressSpace()); + }; + + if (!AUO->forallUnderlyingObjects(Pred)) + return indicatePessimisticFixpoint(); + + return OldAddressSpace == AssumedAddressSpace ? ChangeStatus::UNCHANGED + : ChangeStatus::CHANGED; + } + + /// See AbstractAttribute::manifest(...). + ChangeStatus manifest(Attributor &A) override { + Value *AssociatedValue = &getAssociatedValue(); + Value *OriginalValue = peelAddrspacecast(AssociatedValue); + if (getAddressSpace() == NoAddressSpace || + static_cast(getAddressSpace()) == + getAssociatedType()->getPointerAddressSpace()) + return ChangeStatus::UNCHANGED; + + Type *NewPtrTy = PointerType::getWithSamePointeeType( + cast(getAssociatedType()), + static_cast(getAddressSpace())); + + bool Changed = false; + + auto MakeChange = [&](Instruction *I, Use &U) { + Value *NewV = nullptr; + if (OriginalValue->getType()->getPointerAddressSpace() == + static_cast(getAddressSpace())) { + NewV = OriginalValue; + } else { + Instruction *CastInst = new AddrSpaceCastInst(OriginalValue, NewPtrTy); + CastInst->insertBefore(cast(I)); + NewV = CastInst; + } + A.changeUseAfterManifest(U, *NewV); + Changed = true; + }; + + auto Pred = [&](const Use &U, bool &) { + if (U.get() != AssociatedValue) + return true; + auto *Inst = dyn_cast(U.getUser()); + if (!Inst) + return true; + // This is a WA to make sure we only change uses from the corresponding + // CGSCC if the AA is run on CGSCC instead of the entire module. + if (!A.isRunOn(Inst->getFunction())) + return true; + if (isa(Inst)) { + MakeChange(Inst, const_cast(U)); + return true; + } + if (auto *SI = dyn_cast(Inst)) + if (SI->getPointerOperand() == AssociatedValue) { + MakeChange(Inst, const_cast(U)); + return true; + } + return true; + }; + + // It doesn't matter if we can't check all uses as we can simply + // conservatively ignore those that can not be visited. + (void)A.checkForAllUses(Pred, *this, getAssociatedValue(), + /* CheckBBLivenessOnly */ true); + + return Changed ? ChangeStatus::CHANGED : ChangeStatus::UNCHANGED; + } + + /// See AbstractAttribute::getAsStr(). + const std::string getAsStr(Attributor *A) const override { + if (!isValidState()) + return "addrspace()"; + return "addrspace(" + + (AssumedAddressSpace == NoAddressSpace + ? "none" + : std::to_string(AssumedAddressSpace)) + + ")"; + } + +private: + int32_t AssumedAddressSpace = NoAddressSpace; + + bool takeAddressSpace(uint32_t AS) { + int32_t Cast = static_cast(AS); + if (AssumedAddressSpace == NoAddressSpace) { + AssumedAddressSpace = Cast; + return true; + } + return AssumedAddressSpace == Cast; + } + + static Value *peelAddrspacecast(Value *V) { + if (auto *I = dyn_cast(V)) + return peelAddrspacecast(I->getPointerOperand()); + if (auto *C = dyn_cast(V)) + if (C->getOpcode() == Instruction::AddrSpaceCast) + return peelAddrspacecast(C->getOperand(0)); + return V; + } +}; + +struct AAAddressSpaceFloating final : AAAddressSpaceImpl { + AAAddressSpaceFloating(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceImpl(IRP, A) {} + + void trackStatistics() const override { + STATS_DECLTRACK_FLOATING_ATTR(addrspace); + } +}; + +struct AAAddressSpaceReturned final : AAAddressSpaceImpl { + AAAddressSpaceReturned(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceImpl(IRP, A) {} + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + // TODO: we don't rewrite function argument for now because it will need to + // rewrite the function signature and all call sites. + (void)indicatePessimisticFixpoint(); + } + + void trackStatistics() const override { + STATS_DECLTRACK_FNRET_ATTR(addrspace); + } +}; + +struct AAAddressSpaceCallSiteReturned final : AAAddressSpaceImpl { + AAAddressSpaceCallSiteReturned(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceImpl(IRP, A) {} + + void trackStatistics() const override { + STATS_DECLTRACK_CSRET_ATTR(addrspace); + } +}; + +struct AAAddressSpaceArgument final : AAAddressSpaceImpl { + AAAddressSpaceArgument(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceImpl(IRP, A) {} + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + // TODO: we don't rewrite function argument for now because it will need to + // rewrite the function signature and all call sites. + (void)indicatePessimisticFixpoint(); + } + + void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(addrspace); } +}; + +struct AAAddressSpaceCallSiteArgument final : AAAddressSpaceImpl { + AAAddressSpaceCallSiteArgument(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceImpl(IRP, A) {} + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + // TODO: we don't rewrite call site argument for now because it will need to + // rewrite the function signature of the callee. + (void)indicatePessimisticFixpoint(); + } + + void trackStatistics() const override { + STATS_DECLTRACK_CSARG_ATTR(addrspace); + } +}; +} // namespace const char AAReturnedValues::ID = 0; const char AANoUnwind::ID = 0; @@ -12058,6 +12247,7 @@ const char AAPointerInfo::ID = 0; const char AAAssumptionInfo::ID = 0; const char AAUnderlyingObjects::ID = 0; +const char AAAddressSpace::ID = 0; // Macro magic to create the static generator function for attributes that // follow the naming scheme. @@ -12176,6 +12366,7 @@ CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoUndef) CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFPClass) CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAPointerInfo) +CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAddressSpace) CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueSimplify) CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIsDead) diff --git a/llvm/test/Transforms/Attributor/address_space_info.ll b/llvm/test/Transforms/Attributor/address_space_info.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Attributor/address_space_info.ll @@ -0,0 +1,152 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals --prefix-filecheck-ir-name true +; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK + +@dst = dso_local addrspace(1) externally_initialized global i32 0, align 4 +@g1 = dso_local addrspace(1) externally_initialized global i32 0, align 4 +@g2 = dso_local addrspace(1) externally_initialized global i32 0, align 4 +@s1 = dso_local addrspace(3) global i32 undef, align 4 +@s2 = dso_local addrspace(3) global i32 undef, align 4 +@llvm.compiler.used = appending global [3 x ptr] [ptr addrspacecast (ptr addrspace(1) @dst to ptr), ptr addrspacecast (ptr addrspace(1) @g1 to ptr), ptr addrspacecast (ptr addrspace(1) @g2 to ptr)], section "llvm.metadata" + +; Function Attrs: convergent mustprogress noinline nounwind +;. +; CHECK: @dst = dso_local addrspace(1) externally_initialized global i32 0, align 4 +; CHECK: @g1 = dso_local addrspace(1) externally_initialized global i32 0, align 4 +; CHECK: @g2 = dso_local addrspace(1) externally_initialized global i32 0, align 4 +; CHECK: @s1 = dso_local addrspace(3) global i32 undef, align 4 +; CHECK: @s2 = dso_local addrspace(3) global i32 undef, align 4 +; CHECK: @llvm.compiler.used = appending global [3 x ptr] [ptr addrspacecast (ptr addrspace(1) @dst to ptr), ptr addrspacecast (ptr addrspace(1) @g1 to ptr), ptr addrspacecast (ptr addrspace(1) @g2 to ptr)], section "llvm.metadata" +;. +define internal void @_Z12global_writePi(ptr noundef %p) #0 { +; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write) +; CHECK-LABEL: define {{[^@]+}}@_Z12global_writePi +; CHECK-SAME: (ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i32 1, ptr [[P]], align 4 +; CHECK-NEXT: ret void +; +entry: + store i32 1, ptr %p, align 4 + ret void +} + +; Function Attrs: convergent mustprogress noinline nounwind +define internal void @_Z13unknown_writePi(ptr noundef %p) #0 { +; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write) +; CHECK-LABEL: define {{[^@]+}}@_Z13unknown_writePi +; CHECK-SAME: (ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i32 2, ptr [[P]], align 4 +; CHECK-NEXT: ret void +; +entry: + store i32 2, ptr %p, align 4 + ret void +} + +; Function Attrs: convergent mustprogress noinline nounwind +define internal void @_Z12shared_writePi(ptr noundef %p) #0 { +; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write) +; CHECK-LABEL: define {{[^@]+}}@_Z12shared_writePi +; CHECK-SAME: (ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: store i32 3, ptr [[P]], align 4 +; CHECK-NEXT: ret void +; +entry: + store i32 3, ptr %p, align 4 + ret void +} + +; Function Attrs: convergent mustprogress noinline nounwind +define internal void @_Z11global_readPi(ptr noundef %p) #0 { +; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn +; CHECK-LABEL: define {{[^@]+}}@_Z11global_readPi +; CHECK-SAME: (ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 4 +; CHECK-NEXT: store i32 [[TMP0]], ptr addrspace(1) @dst, align 4 +; CHECK-NEXT: ret void +; +entry: + %0 = load i32, ptr %p, align 4 + store i32 %0, ptr addrspacecast (ptr addrspace(1) @dst to ptr), align 4 + ret void +} + +; Function Attrs: convergent mustprogress noinline nounwind +define internal void @_Z12unknown_readPi(ptr noundef %p) #0 { +; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn +; CHECK-LABEL: define {{[^@]+}}@_Z12unknown_readPi +; CHECK-SAME: (ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 4 +; CHECK-NEXT: store i32 [[TMP0]], ptr addrspace(1) @dst, align 4 +; CHECK-NEXT: ret void +; +entry: + %0 = load i32, ptr %p, align 4 + store i32 %0, ptr addrspacecast (ptr addrspace(1) @dst to ptr), align 4 + ret void +} + +; Function Attrs: convergent mustprogress noinline nounwind +define internal void @_Z11shared_readPi(ptr noundef %p) #0 { +; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn +; CHECK-LABEL: define {{[^@]+}}@_Z11shared_readPi +; CHECK-SAME: (ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[P]], align 4 +; CHECK-NEXT: store i32 [[TMP0]], ptr addrspace(1) @dst, align 4 +; CHECK-NEXT: ret void +; +entry: + %0 = load i32, ptr %p, align 4 + store i32 %0, ptr addrspacecast (ptr addrspace(1) @dst to ptr), align 4 + ret void +} + +; Function Attrs: convergent mustprogress noinline nounwind +define dso_local void @_Z3bazv() #0 { +; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn +; CHECK-LABEL: define {{[^@]+}}@_Z3bazv +; CHECK-SAME: () #[[ATTR1]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @_Z12global_writePi(ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(1) @g1 to ptr)) #[[ATTR2:[0-9]+]] +; CHECK-NEXT: call void @_Z12global_writePi(ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(1) @g2 to ptr)) #[[ATTR2]] +; CHECK-NEXT: call void @_Z13unknown_writePi(ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(1) @g1 to ptr)) #[[ATTR2]] +; CHECK-NEXT: call void @_Z13unknown_writePi(ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(3) @s1 to ptr)) #[[ATTR2]] +; CHECK-NEXT: call void @_Z12shared_writePi(ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(3) @s1 to ptr)) #[[ATTR2]] +; CHECK-NEXT: call void @_Z12shared_writePi(ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(3) @s2 to ptr)) #[[ATTR2]] +; CHECK-NEXT: call void @_Z11global_readPi(ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(1) @g1 to ptr)) #[[ATTR3:[0-9]+]] +; CHECK-NEXT: call void @_Z11global_readPi(ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(1) @g2 to ptr)) #[[ATTR3]] +; CHECK-NEXT: call void @_Z12unknown_readPi(ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(1) @g1 to ptr)) #[[ATTR3]] +; CHECK-NEXT: call void @_Z12unknown_readPi(ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(3) @s1 to ptr)) #[[ATTR3]] +; CHECK-NEXT: call void @_Z11shared_readPi(ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(3) @s1 to ptr)) #[[ATTR3]] +; CHECK-NEXT: call void @_Z11shared_readPi(ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) addrspacecast (ptr addrspace(3) @s2 to ptr)) #[[ATTR3]] +; CHECK-NEXT: ret void +; +entry: + call void @_Z12global_writePi(ptr noundef addrspacecast (ptr addrspace(1) @g1 to ptr)) #1 + call void @_Z12global_writePi(ptr noundef addrspacecast (ptr addrspace(1) @g2 to ptr)) #1 + call void @_Z13unknown_writePi(ptr noundef addrspacecast (ptr addrspace(1) @g1 to ptr)) #1 + call void @_Z13unknown_writePi(ptr noundef addrspacecast (ptr addrspace(3) @s1 to ptr)) #1 + call void @_Z12shared_writePi(ptr noundef addrspacecast (ptr addrspace(3) @s1 to ptr)) #1 + call void @_Z12shared_writePi(ptr noundef addrspacecast (ptr addrspace(3) @s2 to ptr)) #1 + call void @_Z11global_readPi(ptr noundef addrspacecast (ptr addrspace(1) @g1 to ptr)) #1 + call void @_Z11global_readPi(ptr noundef addrspacecast (ptr addrspace(1) @g2 to ptr)) #1 + call void @_Z12unknown_readPi(ptr noundef addrspacecast (ptr addrspace(1) @g1 to ptr)) #1 + call void @_Z12unknown_readPi(ptr noundef addrspacecast (ptr addrspace(3) @s1 to ptr)) #1 + call void @_Z11shared_readPi(ptr noundef addrspacecast (ptr addrspace(3) @s1 to ptr)) #1 + call void @_Z11shared_readPi(ptr noundef addrspacecast (ptr addrspace(3) @s2 to ptr)) #1 + ret void +} + +attributes #0 = { convergent mustprogress noinline nounwind } +attributes #1 = { convergent nounwind } +;. +; CHECK: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write) } +; CHECK: attributes #[[ATTR1]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn } +; CHECK: attributes #[[ATTR2]] = { convergent nofree nosync nounwind willreturn memory(write) } +; CHECK: attributes #[[ATTR3]] = { convergent nofree nosync nounwind willreturn } +;. diff --git a/llvm/test/Transforms/Attributor/depgraph.ll b/llvm/test/Transforms/Attributor/depgraph.ll --- a/llvm/test/Transforms/Attributor/depgraph.ll +++ b/llvm/test/Transforms/Attributor/depgraph.ll @@ -230,6 +230,8 @@ ; GRAPH-EMPTY: ; GRAPH-NEXT: [AANoFree] for CtxI ' %6 = call ptr @checkAndAdvance(ptr %5)' at position {cs_arg: [@0]} with state nofree ; GRAPH-EMPTY: +; GRAPH-NEXT: [AAAddressSpace] for CtxI ' %2 = load i32, ptr %0, align 4' at position {arg: [@0]} with state addrspace() +; GRAPH-EMPTY: ; GRAPH-NEXT: [AADereferenceable] for CtxI ' %5 = getelementptr inbounds i32, ptr %0, i64 4' at position {flt: [@-1]} with state unknown-dereferenceable ; GRAPH-NOT: update @@ -308,6 +310,7 @@ ; DOT-DAG: Node[[Node71:0x[a-z0-9]+]] [shape=record,label="{[AANoAlias] ; DOT-DAG: Node[[Node72:0x[a-z0-9]+]] [shape=record,label="{[AANoAlias] ; DOT-DAG: Node[[Node73:0x[a-z0-9]+]] [shape=record,label="{[AANoFree] +; DOT-DAG: Node[[Node75:0x[a-z0-9]+]] [shape=record,label="{[AAAddressSpace] ; DOT-DAG: Node[[Node74:0x[a-z0-9]+]] [shape=record,label="{[AADereferenceable] ; DOT-DAG: Node[[Node20]] -> Node[[Node19]]; diff --git a/llvm/test/Transforms/Attributor/memory_locations_gpu.ll b/llvm/test/Transforms/Attributor/memory_locations_gpu.ll --- a/llvm/test/Transforms/Attributor/memory_locations_gpu.ll +++ b/llvm/test/Transforms/Attributor/memory_locations_gpu.ll @@ -29,7 +29,7 @@ ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: define {{[^@]+}}@test_const_as_global2 ; CHECK-SAME: () #[[ATTR1]] { -; CHECK-NEXT: [[L2:%.*]] = load i32, ptr addrspacecast (ptr addrspace(4) @G to ptr), align 4 +; CHECK-NEXT: [[L2:%.*]] = load i32, ptr addrspace(4) @G, align 4 ; CHECK-NEXT: ret i32 [[L2]] ; %l2 = load i32, ptr addrspacecast (ptr addrspace(4) @G to ptr) @@ -41,8 +41,7 @@ ; CHECK-LABEL: define {{[^@]+}}@test_const_as_call1 ; CHECK-SAME: () #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: [[P1:%.*]] = call ptr addrspace(4) @ptr_to_const() -; CHECK-NEXT: [[C1:%.*]] = addrspacecast ptr addrspace(4) [[P1]] to ptr -; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[C1]], align 4 +; CHECK-NEXT: [[L1:%.*]] = load i32, ptr addrspace(4) [[P1]], align 4 ; CHECK-NEXT: ret i32 [[L1]] ; %p1 = call ptr addrspace(4) @ptr_to_const() @@ -56,8 +55,7 @@ ; CHECK-LABEL: define {{[^@]+}}@test_const_as_call2 ; CHECK-SAME: () #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: [[P2:%.*]] = call ptr @ptr() -; CHECK-NEXT: [[C2:%.*]] = addrspacecast ptr [[P2]] to ptr addrspace(4) -; CHECK-NEXT: [[L2:%.*]] = load i32, ptr addrspace(4) [[C2]], align 4 +; CHECK-NEXT: [[L2:%.*]] = load i32, ptr [[P2]], align 4 ; CHECK-NEXT: ret i32 [[L2]] ; %p2 = call ptr @ptr() @@ -72,8 +70,7 @@ ; CHECK-LABEL: define {{[^@]+}}@test_shared_as_call1 ; CHECK-SAME: () #[[ATTR2]] { ; CHECK-NEXT: [[P1:%.*]] = call ptr addrspace(3) @ptr_to_shared() -; CHECK-NEXT: [[C1:%.*]] = addrspacecast ptr addrspace(3) [[P1]] to ptr -; CHECK-NEXT: [[L1:%.*]] = load i32, ptr [[C1]], align 4 +; CHECK-NEXT: [[L1:%.*]] = load i32, ptr addrspace(3) [[P1]], align 4 ; CHECK-NEXT: ret i32 [[L1]] ; %p1 = call ptr addrspace(3) @ptr_to_shared() @@ -87,8 +84,7 @@ ; CHECK-LABEL: define {{[^@]+}}@test_shared_as_call2 ; CHECK-SAME: () #[[ATTR2]] { ; CHECK-NEXT: [[P2:%.*]] = call ptr @ptr() -; CHECK-NEXT: [[C2:%.*]] = addrspacecast ptr [[P2]] to ptr addrspace(3) -; CHECK-NEXT: [[L2:%.*]] = load i32, ptr addrspace(3) [[C2]], align 4 +; CHECK-NEXT: [[L2:%.*]] = load i32, ptr [[P2]], align 4 ; CHECK-NEXT: ret i32 [[L2]] ; %p2 = call ptr @ptr() diff --git a/llvm/test/Transforms/Attributor/nocapture-1.ll b/llvm/test/Transforms/Attributor/nocapture-1.ll --- a/llvm/test/Transforms/Attributor/nocapture-1.ll +++ b/llvm/test/Transforms/Attributor/nocapture-1.ll @@ -257,7 +257,7 @@ ; TUNIT-NEXT: [[TMP:%.*]] = addrspacecast ptr addrspace(1) [[P]] to ptr ; TUNIT-NEXT: [[TMP2:%.*]] = select i1 [[B]], ptr [[TMP]], ptr [[Q]] ; TUNIT-NEXT: [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4 -; TUNIT-NEXT: store i32 0, ptr [[TMP]], align 4 +; TUNIT-NEXT: store i32 0, ptr addrspace(1) [[P]], align 4 ; TUNIT-NEXT: store ptr [[Q]], ptr @g, align 8 ; TUNIT-NEXT: ret i32 [[VAL]] ; @@ -272,7 +272,7 @@ ; CGSCC-NEXT: [[TMP:%.*]] = addrspacecast ptr addrspace(1) [[P]] to ptr ; CGSCC-NEXT: [[TMP2:%.*]] = select i1 [[B]], ptr [[TMP]], ptr [[Q]] ; CGSCC-NEXT: [[VAL:%.*]] = load i32, ptr [[TMP2]], align 4 -; CGSCC-NEXT: store i32 0, ptr [[TMP]], align 4 +; CGSCC-NEXT: store i32 0, ptr addrspace(1) [[P]], align 4 ; CGSCC-NEXT: store ptr [[Q]], ptr @g, align 8 ; CGSCC-NEXT: ret i32 [[VAL]] ; diff --git a/llvm/test/Transforms/Attributor/value-simplify-gpu.ll b/llvm/test/Transforms/Attributor/value-simplify-gpu.ll --- a/llvm/test/Transforms/Attributor/value-simplify-gpu.ll +++ b/llvm/test/Transforms/Attributor/value-simplify-gpu.ll @@ -95,7 +95,7 @@ ; CHECK-SAME: () #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i32 1, ptr @ReachableKernelAS0, align 4 -; CHECK-NEXT: store i32 1, ptr addrspacecast (ptr addrspace(3) @ReachableKernel to ptr), align 4 +; CHECK-NEXT: store i32 1, ptr addrspace(3) @ReachableKernel, align 4 ; CHECK-NEXT: ret void ; entry: @@ -109,9 +109,9 @@ ; TUNIT-LABEL: define {{[^@]+}}@level2Kernela ; TUNIT-SAME: () #[[ATTR1]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @ReachableKernel to ptr), align 4 +; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(3) @ReachableKernel, align 4 ; TUNIT-NEXT: [[TMP1:%.*]] = load i32, ptr @ReachableKernelAS0, align 4 -; TUNIT-NEXT: [[TMP2:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @UnreachableKernel to ptr), align 4 +; TUNIT-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(3) @UnreachableKernel, align 4 ; TUNIT-NEXT: call void @use(i32 noundef [[TMP0]], i32 noundef [[TMP1]], i32 noundef [[TMP2]]) #[[ATTR6:[0-9]+]] ; TUNIT-NEXT: ret void ; @@ -119,9 +119,9 @@ ; CGSCC-LABEL: define {{[^@]+}}@level2Kernela ; CGSCC-SAME: () #[[ATTR3:[0-9]+]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @ReachableKernel to ptr), align 4 +; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(3) @ReachableKernel, align 4 ; CGSCC-NEXT: [[TMP1:%.*]] = load i32, ptr @ReachableKernelAS0, align 4 -; CGSCC-NEXT: [[TMP2:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @UnreachableKernel to ptr), align 4 +; CGSCC-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(3) @UnreachableKernel, align 4 ; CGSCC-NEXT: call void @use(i32 noundef [[TMP0]], i32 noundef [[TMP1]], i32 noundef [[TMP2]]) #[[ATTR4]] ; CGSCC-NEXT: ret void ; @@ -138,9 +138,9 @@ ; TUNIT-LABEL: define {{[^@]+}}@level2Kernelb ; TUNIT-SAME: () #[[ATTR1]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @ReachableKernel to ptr), align 4 +; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(3) @ReachableKernel, align 4 ; TUNIT-NEXT: [[TMP1:%.*]] = load i32, ptr @ReachableKernelAS0, align 4 -; TUNIT-NEXT: [[TMP2:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @UnreachableKernel to ptr), align 4 +; TUNIT-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(3) @UnreachableKernel, align 4 ; TUNIT-NEXT: call void @use(i32 noundef [[TMP0]], i32 noundef [[TMP1]], i32 noundef [[TMP2]]) #[[ATTR6]] ; TUNIT-NEXT: ret void ; @@ -148,9 +148,9 @@ ; CGSCC-LABEL: define {{[^@]+}}@level2Kernelb ; CGSCC-SAME: () #[[ATTR3]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @ReachableKernel to ptr), align 4 +; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(3) @ReachableKernel, align 4 ; CGSCC-NEXT: [[TMP1:%.*]] = load i32, ptr @ReachableKernelAS0, align 4 -; CGSCC-NEXT: [[TMP2:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @UnreachableKernel to ptr), align 4 +; CGSCC-NEXT: [[TMP2:%.*]] = load i32, ptr addrspace(3) @UnreachableKernel, align 4 ; CGSCC-NEXT: call void @use(i32 noundef [[TMP0]], i32 noundef [[TMP1]], i32 noundef [[TMP2]]) #[[ATTR4]] ; CGSCC-NEXT: ret void ; @@ -167,7 +167,7 @@ ; CHECK-LABEL: define {{[^@]+}}@level2Kernelall_late ; CHECK-SAME: () #[[ATTR2]] { ; CHECK-NEXT: entry: -; CHECK-NEXT: store i32 1, ptr addrspacecast (ptr addrspace(3) @UnreachableKernel to ptr), align 4 +; CHECK-NEXT: store i32 1, ptr addrspace(3) @UnreachableKernel, align 4 ; CHECK-NEXT: ret void ; entry: @@ -259,14 +259,14 @@ ; TUNIT-LABEL: define {{[^@]+}}@level2all_early ; TUNIT-SAME: (ptr noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: store i32 1, ptr addrspacecast (ptr addrspace(3) @ReachableNonKernel to ptr), align 4 +; TUNIT-NEXT: store i32 1, ptr addrspace(3) @ReachableNonKernel, align 4 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) ; CGSCC-LABEL: define {{[^@]+}}@level2all_early ; CGSCC-SAME: (ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: store i32 1, ptr addrspacecast (ptr addrspace(3) @ReachableNonKernel to ptr), align 4 +; CGSCC-NEXT: store i32 1, ptr addrspace(3) @ReachableNonKernel, align 4 ; CGSCC-NEXT: store i32 17, ptr [[ADDR]], align 4 ; CGSCC-NEXT: ret void ; @@ -281,8 +281,8 @@ ; TUNIT-LABEL: define {{[^@]+}}@level2a ; TUNIT-SAME: () #[[ATTR1]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @ReachableNonKernel to ptr), align 4 -; TUNIT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @UnreachableNonKernel to ptr), align 4 +; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(3) @ReachableNonKernel, align 4 +; TUNIT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(3) @UnreachableNonKernel, align 4 ; TUNIT-NEXT: call void @use(i32 noundef [[TMP0]], i32 noundef [[TMP1]], i32 17) #[[ATTR6]] ; TUNIT-NEXT: ret void ; @@ -290,8 +290,8 @@ ; CGSCC-LABEL: define {{[^@]+}}@level2a ; CGSCC-SAME: (ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR3]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @ReachableNonKernel to ptr), align 4 -; CGSCC-NEXT: [[TMP1:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @UnreachableNonKernel to ptr), align 4 +; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(3) @ReachableNonKernel, align 4 +; CGSCC-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(3) @UnreachableNonKernel, align 4 ; CGSCC-NEXT: [[QQQQ2:%.*]] = load i32, ptr [[ADDR]], align 4 ; CGSCC-NEXT: call void @use(i32 noundef [[TMP0]], i32 noundef [[TMP1]], i32 [[QQQQ2]]) #[[ATTR4]] ; CGSCC-NEXT: ret void @@ -309,8 +309,8 @@ ; TUNIT-LABEL: define {{[^@]+}}@level2b ; TUNIT-SAME: () #[[ATTR1]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @ReachableNonKernel to ptr), align 4 -; TUNIT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @UnreachableNonKernel to ptr), align 4 +; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(3) @ReachableNonKernel, align 4 +; TUNIT-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(3) @UnreachableNonKernel, align 4 ; TUNIT-NEXT: call void @use(i32 noundef [[TMP0]], i32 noundef [[TMP1]], i32 17) #[[ATTR6]] ; TUNIT-NEXT: ret void ; @@ -318,8 +318,8 @@ ; CGSCC-LABEL: define {{[^@]+}}@level2b ; CGSCC-SAME: (ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR3]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @ReachableNonKernel to ptr), align 4 -; CGSCC-NEXT: [[TMP1:%.*]] = load i32, ptr addrspacecast (ptr addrspace(3) @UnreachableNonKernel to ptr), align 4 +; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr addrspace(3) @ReachableNonKernel, align 4 +; CGSCC-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(3) @UnreachableNonKernel, align 4 ; CGSCC-NEXT: [[TMP2:%.*]] = load i32, ptr [[ADDR]], align 4 ; CGSCC-NEXT: call void @use(i32 noundef [[TMP0]], i32 noundef [[TMP1]], i32 [[TMP2]]) #[[ATTR4]] ; CGSCC-NEXT: ret void @@ -337,14 +337,14 @@ ; TUNIT-LABEL: define {{[^@]+}}@level2all_late ; TUNIT-SAME: (ptr noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { ; TUNIT-NEXT: entry: -; TUNIT-NEXT: store i32 1, ptr addrspacecast (ptr addrspace(3) @UnreachableNonKernel to ptr), align 4 +; TUNIT-NEXT: store i32 1, ptr addrspace(3) @UnreachableNonKernel, align 4 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) ; CGSCC-LABEL: define {{[^@]+}}@level2all_late ; CGSCC-SAME: (ptr noalias nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[ADDR:%.*]]) #[[ATTR2]] { ; CGSCC-NEXT: entry: -; CGSCC-NEXT: store i32 1, ptr addrspacecast (ptr addrspace(3) @UnreachableNonKernel to ptr), align 4 +; CGSCC-NEXT: store i32 1, ptr addrspace(3) @UnreachableNonKernel, align 4 ; CGSCC-NEXT: store i32 5, ptr [[ADDR]], align 4 ; CGSCC-NEXT: ret void ; diff --git a/llvm/test/Transforms/Attributor/value-simplify.ll b/llvm/test/Transforms/Attributor/value-simplify.ll --- a/llvm/test/Transforms/Attributor/value-simplify.ll +++ b/llvm/test/Transforms/Attributor/value-simplify.ll @@ -800,7 +800,8 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(write) ; TUNIT-LABEL: define {{[^@]+}}@user ; TUNIT-SAME: () #[[ATTR4]] { -; TUNIT-NEXT: store i32 0, ptr addrspacecast (ptr addrspace(3) @ConstAS3Ptr to ptr), align 4 +; TUNIT-NEXT: [[TMP1:%.*]] = addrspacecast ptr addrspacecast (ptr addrspace(3) @ConstAS3Ptr to ptr) to ptr addrspace(3) +; TUNIT-NEXT: store i32 0, ptr addrspace(3) [[TMP1]], align 4 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write)