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 @@ -5973,6 +5973,40 @@ AA::ValueScope Scope = AA::Interprocedural) const = 0; }; +/// An abstract interface for address space information. +struct AAAddressSpaceInfo + : public StateWrapper { + AAAddressSpaceInfo(const IRPosition &IRP, Attributor &A) + : StateWrapper(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 int64_t getAddressSpace() const = 0; + + /// Create an abstract attribute view for the position \p IRP. + static AAAddressSpaceInfo &createForPosition(const IRPosition &IRP, + Attributor &A); + + /// See AbstractAttribute::getName() + const std::string getName() const override { return "AAAddressSpaceInfo"; } + + /// 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 int64_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 @@ -3502,18 +3502,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 @@ -187,6 +187,7 @@ PIPE_OPERATOR(AAPointerInfo) PIPE_OPERATOR(AAAssumptionInfo) PIPE_OPERATOR(AAUnderlyingObjects) +PIPE_OPERATOR(AAAddressSpaceInfo) #undef PIPE_OPERATOR @@ -11856,6 +11857,162 @@ }; } +/// ------------------------ Address Space Propagation ------------------------- +namespace { +struct AAAddressSpaceInfoImpl : public AAAddressSpaceInfo { + AAAddressSpaceInfoImpl(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceInfo(IRP, A) {} + + int64_t getAddressSpace() const override { + assert(isValidState() && "the AA is invalid"); + return AssumedAddressSpace; + } + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + assert(getAssociatedType()->isPointerTy() && + "Associated value is not a pointer"); + A.getAAFor(*this, getIRPosition(), DepClassTy::OPTIONAL); + } + + ChangeStatus updateImpl(Attributor &A) override { + auto *AUO = A.getOrCreateAAFor(getIRPosition(), this, + DepClassTy::OPTIONAL); + if (!AUO->getState().isValidState()) + return takeAddressSpace(getAssociatedType()->getPointerAddressSpace()) + ? ChangeStatus::CHANGED + : ChangeStatus::UNCHANGED; + + int64_t OldAddressSpace = AssumedAddressSpace; + + auto Pred = [&](Value &Obj) { + if (isa(&Obj)) + return true; + uint32_t AS = Obj.getType()->getPointerAddressSpace(); + if (AssumedAddressSpace == NoAddressSpace) { + (void)takeAddressSpace(AS); + return true; + } + assert(AssumedAddressSpace >= 0 && + "AssumedAddressSpace should not be negative"); + return AS == static_cast(AssumedAddressSpace); + }; + + if (!AUO->forallUnderlyingObjects(Pred)) + return indicatePessimisticFixpoint(); + + return OldAddressSpace == AssumedAddressSpace ? ChangeStatus::UNCHANGED + : ChangeStatus::CHANGED; + } + + /// See AbstractAttribute::manifest(...). + ChangeStatus manifest(Attributor &A) override { + Instruction *CtxI = getCtxI(); + Value *AssociatedValue = &getAssociatedValue(); + if (!CtxI || getAddressSpace() == NoAddressSpace || + static_cast(getAddressSpace()) == + getAssociatedType()->getPointerAddressSpace()) + return ChangeStatus::UNCHANGED; + + SmallVector WorkList; + auto Pred = [&](const Use &U, bool &) { + if (U.get() != &getAssociatedValue()) + return true; + User *Usr = U.getUser(); + if (isa(Usr) || isa(U)) + WorkList.push_back(const_cast(&U)); + 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); + + if (WorkList.empty()) + return ChangeStatus::UNCHANGED; + + uint32_t AS = static_cast(getAddressSpace()); + Type *NewPtrTy = PointerType::getWithSamePointeeType( + cast(getAssociatedType()), AS); + Instruction *CastInst = new AddrSpaceCastInst(AssociatedValue, NewPtrTy); + CastInst->insertAfter(CtxI); + for (auto *U : WorkList) + A.changeUseAfterManifest(*U, *CastInst); + + return ChangeStatus::CHANGED; + } + + /// See AbstractAttribute::getAsStr(). + const std::string getAsStr() const override { + return "addrspace(" + + (AssumedAddressSpace == NoAddressSpace + ? "none" + : std::to_string(AssumedAddressSpace)) + + ")"; + } + +private: + // We use an int64_t to make sure the cast from uint32_t does not overflow. + int64_t AssumedAddressSpace = NoAddressSpace; + + bool takeAddressSpace(uint32_t AS) { + int64_t OldAddressSpace = AssumedAddressSpace; + AssumedAddressSpace = static_cast(AS); + return OldAddressSpace != AssumedAddressSpace; + } +}; + +struct AAAddressSpaceInfoFloating final : AAAddressSpaceInfoImpl { + AAAddressSpaceInfoFloating(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceInfoImpl(IRP, A) {} + + void trackStatistics() const override { + STATS_DECLTRACK_FLOATING_ATTR(addrspace); + } +}; + +struct AAAddressSpaceInfoReturned final : AAAddressSpaceInfoImpl { + AAAddressSpaceInfoReturned(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceInfoImpl(IRP, A) {} + + void trackStatistics() const override { + STATS_DECLTRACK_FNRET_ATTR(addrspace); + } +}; + +struct AAAddressSpaceInfoCallSiteReturned final : AAAddressSpaceInfoImpl { + AAAddressSpaceInfoCallSiteReturned(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceInfoImpl(IRP, A) {} + + void trackStatistics() const override { + STATS_DECLTRACK_CSRET_ATTR(addrspace); + } +}; + +struct AAAddressSpaceInfoArgument final : AAAddressSpaceInfoImpl { + AAAddressSpaceInfoArgument(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceInfoImpl(IRP, A) {} + + void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(addrspace); } +}; + +struct AAAddressSpaceInfoCallSiteArgument final : AAAddressSpaceInfoImpl { + AAAddressSpaceInfoCallSiteArgument(const IRPosition &IRP, Attributor &A) + : AAAddressSpaceInfoImpl(IRP, A) {} + + // We don't rewrite call site argument for now because it will need to rewrite + // the function signature of the callee. + ChangeStatus updateImpl(Attributor &A) override { + return ChangeStatus::UNCHANGED; + }; + + void trackStatistics() const override { + STATS_DECLTRACK_CSARG_ATTR(addrspace); + } +}; +} // namespace + const char AAReturnedValues::ID = 0; const char AANoUnwind::ID = 0; const char AANoSync::ID = 0; @@ -11889,6 +12046,7 @@ const char AAPointerInfo::ID = 0; const char AAAssumptionInfo::ID = 0; const char AAUnderlyingObjects::ID = 0; +const char AAAddressSpaceInfo::ID = 0; // Macro magic to create the static generator function for attributes that // follow the naming scheme. @@ -12007,6 +12165,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(AAAddressSpaceInfo) CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueSimplify) CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAIsDead) diff --git a/llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll b/llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll --- a/llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll +++ b/llvm/test/Transforms/Attributor/IPConstantProp/PR26044.ll @@ -77,11 +77,10 @@ ; TUNIT: for.cond1: ; TUNIT-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]] ; TUNIT: if.end: -; TUNIT-NEXT: [[E_2:%.*]] = phi ptr [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ] ; TUNIT-NEXT: [[TMP0:%.*]] = load i32, ptr null, align 4294967296 ; TUNIT-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]]) #[[ATTR3]] ; TUNIT-NEXT: store i32 [[CALL]], ptr [[P]], align 4 -; TUNIT-NEXT: br label [[FOR_COND1]] +; TUNIT-NEXT: br label [[FOR_COND1:%.*]] ; TUNIT: exit: ; TUNIT-NEXT: ret void ; @@ -93,11 +92,10 @@ ; CGSCC: for.cond1: ; CGSCC-NEXT: br i1 [[C]], label [[IF_END]], label [[EXIT:%.*]] ; CGSCC: if.end: -; CGSCC-NEXT: [[E_2:%.*]] = phi ptr [ undef, [[ENTRY:%.*]] ], [ null, [[FOR_COND1:%.*]] ] ; CGSCC-NEXT: [[TMP0:%.*]] = load i32, ptr null, align 4294967296 ; CGSCC-NEXT: [[CALL:%.*]] = call i32 @fn0(i32 [[TMP0]]) #[[ATTR3]] ; CGSCC-NEXT: store i32 [[CALL]], ptr [[P]], align 4 -; CGSCC-NEXT: br label [[FOR_COND1]] +; CGSCC-NEXT: br label [[FOR_COND1:%.*]] ; CGSCC: exit: ; CGSCC-NEXT: ret void ; 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,299 @@ +; 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 -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK +; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK + +define float @load_global_from_flat(float* %generic_scalar) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; CHECK-LABEL: define {{[^@]+}}@load_global_from_flat +; CHECK-SAME: (ptr nocapture nofree readonly align 4 dereferenceable_or_null(4) [[GENERIC_SCALAR:%.*]]) #[[ATTR0:[0-9]+]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr [[GENERIC_SCALAR]] to ptr addrspace(1) +; CHECK-NEXT: [[TRUETMP1:%.*]] = load float, ptr addrspace(1) [[TRUETMP0]], align 4 +; CHECK-NEXT: ret float [[TRUETMP1]] +; + %tmp0 = addrspacecast float* %generic_scalar to float addrspace(1)* + %tmp1 = load float, float addrspace(1)* %tmp0 + ret float %tmp1 +} + +define float @load_constant_from_flat(float* %generic_scalar) #0 { +; +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; CHECK-LABEL: define {{[^@]+}}@load_constant_from_flat +; CHECK-SAME: (ptr nocapture nofree readonly align 4 dereferenceable_or_null(4) [[GENERIC_SCALAR:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr [[GENERIC_SCALAR]] to ptr addrspace(4) +; CHECK-NEXT: [[TRUETMP1:%.*]] = load float, ptr addrspace(4) [[TRUETMP0]], align 4 +; CHECK-NEXT: ret float [[TRUETMP1]] +; + %tmp0 = addrspacecast float* %generic_scalar to float addrspace(4)* + %tmp1 = load float, float addrspace(4)* %tmp0 + ret float %tmp1 +} + +define float @load_group_from_flat(float* %generic_scalar) #0 { +; +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; CHECK-LABEL: define {{[^@]+}}@load_group_from_flat +; CHECK-SAME: (ptr nocapture nofree readonly align 4 dereferenceable_or_null(4) [[GENERIC_SCALAR:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr [[GENERIC_SCALAR]] to ptr addrspace(3) +; CHECK-NEXT: [[TRUETMP1:%.*]] = load float, ptr addrspace(3) [[TRUETMP0]], align 4 +; CHECK-NEXT: ret float [[TRUETMP1]] +; + %tmp0 = addrspacecast float* %generic_scalar to float addrspace(3)* + %tmp1 = load float, float addrspace(3)* %tmp0 + ret float %tmp1 +} + +define float @load_private_from_flat(float* %generic_scalar) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) +; CHECK-LABEL: define {{[^@]+}}@load_private_from_flat +; CHECK-SAME: (ptr nocapture nofree readonly align 4 dereferenceable_or_null(4) [[GENERIC_SCALAR:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr [[GENERIC_SCALAR]] to ptr addrspace(5) +; CHECK-NEXT: [[TRUETMP1:%.*]] = load float, ptr addrspace(5) [[TRUETMP0]], align 4 +; CHECK-NEXT: ret float [[TRUETMP1]] +; + %tmp0 = addrspacecast float* %generic_scalar to float addrspace(5)* + %tmp1 = load float, float addrspace(5)* %tmp0 + ret float %tmp1 +} + +define void @store_global_from_flat(float* %generic_scalar) #0 { +; +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; CHECK-LABEL: define {{[^@]+}}@store_global_from_flat +; CHECK-SAME: (ptr nocapture nofree writeonly align 4 dereferenceable_or_null(4) [[GENERIC_SCALAR:%.*]]) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr [[GENERIC_SCALAR]] to ptr addrspace(1) +; CHECK-NEXT: store float 0.000000e+00, ptr addrspace(1) [[TRUETMP0]], align 4 +; CHECK-NEXT: ret void +; + %tmp0 = addrspacecast float* %generic_scalar to float addrspace(1)* + store float 0.0, float addrspace(1)* %tmp0 + ret void +} + +define void @store_group_from_flat(float* %generic_scalar) #0 { +; +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; CHECK-LABEL: define {{[^@]+}}@store_group_from_flat +; CHECK-SAME: (ptr nocapture nofree writeonly align 4 dereferenceable_or_null(4) [[GENERIC_SCALAR:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr [[GENERIC_SCALAR]] to ptr addrspace(3) +; CHECK-NEXT: store float 0.000000e+00, ptr addrspace(3) [[TRUETMP0]], align 4 +; CHECK-NEXT: ret void +; + %tmp0 = addrspacecast float* %generic_scalar to float addrspace(3)* + store float 0.0, float addrspace(3)* %tmp0 + ret void +} + +define void @store_private_from_flat(float* %generic_scalar) #0 { +; +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; CHECK-LABEL: define {{[^@]+}}@store_private_from_flat +; CHECK-SAME: (ptr nocapture nofree writeonly align 4 dereferenceable_or_null(4) [[GENERIC_SCALAR:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr [[GENERIC_SCALAR]] to ptr addrspace(5) +; CHECK-NEXT: store float 0.000000e+00, ptr addrspace(5) [[TRUETMP0]], align 4 +; CHECK-NEXT: ret void +; + %tmp0 = addrspacecast float* %generic_scalar to float addrspace(5)* + store float 0.0, float addrspace(5)* %tmp0 + ret void +} + +; optimized to global load/store. + +define void @load_store_global(i32 addrspace(1)* nocapture %input, i32 addrspace(1)* nocapture %output) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@load_store_global +; CHECK-SAME: (ptr addrspace(1) nocapture nofree nonnull readonly align 4 dereferenceable(4) [[INPUT:%.*]], ptr addrspace(1) nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[OUTPUT:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[CAST0:%.*]] = addrspacecast ptr addrspace(1) [[INPUT]] to ptr +; CHECK-NEXT: [[CAST1:%.*]] = addrspacecast ptr addrspace(1) [[OUTPUT]] to ptr +; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[CAST0]], align 4 +; CHECK-NEXT: store i32 [[VAL]], ptr [[CAST1]], align 4 +; CHECK-NEXT: ret void +; + %cast0 = addrspacecast i32 addrspace(1)* %input to i32* + %cast1 = addrspacecast i32 addrspace(1)* %output to i32* + %val = load i32, i32* %cast0, align 4 + store i32 %val, i32* %cast1, align 4 + ret void +} + +; Optimized to group load/store. + +define void @load_store_group(i32 addrspace(3)* nocapture %input, i32 addrspace(3)* nocapture %output) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@load_store_group +; CHECK-SAME: (ptr addrspace(3) nocapture nofree nonnull readonly align 4 dereferenceable(4) [[INPUT:%.*]], ptr addrspace(3) nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[OUTPUT:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr addrspace(3) [[INPUT]] to ptr +; CHECK-NEXT: [[TRUETMP1:%.*]] = addrspacecast ptr addrspace(3) [[OUTPUT]] to ptr +; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[TRUETMP0]], align 4 +; CHECK-NEXT: store i32 [[VAL]], ptr [[TRUETMP1]], align 4 +; CHECK-NEXT: ret void +; + %tmp0 = addrspacecast i32 addrspace(3)* %input to i32* + %tmp1 = addrspacecast i32 addrspace(3)* %output to i32* + %val = load i32, i32* %tmp0, align 4 + store i32 %val, i32* %tmp1, align 4 + ret void +} + +; Optimized to private load/store. + +define void @load_store_private(i32 addrspace(5)* nocapture %input, i32 addrspace(5)* nocapture %output) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@load_store_private +; CHECK-SAME: (ptr addrspace(5) nocapture nofree nonnull readonly align 4 dereferenceable(4) [[INPUT:%.*]], ptr addrspace(5) nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[OUTPUT:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[TRUETMP0:%.*]] = addrspacecast ptr addrspace(5) [[INPUT]] to ptr +; CHECK-NEXT: [[TRUETMP1:%.*]] = addrspacecast ptr addrspace(5) [[OUTPUT]] to ptr +; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[TRUETMP0]], align 4 +; CHECK-NEXT: store i32 [[VAL]], ptr [[TRUETMP1]], align 4 +; CHECK-NEXT: ret void +; + %tmp0 = addrspacecast i32 addrspace(5)* %input to i32* + %tmp1 = addrspacecast i32 addrspace(5)* %output to i32* + %val = load i32, i32* %tmp0, align 4 + store i32 %val, i32* %tmp1, align 4 + ret void +} + +; No optimization. flat load/store. + +define void @load_store_flat(i32* nocapture %input, i32* nocapture %output) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@load_store_flat +; CHECK-SAME: (ptr nocapture nofree noundef nonnull readonly align 4 dereferenceable(4) [[INPUT:%.*]], ptr nocapture nofree noundef nonnull writeonly align 4 dereferenceable(4) [[OUTPUT:%.*]]) #[[ATTR2]] { +; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[INPUT]], align 4 +; CHECK-NEXT: store i32 [[VAL]], ptr [[OUTPUT]], align 4 +; CHECK-NEXT: ret void +; + %val = load i32, i32* %input, align 4 + store i32 %val, i32* %output, align 4 + ret void +} + +define void @store_addrspacecast_ptr_value(i32 addrspace(1)* nocapture %input, i32* addrspace(1)* nocapture %output) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; CHECK-LABEL: define {{[^@]+}}@store_addrspacecast_ptr_value +; CHECK-SAME: (ptr addrspace(1) nocapture nofree writeonly [[INPUT:%.*]], ptr addrspace(1) nocapture nofree noundef writeonly align 4 dereferenceable_or_null(8) [[OUTPUT:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: [[CAST:%.*]] = addrspacecast ptr addrspace(1) [[INPUT]] to ptr +; CHECK-NEXT: store ptr [[CAST]], ptr addrspace(1) [[OUTPUT]], align 4 +; CHECK-NEXT: ret void +; + %cast = addrspacecast i32 addrspace(1)* %input to i32* + store i32* %cast, i32* addrspace(1)* %output, align 4 + ret void +} + +define i32 @atomicrmw_add_global_to_flat(i32 addrspace(1)* %global.ptr, i32 %y) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@atomicrmw_add_global_to_flat +; CHECK-SAME: (ptr addrspace(1) nocapture nofree nonnull dereferenceable(4) [[GLOBAL_PTR:%.*]], i32 [[Y:%.*]]) #[[ATTR3:[0-9]+]] { +; CHECK-NEXT: [[CAST:%.*]] = addrspacecast ptr addrspace(1) [[GLOBAL_PTR]] to ptr +; CHECK-NEXT: [[RET:%.*]] = atomicrmw add ptr [[CAST]], i32 [[Y]] seq_cst, align 4 +; CHECK-NEXT: ret i32 [[RET]] +; + %cast = addrspacecast i32 addrspace(1)* %global.ptr to i32* + %ret = atomicrmw add i32* %cast, i32 %y seq_cst + ret i32 %ret +} + +define i32 @atomicrmw_add_group_to_flat(i32 addrspace(3)* %group.ptr, i32 %y) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@atomicrmw_add_group_to_flat +; CHECK-SAME: (ptr addrspace(3) nocapture nofree nonnull dereferenceable(4) [[GROUP_PTR:%.*]], i32 [[Y:%.*]]) #[[ATTR3]] { +; CHECK-NEXT: [[CAST:%.*]] = addrspacecast ptr addrspace(3) [[GROUP_PTR]] to ptr +; CHECK-NEXT: [[RET:%.*]] = atomicrmw add ptr [[CAST]], i32 [[Y]] seq_cst, align 4 +; CHECK-NEXT: ret i32 [[RET]] +; + %cast = addrspacecast i32 addrspace(3)* %group.ptr to i32* + %ret = atomicrmw add i32* %cast, i32 %y seq_cst + ret i32 %ret +} + +define { i32, i1 } @cmpxchg_global_to_flat(i32 addrspace(1)* %global.ptr, i32 %cmp, i32 %val) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@cmpxchg_global_to_flat +; CHECK-SAME: (ptr addrspace(1) nocapture nofree nonnull dereferenceable(4) [[GLOBAL_PTR:%.*]], i32 [[CMP:%.*]], i32 [[VAL:%.*]]) #[[ATTR3]] { +; CHECK-NEXT: [[CAST:%.*]] = addrspacecast ptr addrspace(1) [[GLOBAL_PTR]] to ptr +; CHECK-NEXT: [[RET:%.*]] = cmpxchg ptr [[CAST]], i32 [[CMP]], i32 [[VAL]] seq_cst monotonic, align 4 +; CHECK-NEXT: ret { i32, i1 } [[RET]] +; + %cast = addrspacecast i32 addrspace(1)* %global.ptr to i32* + %ret = cmpxchg i32* %cast, i32 %cmp, i32 %val seq_cst monotonic + ret { i32, i1 } %ret +} + +define { i32, i1 } @cmpxchg_group_to_flat(i32 addrspace(3)* %group.ptr, i32 %cmp, i32 %val) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@cmpxchg_group_to_flat +; CHECK-SAME: (ptr addrspace(3) nocapture nofree nonnull dereferenceable(4) [[GROUP_PTR:%.*]], i32 [[CMP:%.*]], i32 [[VAL:%.*]]) #[[ATTR3]] { +; CHECK-NEXT: [[CAST:%.*]] = addrspacecast ptr addrspace(3) [[GROUP_PTR]] to ptr +; CHECK-NEXT: [[RET:%.*]] = cmpxchg ptr [[CAST]], i32 [[CMP]], i32 [[VAL]] seq_cst monotonic, align 4 +; CHECK-NEXT: ret { i32, i1 } [[RET]] +; + %cast = addrspacecast i32 addrspace(3)* %group.ptr to i32* + %ret = cmpxchg i32* %cast, i32 %cmp, i32 %val seq_cst monotonic + ret { i32, i1 } %ret +} + +; Not pointer operand +define { i32*, i1 } @cmpxchg_group_to_flat_wrong_operand(i32* addrspace(3)* %cas.ptr, i32 addrspace(3)* %cmp.ptr, i32* %val) #0 { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) +; CHECK-LABEL: define {{[^@]+}}@cmpxchg_group_to_flat_wrong_operand +; CHECK-SAME: (ptr addrspace(3) nocapture nofree noundef dereferenceable_or_null(8) [[CAS_PTR:%.*]], ptr addrspace(3) nofree [[CMP_PTR:%.*]], ptr nofree [[VAL:%.*]]) #[[ATTR3]] { +; CHECK-NEXT: [[CAST_CMP:%.*]] = addrspacecast ptr addrspace(3) [[CMP_PTR]] to ptr +; CHECK-NEXT: [[RET:%.*]] = cmpxchg ptr addrspace(3) [[CAS_PTR]], ptr [[CAST_CMP]], ptr [[VAL]] seq_cst monotonic, align 8 +; CHECK-NEXT: ret { ptr, i1 } [[RET]] +; + %cast.cmp = addrspacecast i32 addrspace(3)* %cmp.ptr to i32* + %ret = cmpxchg i32* addrspace(3)* %cas.ptr, i32* %cast.cmp, i32* %val seq_cst monotonic + ret { i32*, i1 } %ret +} + +; Null pointer in local addr space + +define void @local_nullptr(i32 addrspace(1)* nocapture %results, i8 addrspace(3)* %a) { +; +; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) +; CHECK-LABEL: define {{[^@]+}}@local_nullptr +; CHECK-SAME: (ptr addrspace(1) nocapture nofree noundef writeonly align 4 dereferenceable_or_null(4) [[RESULTS:%.*]], ptr addrspace(3) nofree writeonly [[A:%.*]]) #[[ATTR1]] { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr addrspace(3) [[A]], null +; CHECK-NEXT: [[CONV:%.*]] = zext i1 [[TOBOOL]] to i32 +; CHECK-NEXT: store i32 [[CONV]], ptr addrspace(1) [[RESULTS]], align 4 +; CHECK-NEXT: ret void +; +entry: + %tobool = icmp ne i8 addrspace(3)* %a, addrspacecast (i8 addrspace(5)* null to i8 addrspace(3)*) + %conv = zext i1 %tobool to i32 + store i32 %conv, i32 addrspace(1)* %results, align 4 + ret void +} + +attributes #0 = { nounwind } + + + + +;. +; CHECK: attributes #[[ATTR0]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: read) } +; CHECK: attributes #[[ATTR1]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write) } +; CHECK: attributes #[[ATTR2]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) } +; CHECK: attributes #[[ATTR3]] = { mustprogress nofree norecurse nounwind willreturn memory(argmem: readwrite) } +;. 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 @@ -239,6 +239,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: [AAAddressSpaceInfo] for CtxI ' %2 = load i32, ptr %0, align 4' at position {arg: [@0]} with state address space: none +; 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 @@ -320,6 +322,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="{[AAAddressSpaceInfo] ; 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/undefined_behavior.ll b/llvm/test/Transforms/Attributor/undefined_behavior.ll --- a/llvm/test/Transforms/Attributor/undefined_behavior.ll +++ b/llvm/test/Transforms/Attributor/undefined_behavior.ll @@ -75,7 +75,7 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@load_null_propagated ; TUNIT-SAME: () #[[ATTR0]] { -; TUNIT-NEXT: unreachable +; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@load_null_propagated @@ -158,7 +158,7 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@store_null_propagated ; TUNIT-SAME: () #[[ATTR0]] { -; TUNIT-NEXT: unreachable +; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(write) ; CGSCC-LABEL: define {{[^@]+}}@store_null_propagated @@ -239,7 +239,8 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@atomicrmw_null_propagated ; TUNIT-SAME: () #[[ATTR2]] { -; TUNIT-NEXT: unreachable +; TUNIT-NEXT: [[A:%.*]] = atomicrmw add ptr null, i32 1 acquire, align 4 +; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree nounwind willreturn ; CGSCC-LABEL: define {{[^@]+}}@atomicrmw_null_propagated @@ -321,7 +322,8 @@ ; TUNIT: Function Attrs: mustprogress nofree norecurse nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@atomiccmpxchg_null_propagated ; TUNIT-SAME: () #[[ATTR2]] { -; TUNIT-NEXT: unreachable +; TUNIT-NEXT: [[A:%.*]] = cmpxchg ptr null, i32 2, i32 3 acq_rel monotonic, align 4 +; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree nounwind willreturn ; CGSCC-LABEL: define {{[^@]+}}@atomiccmpxchg_null_propagated diff --git a/llvm/test/Transforms/Attributor/value-simplify-local-remote.ll b/llvm/test/Transforms/Attributor/value-simplify-local-remote.ll --- a/llvm/test/Transforms/Attributor/value-simplify-local-remote.ll +++ b/llvm/test/Transforms/Attributor/value-simplify-local-remote.ll @@ -342,14 +342,6 @@ ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RETVAL:%.*]] = alloca [[S_2:%.*]], align 8 ; CHECK-NEXT: call void @ext1(ptr noundef nonnull align 8 dereferenceable(24) [[RETVAL]]) -; CHECK-NEXT: [[DOTFCA_0_LOAD:%.*]] = load ptr, ptr [[RETVAL]], align 8 -; CHECK-NEXT: [[DOTFCA_0_INSERT:%.*]] = insertvalue [[S_2]] poison, ptr [[DOTFCA_0_LOAD]], 0 -; CHECK-NEXT: [[DOTFCA_1_GEP:%.*]] = getelementptr inbounds [[S_2]], ptr [[RETVAL]], i32 0, i32 1 -; CHECK-NEXT: [[DOTFCA_1_LOAD:%.*]] = load i64, ptr [[DOTFCA_1_GEP]], align 8 -; CHECK-NEXT: [[DOTFCA_1_INSERT:%.*]] = insertvalue [[S_2]] [[DOTFCA_0_INSERT]], i64 [[DOTFCA_1_LOAD]], 1 -; CHECK-NEXT: [[DOTFCA_2_GEP:%.*]] = getelementptr inbounds [[S_2]], ptr [[RETVAL]], i32 0, i32 2 -; CHECK-NEXT: [[DOTFCA_2_LOAD:%.*]] = load i64, ptr [[DOTFCA_2_GEP]], align 8 -; CHECK-NEXT: [[DOTFCA_2_INSERT:%.*]] = insertvalue [[S_2]] [[DOTFCA_1_INSERT]], i64 [[DOTFCA_2_LOAD]], 2 ; CHECK-NEXT: ret [[S_2]] zeroinitializer ; entry: