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 @@ -119,6 +119,12 @@ // Properties of allocation functions // +/// Gets the alignment argument for an aligned_alloc-like function +Value *getAllocAlignment(const CallBase *V, const TargetLibraryInfo *TLI); + +/// Gets the size allocated by the allocation function +Value *getAllocSize(CallBase *Alloc, const TargetLibraryInfo *TLI); + /// If this allocation function initializes memory to a fixed value, return /// said value in the requested type. Otherwise, return nullptr. Constant *getInitialValueOfAllocation(const CallBase *Alloc, 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 @@ -66,53 +66,51 @@ unsigned NumParams; // First and Second size parameters (or -1 if unused) int FstParam, SndParam; + // Alignment parameter for aligned_alloc and aligned new + int AlignParam; }; // FIXME: certain users need more information. E.g., SimplifyLibCalls needs to // know which functions are nounwind, noalias, nocapture parameters, etc. static const std::pair AllocationFnData[] = { - {LibFunc_malloc, {MallocLike, 1, 0, -1}}, - {LibFunc_vec_malloc, {MallocLike, 1, 0, -1}}, - {LibFunc_valloc, {MallocLike, 1, 0, -1}}, - {LibFunc_Znwj, {OpNewLike, 1, 0, -1}}, // new(unsigned int) - {LibFunc_ZnwjRKSt9nothrow_t, {MallocLike, 2, 0, -1}}, // new(unsigned int, nothrow) - {LibFunc_ZnwjSt11align_val_t, {OpNewLike, 2, 0, -1}}, // new(unsigned int, align_val_t) - {LibFunc_ZnwjSt11align_val_tRKSt9nothrow_t, // new(unsigned int, align_val_t, nothrow) - {MallocLike, 3, 0, -1}}, - {LibFunc_Znwm, {OpNewLike, 1, 0, -1}}, // new(unsigned long) - {LibFunc_ZnwmRKSt9nothrow_t, {MallocLike, 2, 0, -1}}, // new(unsigned long, nothrow) - {LibFunc_ZnwmSt11align_val_t, {OpNewLike, 2, 0, -1}}, // new(unsigned long, align_val_t) - {LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t, // new(unsigned long, align_val_t, nothrow) - {MallocLike, 3, 0, -1}}, - {LibFunc_Znaj, {OpNewLike, 1, 0, -1}}, // new[](unsigned int) - {LibFunc_ZnajRKSt9nothrow_t, {MallocLike, 2, 0, -1}}, // new[](unsigned int, nothrow) - {LibFunc_ZnajSt11align_val_t, {OpNewLike, 2, 0, -1}}, // new[](unsigned int, align_val_t) - {LibFunc_ZnajSt11align_val_tRKSt9nothrow_t, // new[](unsigned int, align_val_t, nothrow) - {MallocLike, 3, 0, -1}}, - {LibFunc_Znam, {OpNewLike, 1, 0, -1}}, // new[](unsigned long) - {LibFunc_ZnamRKSt9nothrow_t, {MallocLike, 2, 0, -1}}, // new[](unsigned long, nothrow) - {LibFunc_ZnamSt11align_val_t, {OpNewLike, 2, 0, -1}}, // new[](unsigned long, align_val_t) - {LibFunc_ZnamSt11align_val_tRKSt9nothrow_t, // new[](unsigned long, align_val_t, nothrow) - {MallocLike, 3, 0, -1}}, - {LibFunc_msvc_new_int, {OpNewLike, 1, 0, -1}}, // new(unsigned int) - {LibFunc_msvc_new_int_nothrow, {MallocLike, 2, 0, -1}}, // new(unsigned int, nothrow) - {LibFunc_msvc_new_longlong, {OpNewLike, 1, 0, -1}}, // new(unsigned long long) - {LibFunc_msvc_new_longlong_nothrow, {MallocLike, 2, 0, -1}}, // new(unsigned long long, nothrow) - {LibFunc_msvc_new_array_int, {OpNewLike, 1, 0, -1}}, // new[](unsigned int) - {LibFunc_msvc_new_array_int_nothrow, {MallocLike, 2, 0, -1}}, // new[](unsigned int, nothrow) - {LibFunc_msvc_new_array_longlong, {OpNewLike, 1, 0, -1}}, // new[](unsigned long long) - {LibFunc_msvc_new_array_longlong_nothrow, {MallocLike, 2, 0, -1}}, // new[](unsigned long long, nothrow) - {LibFunc_aligned_alloc, {AlignedAllocLike, 2, 1, -1}}, - {LibFunc_memalign, {AlignedAllocLike, 2, 1, -1}}, - {LibFunc_calloc, {CallocLike, 2, 0, 1}}, - {LibFunc_vec_calloc, {CallocLike, 2, 0, 1}}, - {LibFunc_realloc, {ReallocLike, 2, 1, -1}}, - {LibFunc_vec_realloc, {ReallocLike, 2, 1, -1}}, - {LibFunc_reallocf, {ReallocLike, 2, 1, -1}}, - {LibFunc_strdup, {StrDupLike, 1, -1, -1}}, - {LibFunc_strndup, {StrDupLike, 2, 1, -1}}, - {LibFunc___kmpc_alloc_shared, {MallocLike, 1, 0, -1}}, - // TODO: Handle "int posix_memalign(void **, size_t, size_t)" + {LibFunc_malloc, {MallocLike, 1, 0, -1, -1}}, + {LibFunc_vec_malloc, {MallocLike, 1, 0, -1, -1}}, + {LibFunc_valloc, {MallocLike, 1, 0, -1, -1}}, + {LibFunc_Znwj, {OpNewLike, 1, 0, -1, -1}}, // new(unsigned int) + {LibFunc_ZnwjRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1}}, // new(unsigned int, nothrow) + {LibFunc_ZnwjSt11align_val_t, {OpNewLike, 2, 0, -1, 1}}, // new(unsigned int, align_val_t) + {LibFunc_ZnwjSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1}}, // new(unsigned int, align_val_t, nothrow) + {LibFunc_Znwm, {OpNewLike, 1, 0, -1, -1}}, // new(unsigned long) + {LibFunc_ZnwmRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1}}, // new(unsigned long, nothrow) + {LibFunc_ZnwmSt11align_val_t, {OpNewLike, 2, 0, -1, 1}}, // new(unsigned long, align_val_t) + {LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1}}, // new(unsigned long, align_val_t, nothrow) + {LibFunc_Znaj, {OpNewLike, 1, 0, -1, -1}}, // new[](unsigned int) + {LibFunc_ZnajRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1}}, // new[](unsigned int, nothrow) + {LibFunc_ZnajSt11align_val_t, {OpNewLike, 2, 0, -1, 1}}, // new[](unsigned int, align_val_t) + {LibFunc_ZnajSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1}}, // new[](unsigned int, align_val_t, nothrow) + {LibFunc_Znam, {OpNewLike, 1, 0, -1, -1}}, // new[](unsigned long) + {LibFunc_ZnamRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1}}, // new[](unsigned long, nothrow) + {LibFunc_ZnamSt11align_val_t, {OpNewLike, 2, 0, -1, 1}}, // new[](unsigned long, align_val_t) + {LibFunc_ZnamSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1}}, // new[](unsigned long, align_val_t, nothrow) + {LibFunc_msvc_new_int, {OpNewLike, 1, 0, -1, -1}}, // new(unsigned int) + {LibFunc_msvc_new_int_nothrow, {MallocLike, 2, 0, -1, -1}}, // new(unsigned int, nothrow) + {LibFunc_msvc_new_longlong, {OpNewLike, 1, 0, -1, -1}}, // new(unsigned long long) + {LibFunc_msvc_new_longlong_nothrow, {MallocLike, 2, 0, -1, -1}}, // new(unsigned long long, nothrow) + {LibFunc_msvc_new_array_int, {OpNewLike, 1, 0, -1, -1}}, // new[](unsigned int) + {LibFunc_msvc_new_array_int_nothrow, {MallocLike, 2, 0, -1, -1}}, // new[](unsigned int, nothrow) + {LibFunc_msvc_new_array_longlong, {OpNewLike, 1, 0, -1, -1}}, // new[](unsigned long long) + {LibFunc_msvc_new_array_longlong_nothrow, {MallocLike, 2, 0, -1, -1}}, // new[](unsigned long long, nothrow) + {LibFunc_aligned_alloc, {AlignedAllocLike, 2, 1, -1, 0}}, + {LibFunc_memalign, {AlignedAllocLike, 2, 1, -1, 0}}, + {LibFunc_calloc, {CallocLike, 2, 0, 1, -1}}, + {LibFunc_vec_calloc, {CallocLike, 2, 0, 1, -1}}, + {LibFunc_realloc, {ReallocLike, 2, 1, -1, -1}}, + {LibFunc_vec_realloc, {ReallocLike, 2, 1, -1, -1}}, + {LibFunc_reallocf, {ReallocLike, 2, 1, -1, -1}}, + {LibFunc_strdup, {StrDupLike, 1, -1, -1, -1}}, + {LibFunc_strndup, {StrDupLike, 2, 1, -1, -1}}, + {LibFunc___kmpc_alloc_shared, {MallocLike, 1, 0, -1, -1}}, + // TODO: Handle "int posix_memalign(void **, size_t, size_t)" }; static const Function *getCalledFunction(const Value *V, @@ -219,6 +217,8 @@ Result.NumParams = Callee->getNumOperands(); Result.FstParam = Args.first; Result.SndParam = Args.second.getValueOr(-1); + // Allocsize has no way to specify an alignment argument + Result.AlignParam = -1; return Result; } @@ -269,6 +269,14 @@ return getAllocationData(V, AlignedAllocLike, GetTLI) .hasValue(); } +/// Gets the alignment argument for an aligned_alloc-like function +Value *llvm::getAllocAlignment(const CallBase *V, const TargetLibraryInfo *TLI) { + const Optional FnData = getAllocationData(V, AnyAlloc, TLI); + if (!FnData.hasValue()) { + return nullptr; + } + return V->getOperand(FnData->AlignParam); +} /// Tests if a value is a call or invoke to a library function that /// allocates zero-filled memory (such as calloc). @@ -328,6 +336,31 @@ return nullptr; } +Value *llvm::getAllocSize(CallBase *Alloc, const TargetLibraryInfo *TLI) { + Optional FnData = getAllocationSize(Alloc, TLI); + if (!FnData.hasValue()) + return nullptr; + + // TODO: handle strdup-like functions separately. + if (FnData->AllocTy == StrDupLike) + return nullptr; + + Value *ElementSize = Alloc->getArgOperand(FnData->FstParam); + if (!ElementSize) + return nullptr; + + // Size is determined by just 1 parameter. + if (FnData->SndParam < 0) + return ElementSize; + + Value *ElementCount = Alloc->getArgOperand(FnData->SndParam); + if (!ElementCount) + return nullptr; + + IRBuilder<> B(static_cast(Alloc)); + return B.CreateMul(ElementSize, ElementCount); +} + /// isLibFreeFunction - Returns true if the function is a builtin free() bool llvm::isLibFreeFunction(const Function *F, const LibFunc TLIFn) { unsigned ExpectedNumParams; 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 @@ -5765,9 +5765,8 @@ /// The kind of allocation. const enum class AllocationKind { - MALLOC, - CALLOC, - ALIGNED_ALLOC, + INITIAL_UNDEF, + INITIAL_ZERO, } Kind; /// The library function id for the allocation. @@ -5826,18 +5825,24 @@ DeallocationInfos[CB] = new (A.Allocator) DeallocationInfo{CB}; return true; } - bool IsMalloc = isMallocLikeFn(CB, TLI); - bool IsAlignedAllocLike = !IsMalloc && isAlignedAllocLikeFn(CB, TLI); - bool IsCalloc = - !IsMalloc && !IsAlignedAllocLike && isCallocLikeFn(CB, TLI); - if (!IsMalloc && !IsAlignedAllocLike && !IsCalloc) + + if (!isAllocLikeFn(CB, TLI)) + return true; + + Constant *AllocValue = getInitialValueOfAllocation(CB, TLI, Type::getInt8PtrTy(CB->getContext())); + + AllocationInfo::AllocationKind Kind; + if (AllocValue->containsUndefOrPoisonElement()) { + Kind = AllocationInfo::AllocationKind::INITIAL_UNDEF; + } else if (AllocValue->isNullValue()) { + Kind = AllocationInfo::AllocationKind::INITIAL_ZERO; + } else { + // The initial value is unknown or not constant but not undef return true; - auto Kind = - IsMalloc ? AllocationInfo::AllocationKind::MALLOC - : (IsCalloc ? AllocationInfo::AllocationKind::CALLOC - : AllocationInfo::AllocationKind::ALIGNED_ALLOC); + } - AllocationInfo *AI = new (A.Allocator) AllocationInfo{CB, Kind}; + AllocationInfo *AI = + new (A.Allocator) AllocationInfo{CB, Kind}; AllocationInfos[CB] = AI; TLI->getLibFunc(*CB, AI->LibraryFunctionId); return true; @@ -5935,23 +5940,16 @@ Optional SizeAPI = getSize(A, *this, AI); if (SizeAPI.hasValue()) { Size = ConstantInt::get(AI.CB->getContext(), *SizeAPI); - } else if (AI.Kind == AllocationInfo::AllocationKind::CALLOC) { - auto *Num = AI.CB->getOperand(0); - auto *SizeT = AI.CB->getOperand(1); - IRBuilder<> B(AI.CB); - Size = B.CreateMul(Num, SizeT, "h2s.calloc.size"); - } else if (AI.Kind == AllocationInfo::AllocationKind::ALIGNED_ALLOC) { - Size = AI.CB->getOperand(1); } else { - Size = AI.CB->getOperand(0); + Size = getAllocSize(AI.CB, TLI); } Align Alignment(1); if (MaybeAlign RetAlign = AI.CB->getRetAlign()) Alignment = max(Alignment, RetAlign); - if (AI.Kind == AllocationInfo::AllocationKind::ALIGNED_ALLOC) { + if (Value *AllocatorAlignment = getAllocAlignment(AI.CB, TLI)) { Optional AlignmentAPI = - getAPInt(A, *this, *AI.CB->getArgOperand(0)); + getAPInt(A, *this, *AllocatorAlignment); assert(AlignmentAPI.hasValue() && "Expected an alignment during manifest!"); Alignment = @@ -5978,7 +5976,7 @@ } // Zero out the allocated memory if it was a calloc. - if (AI.Kind == AllocationInfo::AllocationKind::CALLOC) { + if (AI.Kind == AllocationInfo::AllocationKind::INITIAL_ZERO) { auto *BI = new BitCastInst(Alloca, AI.CB->getType(), "calloc_bc", Alloca->getNextNode()); Value *Ops[] = { @@ -6010,25 +6008,9 @@ Optional getSize(Attributor &A, const AbstractAttribute &AA, AllocationInfo &AI) { - - if (AI.Kind == AllocationInfo::AllocationKind::MALLOC) - return getAPInt(A, AA, *AI.CB->getArgOperand(0)); - - if (AI.Kind == AllocationInfo::AllocationKind::ALIGNED_ALLOC) - // Only if the alignment is also constant we return a size. - return getAPInt(A, AA, *AI.CB->getArgOperand(0)).hasValue() - ? getAPInt(A, AA, *AI.CB->getArgOperand(1)) - : llvm::None; - - assert(AI.Kind == AllocationInfo::AllocationKind::CALLOC && - "Expected only callocs are left"); - Optional Num = getAPInt(A, AA, *AI.CB->getArgOperand(0)); - Optional Size = getAPInt(A, AA, *AI.CB->getArgOperand(1)); - if (!Num.hasValue() || !Size.hasValue()) - return llvm::None; - bool Overflow = false; - Size = Size.getValue().umul_ov(Num.getValue(), Overflow); - return Overflow ? llvm::None : Size; + Function *F = getAnchorScope(); + const auto *TLI = A.getInfoCache().getTargetLibraryInfoForFunction(*F); + return getAPInt(A, AA, *getAllocSize(AI.CB, TLI)); } /// Collection of all malloc-like calls in a function with associated @@ -6260,8 +6242,9 @@ continue; if (MaxHeapToStackSize == -1) { - if (AI.Kind == AllocationInfo::AllocationKind::ALIGNED_ALLOC) - if (!getAPInt(A, *this, *AI.CB->getArgOperand(0)).hasValue()) { + if (AI.AlignArgPos != -1) + if (!getAPInt(A, *this, *AI.CB->getArgOperand(AI.AlignArgPos)) + .hasValue()) { LLVM_DEBUG(dbgs() << "[H2S] Unknown allocation alignment: " << *AI.CB << "\n"); AI.Status = AllocationInfo::INVALID; diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp @@ -2585,13 +2585,14 @@ } // Add alignment attribute if alignment is a power of two constant. - if (!isAlignedAllocLikeFn(&Call, TLI)) + Value *Alignment = getAllocAlignment(&Call, TLI); + if (!Alignment) return; - ConstantInt *Op0C = dyn_cast(Call.getOperand(0)); - if (Op0C && Op0C->getValue().ult(llvm::Value::MaximumAlignment) && + ConstantInt *AlignOpC = dyn_cast(Alignment); + if (AlignOpC && AlignOpC->getValue().ult(llvm::Value::MaximumAlignment) && isKnownNonZero(Call.getOperand(1), DL, 0, &AC, &Call, &DT)) { - uint64_t AlignmentVal = Op0C->getZExtValue(); + uint64_t AlignmentVal = AlignOpC->getZExtValue(); if (llvm::isPowerOf2_64(AlignmentVal)) { Call.removeRetAttr(Attribute::Alignment); Call.addRetAttr(Attribute::getWithAlignment(Call.getContext(),