diff --git a/llvm/include/llvm/Analysis/MemoryBuiltins.h b/llvm/include/llvm/Analysis/MemoryBuiltins.h --- a/llvm/include/llvm/Analysis/MemoryBuiltins.h +++ b/llvm/include/llvm/Analysis/MemoryBuiltins.h @@ -118,12 +118,48 @@ return V; }); +bool loadHasFreezeBits(const LoadInst *const LI); + +/// Provide categorical information concerning constant selection. +enum class InitializationCategory { + /// This enumerator will be returned when the load is determined to be of an + /// address that was allocated using: + /// + /// * calloc or compliant new derivative + /// * malloc in the absence of !freeze_bits + /// + /// In the first case the returned constant will be zero. This is in keeping + /// with current behavior where initialized AllocFnKind::Zeroed results in a + /// zero constant of arbitrary type. In the second case the return constant + /// will be poison. + Constant, + /// This enumerator will be returned when the load accompanied by !freeze_bits + /// and classification of the allocator such that memory is determined to be + /// in an uninitialized state. The returned constant will be null of arbitrary + /// type. + /// + /// The MemoryBuiltins API is used by clients that remove loads and stores. + /// The reason for the null of arbitrary type is it is differentiated + /// from zero. If zero were used optimizations like dead store elimination + /// (DSE) would remove the store 0. + FreezePoison, + /// Returned when getInitialValueOfAllocation is unable to determine the + /// classification of the allocation operations. No constant object is + /// produced and thus the returned pointer will be a nullptr. This provides + /// backward compatibility with the current API. + Unknown +}; + /// If this is a call to an allocation function that initializes memory to a -/// fixed value, return said value in the requested type. If this is a call to -/// alloca instruction the returned value is undef. Otherwise, return nullptr. -Constant *getInitialValueOfAllocation(const Value *V, - const TargetLibraryInfo *TLI, - Type *Ty); +/// fixed value, return said value in the requested type. Otherwise, return +/// nullptr. +/// +/// This function also serves to determine the return value of a load +/// instruction directly using allocation without an intermediate store +/// instruction(s). This mode is enabled by passing a load institution pointer. +std::pair +getInitialValueOfAllocation(const Value *V, const TargetLibraryInfo *TLI, + Type *Ty, const LoadInst *LI = nullptr); /// If a function is part of an allocation family (e.g. /// malloc/realloc/calloc/free), return the identifier for its family diff --git a/llvm/lib/Analysis/MemoryBuiltins.cpp b/llvm/lib/Analysis/MemoryBuiltins.cpp --- a/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -433,27 +433,39 @@ return Size; } -Constant *llvm::getInitialValueOfAllocation(const Value *V, - const TargetLibraryInfo *TLI, - Type *Ty) { +bool llvm::loadHasFreezeBits(const LoadInst *const LI) { + return LI && LI->hasMetadata(LLVMContext::MD_freeze_bits); +} + +static std::pair +valueUsingLoadInstAndFreezeBits(const LoadInst *const LI, Type *const Ty) { + if (loadHasFreezeBits(LI)) + return {InitializationCategory::FreezePoison, nullptr}; + else + return {InitializationCategory::Constant, PoisonValue::get(Ty)}; +} + +std::pair +llvm::getInitialValueOfAllocation(const Value *V, const TargetLibraryInfo *TLI, + Type *Ty, const LoadInst *LI) { if (isa(V)) - return UndefValue::get(Ty); + return valueUsingLoadInstAndFreezeBits(LI, Ty); auto *Alloc = dyn_cast(V); if (!Alloc) - return nullptr; + return {InitializationCategory::Unknown, nullptr}; // malloc are uninitialized (undef) if (getAllocationData(Alloc, MallocOrOpNewLike, TLI).has_value()) - return UndefValue::get(Ty); + return valueUsingLoadInstAndFreezeBits(LI, Ty); AllocFnKind AK = getAllocFnKind(Alloc); if ((AK & AllocFnKind::Uninitialized) != AllocFnKind::Unknown) - return UndefValue::get(Ty); + return valueUsingLoadInstAndFreezeBits(LI, Ty); if ((AK & AllocFnKind::Zeroed) != AllocFnKind::Unknown) - return Constant::getNullValue(Ty); + return {InitializationCategory::Constant, Constant::getNullValue(Ty)}; - return nullptr; + return {InitializationCategory::Unknown, nullptr}; } struct FreeFnsTy { diff --git a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp --- a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp @@ -803,12 +803,12 @@ const unsigned VecStoreSize = DL->getTypeStoreSize(VectorTy); // Alloca is uninitialized memory. Imitate that by making the first value - // undef. + // poison. SSAUpdater Updater; Updater.Initialize(VectorTy, "promotealloca"); Updater.AddAvailableValue( Alloca.getParent(), - getInitialValueOfAllocation(&Alloca, nullptr, VectorTy)); + getInitialValueOfAllocation(&Alloca, nullptr, VectorTy).second); // First handle the initial worklist. SmallVector DeferredLoads; 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 @@ -232,7 +232,7 @@ const DataLayout &DL, AA::RangeTy *RangePtr) { if (isa(Obj)) return UndefValue::get(&Ty); - if (Constant *Init = getInitialValueOfAllocation(&Obj, TLI, &Ty)) + if (Constant *Init = getInitialValueOfAllocation(&Obj, TLI, &Ty).second) return Init; auto *GV = dyn_cast(&Obj); if (!GV) 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 @@ -6702,7 +6702,7 @@ // alloca to the same pattern as the original allocation result. if (isRemovableAlloc(CB, TLI)) { auto *I8Ty = Type::getInt8Ty(CB->getParent()->getContext()); - if (nullptr != getInitialValueOfAllocation(CB, TLI, I8Ty)) { + if (nullptr != getInitialValueOfAllocation(CB, TLI, I8Ty).second) { AllocationInfo *AI = new (A.Allocator) AllocationInfo{CB}; AllocationInfos[CB] = AI; if (TLI) @@ -6851,7 +6851,7 @@ Alloca, AI.CB->getType(), "malloc_cast", AI.CB); auto *I8Ty = Type::getInt8Ty(F->getContext()); - auto *InitVal = getInitialValueOfAllocation(AI.CB, TLI, I8Ty); + auto *InitVal = getInitialValueOfAllocation(AI.CB, TLI, I8Ty).second; assert(InitVal && "Must be able to materialize initial memory state of allocation"); diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp --- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp +++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp @@ -1091,7 +1091,7 @@ return false; Type *Int8Ty = Type::getInt8Ty(CI->getFunction()->getContext()); - Constant *InitVal = getInitialValueOfAllocation(CI, TLI, Int8Ty); + Constant *InitVal = getInitialValueOfAllocation(CI, TLI, Int8Ty).second; if (!InitVal) // Must be able to emit a memset for initialization return false; diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -1926,7 +1926,8 @@ if (StoredConstant) { Constant *InitC = - getInitialValueOfAllocation(DefUO, &TLI, StoredConstant->getType()); + getInitialValueOfAllocation(DefUO, &TLI, StoredConstant->getType()) + .second; // If the clobbering access is LiveOnEntry, no instructions between them // can modify the memory location. if (InitC && InitC == StoredConstant) diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp --- a/llvm/lib/Transforms/Scalar/GVN.cpp +++ b/llvm/lib/Transforms/Scalar/GVN.cpp @@ -1246,7 +1246,7 @@ // In addition to allocator function calls this includes loading the alloca -> // undef. if (Constant *InitVal = - getInitialValueOfAllocation(DepInst, TLI, Load->getType())) + getInitialValueOfAllocation(DepInst, TLI, Load->getType()).second) return AvailableValue::get(InitVal); if (StoreInst *S = dyn_cast(DepInst)) { diff --git a/llvm/lib/Transforms/Scalar/NewGVN.cpp b/llvm/lib/Transforms/Scalar/NewGVN.cpp --- a/llvm/lib/Transforms/Scalar/NewGVN.cpp +++ b/llvm/lib/Transforms/Scalar/NewGVN.cpp @@ -1506,7 +1506,7 @@ // undef value. This can happen when loading for a fresh allocation with no // intervening stores, for example. Note that this is only true in the case // that the result of the allocation is pointer equal to the load ptr. - else if (auto *InitVal = getInitialValueOfAllocation(DepInst, TLI, LoadType)) + else if (auto *InitVal = getInitialValueOfAllocation(DepInst, TLI, LoadType).second) return createConstantExpression(InitVal); return nullptr; diff --git a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp --- a/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp +++ b/llvm/lib/Transforms/Utils/PromoteMemoryToRegister.cpp @@ -601,7 +601,7 @@ if (I == StoresByIndex.begin()) { if (StoresByIndex.empty()) // If there are no stores, the load takes the undef value. - ReplVal = getInitialValueOfAllocation(AI, nullptr, LI->getType()); + ReplVal = getInitialValueOfAllocation(AI, nullptr, LI->getType()).second; else // There is no store before this load, bail out (load may be affected // by the following stores - see main comment).