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 @@ -75,6 +75,14 @@ function_ref GetTLI, bool LookThroughBitCast = false); +/// Tests if a value is a call or invoke to a library function that +/// allocates uninitialized memory with alignment (such as aligned_alloc). +bool isAlignedAllocLikeFn(const Value *V, const TargetLibraryInfo *TLI, + bool LookThroughBitCast = false); +bool isAlignedAllocLikeFn( + const Value *V, function_ref GetTLI, + bool LookThroughBitCast = false); + /// Tests if a value is a call or invoke to a library function that /// allocates zero-filled memory (such as calloc). bool isCallocLikeFn(const Value *V, const TargetLibraryInfo *TLI, diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.def b/llvm/include/llvm/Analysis/TargetLibraryInfo.def --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.def +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.def @@ -481,6 +481,9 @@ /// long double acosl(long double x); TLI_DEFINE_ENUM_INTERNAL(acosl) TLI_DEFINE_STRING_INTERNAL("acosl") +/// void *aligned_alloc(size_t alignment, size_t size); +TLI_DEFINE_ENUM_INTERNAL(aligned_alloc) +TLI_DEFINE_STRING_INTERNAL("aligned_alloc") /// double asin(double x); TLI_DEFINE_ENUM_INTERNAL(asin) TLI_DEFINE_STRING_INTERNAL("asin") diff --git a/llvm/lib/Analysis/BasicAliasAnalysis.cpp b/llvm/lib/Analysis/BasicAliasAnalysis.cpp --- a/llvm/lib/Analysis/BasicAliasAnalysis.cpp +++ b/llvm/lib/Analysis/BasicAliasAnalysis.cpp @@ -960,7 +960,7 @@ } } - // If the call is to malloc or calloc, we can assume that it doesn't + // If the call is malloc/calloc like, we can assume that it doesn't // modify any IR visible value. This is only valid because we assume these // routines do not read values visible in the IR. TODO: Consider special // casing realloc and strdup routines which access only their arguments as 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 @@ -50,14 +50,15 @@ #define DEBUG_TYPE "memory-builtins" enum AllocType : uint8_t { - OpNewLike = 1<<0, // allocates; never returns null - MallocLike = 1<<1 | OpNewLike, // allocates; may return null - CallocLike = 1<<2, // allocates + bzero - ReallocLike = 1<<3, // reallocates - StrDupLike = 1<<4, - MallocOrCallocLike = MallocLike | CallocLike, - AllocLike = MallocLike | CallocLike | StrDupLike, - AnyAlloc = AllocLike | ReallocLike + OpNewLike = 1 << 0, // allocates; never returns null + MallocLike = 1 << 1 | OpNewLike, // 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, + AllocLike = MallocOrCallocLike | StrDupLike, + AnyAlloc = AllocLike | ReallocLike }; struct AllocFnsTy { @@ -70,42 +71,61 @@ // 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_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_calloc, {CallocLike, 2, 0, 1}}, - {LibFunc_realloc, {ReallocLike, 2, 1, -1}}, - {LibFunc_reallocf, {ReallocLike, 2, 1, -1}}, - {LibFunc_strdup, {StrDupLike, 1, -1, -1}}, - {LibFunc_strndup, {StrDupLike, 2, 1, -1}} - // TODO: Handle "int posix_memalign(void **, size_t, size_t)" + {LibFunc_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_calloc, {CallocLike, 2, 0, 1}}, + {LibFunc_realloc, {ReallocLike, 2, 1, -1}}, + {LibFunc_reallocf, {ReallocLike, 2, 1, -1}}, + {LibFunc_strdup, {StrDupLike, 1, -1, -1}}, + {LibFunc_strndup, {StrDupLike, 2, 1, -1}} + // TODO: Handle "int posix_memalign(void **, size_t, size_t)" }; static const Function *getCalledFunction(const Value *V, bool LookThroughBitCast, @@ -265,6 +285,20 @@ .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 GetTLI, + bool LookThroughBitCast) { + return getAllocationData(V, AlignedAllocLike, GetTLI, LookThroughBitCast) + .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, diff --git a/llvm/lib/Analysis/TargetLibraryInfo.cpp b/llvm/lib/Analysis/TargetLibraryInfo.cpp --- a/llvm/lib/Analysis/TargetLibraryInfo.cpp +++ b/llvm/lib/Analysis/TargetLibraryInfo.cpp @@ -901,6 +901,8 @@ FTy.getParamType(1)->isPointerTy()); case LibFunc_write: return (NumParams == 3 && FTy.getParamType(1)->isPointerTy()); + case LibFunc_aligned_alloc: + return (NumParams == 2 && FTy.getReturnType()->isPointerTy()); case LibFunc_bcopy: case LibFunc_bcmp: return (NumParams == 3 && FTy.getParamType(0)->isPointerTy() && diff --git a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp --- a/llvm/lib/Transforms/Utils/BuildLibCalls.cpp +++ b/llvm/lib/Transforms/Utils/BuildLibCalls.cpp @@ -378,6 +378,10 @@ Changed |= setDoesNotCapture(F, 1); Changed |= setOnlyReadsMemory(F, 1); return Changed; + case LibFunc_aligned_alloc: + Changed |= setDoesNotThrow(F); + Changed |= setRetDoesNotAlias(F); + return Changed; case LibFunc_bcopy: Changed |= setDoesNotThrow(F); Changed |= setDoesNotCapture(F, 0); diff --git a/llvm/test/Transforms/DeadStoreElimination/simple.ll b/llvm/test/Transforms/DeadStoreElimination/simple.ll --- a/llvm/test/Transforms/DeadStoreElimination/simple.ll +++ b/llvm/test/Transforms/DeadStoreElimination/simple.ll @@ -259,6 +259,8 @@ declare noalias i8* @malloc(i32) declare noalias i8* @calloc(i32, i32) +declare noalias i8* @aligned_alloc(i32, i32) +declare void @free(i8*) define void @test14(i32* %Q) { @@ -272,6 +274,17 @@ } +; Dead store on an aligned_alloc: should know that %M doesn't alias with %A. +define i32 @test14a(i8* %M, i8 %value) { +; CHECK-LABEL: @test14a( +; CHECK-NOT: store +; CHECK: ret i32 0 +; + %A = tail call i8* @aligned_alloc(i32 32, i32 1024) + store i8 %value, i8* %A + tail call void @free(i8* %A) + ret i32 0 +} ; PR8701 diff --git a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp --- a/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp +++ b/llvm/unittests/Analysis/TargetLibraryInfoTest.cpp @@ -96,6 +96,7 @@ "declare float @acoshf(float)\n" "declare x86_fp80 @acoshl(x86_fp80)\n" "declare x86_fp80 @acosl(x86_fp80)\n" + "declare i8* @aligned_alloc(i64, i64)\n" "declare double @asin(double)\n" "declare float @asinf(float)\n" "declare double @asinh(double)\n" @@ -509,7 +510,8 @@ "declare i32 @iprintf(i8*, ...)\n" "declare i32 @siprintf(i8*, i8*, ...)\n" - // __small_printf variants have the same prototype as the non-'i' versions. + // __small_printf variants have the same prototype as the non-'i' + // versions. "declare i32 @__small_fprintf(%struct*, i8*, ...)\n" "declare i32 @__small_printf(i8*, ...)\n" "declare i32 @__small_sprintf(i8*, i8*, ...)\n" @@ -567,8 +569,7 @@ "declare x86_fp80 @__powl_finite(x86_fp80, x86_fp80)\n" "declare double @__sinh_finite(double)\n" "declare float @__sinhf_finite(float)\n" - "declare x86_fp80 @__sinhl_finite(x86_fp80)\n" - ); + "declare x86_fp80 @__sinhl_finite(x86_fp80)\n"); for (unsigned FI = 0; FI != LibFunc::NumLibFuncs; ++FI) { LibFunc LF = (LibFunc)FI;