diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2022-01-27 22:17:16 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2022-05-14 11:44:34 +0000 |
commit | 04eeddc0aa8e0a417a16eaf9d7d095207f4a8623 (patch) | |
tree | 2a5d3b2fe5c852e91531d128d9177754572d5338 /contrib/llvm-project/llvm/lib/Analysis/MemoryBuiltins.cpp | |
parent | 0eae32dcef82f6f06de6419a0d623d7def0cc8f6 (diff) | |
parent | 6f8fc217eaa12bf657be1c6468ed9938d10168b3 (diff) |
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Analysis/MemoryBuiltins.cpp')
-rw-r--r-- | contrib/llvm-project/llvm/lib/Analysis/MemoryBuiltins.cpp | 450 |
1 files changed, 172 insertions, 278 deletions
diff --git a/contrib/llvm-project/llvm/lib/Analysis/MemoryBuiltins.cpp b/contrib/llvm-project/llvm/lib/Analysis/MemoryBuiltins.cpp index ffdd7a2cfd4b..208f93aa1ac6 100644 --- a/contrib/llvm-project/llvm/lib/Analysis/MemoryBuiltins.cpp +++ b/contrib/llvm-project/llvm/lib/Analysis/MemoryBuiltins.cpp @@ -51,12 +51,13 @@ using namespace llvm; enum AllocType : uint8_t { OpNewLike = 1<<0, // allocates; never returns null - MallocLike = 1<<1 | OpNewLike, // allocates; may return null + MallocLike = 1<<1, // allocates; may return null AlignedAllocLike = 1<<2, // allocates with alignment; may return null CallocLike = 1<<3, // allocates + bzero ReallocLike = 1<<4, // reallocates StrDupLike = 1<<5, - MallocOrCallocLike = MallocLike | CallocLike | AlignedAllocLike, + MallocOrOpNewLike = MallocLike | OpNewLike, + MallocOrCallocLike = MallocLike | OpNewLike | CallocLike | AlignedAllocLike, AllocLike = MallocOrCallocLike | StrDupLike, AnyAlloc = AllocLike | ReallocLike }; @@ -66,64 +67,59 @@ struct AllocFnsTy { 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<LibFunc, AllocFnsTy> 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, bool LookThroughBitCast, +static const Function *getCalledFunction(const Value *V, bool &IsNoBuiltin) { // Don't care about intrinsics in this case. if (isa<IntrinsicInst>(V)) return nullptr; - if (LookThroughBitCast) - V = V->stripPointerCasts(); - const auto *CB = dyn_cast<CallBase>(V); if (!CB) return nullptr; @@ -175,11 +171,9 @@ getAllocationDataForFunction(const Function *Callee, AllocType AllocTy, } static Optional<AllocFnsTy> getAllocationData(const Value *V, AllocType AllocTy, - const TargetLibraryInfo *TLI, - bool LookThroughBitCast = false) { + const TargetLibraryInfo *TLI) { bool IsNoBuiltinCall; - if (const Function *Callee = - getCalledFunction(V, LookThroughBitCast, IsNoBuiltinCall)) + if (const Function *Callee = getCalledFunction(V, IsNoBuiltinCall)) if (!IsNoBuiltinCall) return getAllocationDataForFunction(Callee, AllocTy, TLI); return None; @@ -187,11 +181,9 @@ static Optional<AllocFnsTy> getAllocationData(const Value *V, AllocType AllocTy, static Optional<AllocFnsTy> getAllocationData(const Value *V, AllocType AllocTy, - function_ref<const TargetLibraryInfo &(Function &)> GetTLI, - bool LookThroughBitCast = false) { + function_ref<const TargetLibraryInfo &(Function &)> GetTLI) { bool IsNoBuiltinCall; - if (const Function *Callee = - getCalledFunction(V, LookThroughBitCast, IsNoBuiltinCall)) + if (const Function *Callee = getCalledFunction(V, IsNoBuiltinCall)) if (!IsNoBuiltinCall) return getAllocationDataForFunction( Callee, AllocTy, &GetTLI(const_cast<Function &>(*Callee))); @@ -202,7 +194,7 @@ static Optional<AllocFnsTy> getAllocationSize(const Value *V, const TargetLibraryInfo *TLI) { bool IsNoBuiltinCall; const Function *Callee = - getCalledFunction(V, /*LookThroughBitCast=*/false, IsNoBuiltinCall); + getCalledFunction(V, IsNoBuiltinCall); if (!Callee) return None; @@ -226,92 +218,57 @@ static Optional<AllocFnsTy> getAllocationSize(const Value *V, 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; } -static bool hasNoAliasAttr(const Value *V, bool LookThroughBitCast) { - const auto *CB = - dyn_cast<CallBase>(LookThroughBitCast ? V->stripPointerCasts() : V); - return CB && CB->hasRetAttr(Attribute::NoAlias); -} - /// Tests if a value is a call or invoke to a library function that /// allocates or reallocates memory (either malloc, calloc, realloc, or strdup /// like). -bool llvm::isAllocationFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, AnyAlloc, TLI, LookThroughBitCast).hasValue(); +bool llvm::isAllocationFn(const Value *V, const TargetLibraryInfo *TLI) { + return getAllocationData(V, AnyAlloc, TLI).hasValue(); } bool llvm::isAllocationFn( - const Value *V, function_ref<const TargetLibraryInfo &(Function &)> GetTLI, - bool LookThroughBitCast) { - return getAllocationData(V, AnyAlloc, GetTLI, LookThroughBitCast).hasValue(); -} - -/// Tests if a value is a call or invoke to a function that returns a -/// NoAlias pointer (including malloc/calloc/realloc/strdup-like functions). -bool llvm::isNoAliasFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - // it's safe to consider realloc as noalias since accessing the original - // pointer is undefined behavior - return isAllocationFn(V, TLI, LookThroughBitCast) || - hasNoAliasAttr(V, LookThroughBitCast); + const Value *V, function_ref<const TargetLibraryInfo &(Function &)> GetTLI) { + return getAllocationData(V, AnyAlloc, GetTLI).hasValue(); } /// Tests if a value is a call or invoke to a library function that /// allocates uninitialized memory (such as malloc). -bool llvm::isMallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, MallocLike, TLI, LookThroughBitCast).hasValue(); -} -bool llvm::isMallocLikeFn( - const Value *V, function_ref<const TargetLibraryInfo &(Function &)> GetTLI, - bool LookThroughBitCast) { - return getAllocationData(V, MallocLike, GetTLI, LookThroughBitCast) - .hasValue(); +static bool isMallocLikeFn(const Value *V, const TargetLibraryInfo *TLI) { + return getAllocationData(V, MallocOrOpNewLike, TLI).hasValue(); } /// Tests if a value is a call or invoke to a library function that /// allocates uninitialized memory with alignment (such as aligned_alloc). -bool llvm::isAlignedAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, AlignedAllocLike, TLI, LookThroughBitCast) - .hasValue(); -} -bool llvm::isAlignedAllocLikeFn( - const Value *V, function_ref<const TargetLibraryInfo &(Function &)> GetTLI, - bool LookThroughBitCast) { - return getAllocationData(V, AlignedAllocLike, GetTLI, LookThroughBitCast) +static bool isAlignedAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI) { + return getAllocationData(V, AlignedAllocLike, TLI) .hasValue(); } /// Tests if a value is a call or invoke to a library function that /// allocates zero-filled memory (such as calloc). -bool llvm::isCallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, CallocLike, TLI, LookThroughBitCast).hasValue(); +static bool isCallocLikeFn(const Value *V, const TargetLibraryInfo *TLI) { + return getAllocationData(V, CallocLike, TLI).hasValue(); } /// Tests if a value is a call or invoke to a library function that /// allocates memory similar to malloc or calloc. -bool llvm::isMallocOrCallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, MallocOrCallocLike, TLI, - LookThroughBitCast).hasValue(); +bool llvm::isMallocOrCallocLikeFn(const Value *V, const TargetLibraryInfo *TLI) { + return getAllocationData(V, MallocOrCallocLike, TLI).hasValue(); } /// Tests if a value is a call or invoke to a library function that /// allocates memory (either malloc, calloc, or strdup like). -bool llvm::isAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, AllocLike, TLI, LookThroughBitCast).hasValue(); +bool llvm::isAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI) { + return getAllocationData(V, AllocLike, TLI).hasValue(); } /// Tests if a value is a call or invoke to a library function that /// reallocates memory (e.g., realloc). -bool llvm::isReallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, ReallocLike, TLI, LookThroughBitCast).hasValue(); +bool llvm::isReallocLikeFn(const Value *V, const TargetLibraryInfo *TLI) { + return getAllocationData(V, ReallocLike, TLI).hasValue(); } /// Tests if a functions is a call or invoke to a library function that @@ -320,113 +277,122 @@ bool llvm::isReallocLikeFn(const Function *F, const TargetLibraryInfo *TLI) { return getAllocationDataForFunction(F, ReallocLike, TLI).hasValue(); } -/// Tests if a value is a call or invoke to a library function that -/// allocates memory and throws if an allocation failed (e.g., new). -bool llvm::isOpNewLikeFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, OpNewLike, TLI, LookThroughBitCast).hasValue(); -} +bool llvm::isAllocRemovable(const CallBase *CB, const TargetLibraryInfo *TLI) { + assert(isAllocationFn(CB, TLI)); -/// Tests if a value is a call or invoke to a library function that -/// allocates memory (strdup, strndup). -bool llvm::isStrdupLikeFn(const Value *V, const TargetLibraryInfo *TLI, - bool LookThroughBitCast) { - return getAllocationData(V, StrDupLike, TLI, LookThroughBitCast).hasValue(); -} + // Note: Removability is highly dependent on the source language. For + // example, recent C++ requires direct calls to the global allocation + // [basic.stc.dynamic.allocation] to be observable unless part of a new + // expression [expr.new paragraph 13]. -/// extractMallocCall - Returns the corresponding CallInst if the instruction -/// is a malloc call. Since CallInst::CreateMalloc() only creates calls, we -/// ignore InvokeInst here. -const CallInst *llvm::extractMallocCall( - const Value *I, - function_ref<const TargetLibraryInfo &(Function &)> GetTLI) { - return isMallocLikeFn(I, GetTLI) ? dyn_cast<CallInst>(I) : nullptr; + // Historically we've treated the C family allocation routines as removable + return isAllocLikeFn(CB, TLI); } -static Value *computeArraySize(const CallInst *CI, const DataLayout &DL, - const TargetLibraryInfo *TLI, - bool LookThroughSExt = false) { - if (!CI) - return nullptr; +Value *llvm::getAllocAlignment(const CallBase *V, + const TargetLibraryInfo *TLI) { + assert(isAllocationFn(V, TLI)); - // The size of the malloc's result type must be known to determine array size. - Type *T = getMallocAllocatedType(CI, TLI); - if (!T || !T->isSized()) + const Optional<AllocFnsTy> FnData = getAllocationData(V, AnyAlloc, TLI); + if (!FnData.hasValue() || FnData->AlignParam < 0) { return nullptr; + } + return V->getOperand(FnData->AlignParam); +} - unsigned ElementSize = DL.getTypeAllocSize(T); - if (StructType *ST = dyn_cast<StructType>(T)) - ElementSize = DL.getStructLayout(ST)->getSizeInBytes(); +/// When we're compiling N-bit code, and the user uses parameters that are +/// greater than N bits (e.g. uint64_t on a 32-bit build), we can run into +/// trouble with APInt size issues. This function handles resizing + overflow +/// checks for us. Check and zext or trunc \p I depending on IntTyBits and +/// I's value. +static bool CheckedZextOrTrunc(APInt &I, unsigned IntTyBits) { + // More bits than we can handle. Checking the bit width isn't necessary, but + // it's faster than checking active bits, and should give `false` in the + // vast majority of cases. + if (I.getBitWidth() > IntTyBits && I.getActiveBits() > IntTyBits) + return false; + if (I.getBitWidth() != IntTyBits) + I = I.zextOrTrunc(IntTyBits); + return true; +} - // If malloc call's arg can be determined to be a multiple of ElementSize, - // return the multiple. Otherwise, return NULL. - Value *MallocArg = CI->getArgOperand(0); - Value *Multiple = nullptr; - if (ComputeMultiple(MallocArg, ElementSize, Multiple, LookThroughSExt)) - return Multiple; +Optional<APInt> +llvm::getAllocSize(const CallBase *CB, + const TargetLibraryInfo *TLI, + std::function<const Value*(const Value*)> Mapper) { + // Note: This handles both explicitly listed allocation functions and + // allocsize. The code structure could stand to be cleaned up a bit. + Optional<AllocFnsTy> FnData = getAllocationSize(CB, TLI); + if (!FnData) + return None; - return nullptr; -} + // Get the index type for this address space, results and intermediate + // computations are performed at that width. + auto &DL = CB->getModule()->getDataLayout(); + const unsigned IntTyBits = DL.getIndexTypeSizeInBits(CB->getType()); + + // Handle strdup-like functions separately. + if (FnData->AllocTy == StrDupLike) { + APInt Size(IntTyBits, GetStringLength(Mapper(CB->getArgOperand(0)))); + if (!Size) + return None; -/// getMallocType - Returns the PointerType resulting from the malloc call. -/// The PointerType depends on the number of bitcast uses of the malloc call: -/// 0: PointerType is the calls' return type. -/// 1: PointerType is the bitcast's result type. -/// >1: Unique PointerType cannot be determined, return NULL. -PointerType *llvm::getMallocType(const CallInst *CI, - const TargetLibraryInfo *TLI) { - assert(isMallocLikeFn(CI, TLI) && "getMallocType and not malloc call"); - - PointerType *MallocType = nullptr; - unsigned NumOfBitCastUses = 0; - - // Determine if CallInst has a bitcast use. - for (const User *U : CI->users()) - if (const BitCastInst *BCI = dyn_cast<BitCastInst>(U)) { - MallocType = cast<PointerType>(BCI->getDestTy()); - NumOfBitCastUses++; + // Strndup limits strlen. + if (FnData->FstParam > 0) { + const ConstantInt *Arg = + dyn_cast<ConstantInt>(Mapper(CB->getArgOperand(FnData->FstParam))); + if (!Arg) + return None; + + APInt MaxSize = Arg->getValue().zextOrSelf(IntTyBits); + if (Size.ugt(MaxSize)) + Size = MaxSize + 1; } + return Size; + } - // Malloc call has 1 bitcast use, so type is the bitcast's destination type. - if (NumOfBitCastUses == 1) - return MallocType; + const ConstantInt *Arg = + dyn_cast<ConstantInt>(Mapper(CB->getArgOperand(FnData->FstParam))); + if (!Arg) + return None; - // Malloc call was not bitcast, so type is the malloc function's return type. - if (NumOfBitCastUses == 0) - return cast<PointerType>(CI->getType()); + APInt Size = Arg->getValue(); + if (!CheckedZextOrTrunc(Size, IntTyBits)) + return None; - // Type could not be determined. - return nullptr; -} + // Size is determined by just 1 parameter. + if (FnData->SndParam < 0) + return Size; -/// getMallocAllocatedType - Returns the Type allocated by malloc call. -/// The Type depends on the number of bitcast uses of the malloc call: -/// 0: PointerType is the malloc calls' return type. -/// 1: PointerType is the bitcast's result type. -/// >1: Unique PointerType cannot be determined, return NULL. -Type *llvm::getMallocAllocatedType(const CallInst *CI, - const TargetLibraryInfo *TLI) { - PointerType *PT = getMallocType(CI, TLI); - return PT ? PT->getElementType() : nullptr; -} + Arg = dyn_cast<ConstantInt>(Mapper(CB->getArgOperand(FnData->SndParam))); + if (!Arg) + return None; + + APInt NumElems = Arg->getValue(); + if (!CheckedZextOrTrunc(NumElems, IntTyBits)) + return None; -/// getMallocArraySize - Returns the array size of a malloc call. If the -/// argument passed to malloc is a multiple of the size of the malloced type, -/// then return that multiple. For non-array mallocs, the multiple is -/// constant 1. Otherwise, return NULL for mallocs whose array size cannot be -/// determined. -Value *llvm::getMallocArraySize(CallInst *CI, const DataLayout &DL, - const TargetLibraryInfo *TLI, - bool LookThroughSExt) { - assert(isMallocLikeFn(CI, TLI) && "getMallocArraySize and not malloc call"); - return computeArraySize(CI, DL, TLI, LookThroughSExt); + bool Overflow; + Size = Size.umul_ov(NumElems, Overflow); + if (Overflow) + return None; + return Size; } -/// extractCallocCall - Returns the corresponding CallInst if the instruction -/// is a calloc call. -const CallInst *llvm::extractCallocCall(const Value *I, - const TargetLibraryInfo *TLI) { - return isCallocLikeFn(I, TLI) ? cast<CallInst>(I) : nullptr; +Constant *llvm::getInitialValueOfAllocation(const CallBase *Alloc, + const TargetLibraryInfo *TLI, + Type *Ty) { + assert(isAllocationFn(Alloc, TLI)); + + // malloc and aligned_alloc are uninitialized (undef) + if (isMallocLikeFn(Alloc, TLI) || isAlignedAllocLikeFn(Alloc, TLI)) + return UndefValue::get(Ty); + + // calloc zero initializes + if (isCallocLikeFn(Alloc, TLI)) + return Constant::getNullValue(Ty); + + return nullptr; } /// isLibFreeFunction - Returns true if the function is a builtin free() @@ -485,8 +451,7 @@ bool llvm::isLibFreeFunction(const Function *F, const LibFunc TLIFn) { /// isFreeCall - Returns non-null if the value is a call to the builtin free() const CallInst *llvm::isFreeCall(const Value *I, const TargetLibraryInfo *TLI) { bool IsNoBuiltinCall; - const Function *Callee = - getCalledFunction(I, /*LookThroughBitCast=*/false, IsNoBuiltinCall); + const Function *Callee = getCalledFunction(I, IsNoBuiltinCall); if (Callee == nullptr || IsNoBuiltinCall) return nullptr; @@ -644,20 +609,8 @@ SizeOffsetType ObjectSizeOffsetVisitor::compute(Value *V) { return unknown(); } -/// When we're compiling N-bit code, and the user uses parameters that are -/// greater than N bits (e.g. uint64_t on a 32-bit build), we can run into -/// trouble with APInt size issues. This function handles resizing + overflow -/// checks for us. Check and zext or trunc \p I depending on IntTyBits and -/// I's value. bool ObjectSizeOffsetVisitor::CheckedZextOrTrunc(APInt &I) { - // More bits than we can handle. Checking the bit width isn't necessary, but - // it's faster than checking active bits, and should give `false` in the - // vast majority of cases. - if (I.getBitWidth() > IntTyBits && I.getActiveBits() > IntTyBits) - return false; - if (I.getBitWidth() != IntTyBits) - I = I.zextOrTrunc(IntTyBits); - return true; + return ::CheckedZextOrTrunc(I, IntTyBits); } SizeOffsetType ObjectSizeOffsetVisitor::visitAllocaInst(AllocaInst &I) { @@ -698,61 +651,10 @@ SizeOffsetType ObjectSizeOffsetVisitor::visitArgument(Argument &A) { } SizeOffsetType ObjectSizeOffsetVisitor::visitCallBase(CallBase &CB) { - Optional<AllocFnsTy> FnData = getAllocationSize(&CB, TLI); - if (!FnData) - return unknown(); - - // Handle strdup-like functions separately. - if (FnData->AllocTy == StrDupLike) { - APInt Size(IntTyBits, GetStringLength(CB.getArgOperand(0))); - if (!Size) - return unknown(); - - // Strndup limits strlen. - if (FnData->FstParam > 0) { - ConstantInt *Arg = - dyn_cast<ConstantInt>(CB.getArgOperand(FnData->FstParam)); - if (!Arg) - return unknown(); - - APInt MaxSize = Arg->getValue().zextOrSelf(IntTyBits); - if (Size.ugt(MaxSize)) - Size = MaxSize + 1; - } - return std::make_pair(Size, Zero); - } - - ConstantInt *Arg = dyn_cast<ConstantInt>(CB.getArgOperand(FnData->FstParam)); - if (!Arg) - return unknown(); - - APInt Size = Arg->getValue(); - if (!CheckedZextOrTrunc(Size)) - return unknown(); - - // Size is determined by just 1 parameter. - if (FnData->SndParam < 0) - return std::make_pair(Size, Zero); - - Arg = dyn_cast<ConstantInt>(CB.getArgOperand(FnData->SndParam)); - if (!Arg) - return unknown(); - - APInt NumElems = Arg->getValue(); - if (!CheckedZextOrTrunc(NumElems)) - return unknown(); - - bool Overflow; - Size = Size.umul_ov(NumElems, Overflow); - return Overflow ? unknown() : std::make_pair(Size, Zero); - - // TODO: handle more standard functions (+ wchar cousins): - // - strdup / strndup - // - strcpy / strncpy - // - strcat / strncat - // - memcpy / memmove - // - strcat / strncat - // - memset + auto Mapper = [](const Value *V) { return V; }; + if (Optional<APInt> Size = getAllocSize(&CB, TLI, Mapper)) + return std::make_pair(*Size, Zero); + return unknown(); } SizeOffsetType @@ -976,7 +878,7 @@ SizeOffsetEvalType ObjectSizeOffsetEvaluator::visitCallBase(CallBase &CB) { // Handle strdup-like functions separately. if (FnData->AllocTy == StrDupLike) { - // TODO + // TODO: implement evaluation of strdup/strndup return unknown(); } @@ -989,14 +891,6 @@ SizeOffsetEvalType ObjectSizeOffsetEvaluator::visitCallBase(CallBase &CB) { SecondArg = Builder.CreateZExtOrTrunc(SecondArg, IntTy); Value *Size = Builder.CreateMul(FirstArg, SecondArg); return std::make_pair(Size, Zero); - - // TODO: handle more standard functions (+ wchar cousins): - // - strdup / strndup - // - strcpy / strncpy - // - strcat / strncat - // - memcpy / memmove - // - strcat / strncat - // - memset } SizeOffsetEvalType |