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 @@ -123,6 +123,12 @@ const TargetLibraryInfo *TLI, Type *Ty); +/// If a function is part of an allocation family (e.g. +/// malloc/realloc/calloc/free), return the identifier for its family +/// of functions. +Optional getAllocationFamily(const Value *I, + const TargetLibraryInfo *TLI); + //===----------------------------------------------------------------------===// // Utility functions to compute size of objects. // 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 @@ -43,6 +43,7 @@ #include #include #include +#include #include using namespace llvm; @@ -69,50 +70,54 @@ int FstParam, SndParam; // Alignment parameter for aligned_alloc and aligned new int AlignParam; + // Name of default allocator function to group malloc/free calls by family + StringRef Family; }; +// clang-format off // 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, -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}}, + {LibFunc_malloc, {MallocLike, 1, 0, -1, -1, "malloc"}}, + {LibFunc_vec_malloc, {MallocLike, 1, 0, -1, -1, "malloc"}}, + {LibFunc_valloc, {MallocLike, 1, 0, -1, -1, "malloc"}}, + {LibFunc_Znwj, {OpNewLike, 1, 0, -1, -1, "Znwm"}}, // new(unsigned int) + {LibFunc_ZnwjRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1, "Znwm"}}, // new(unsigned int, nothrow) + {LibFunc_ZnwjSt11align_val_t, {OpNewLike, 2, 0, -1, 1, "Znwm"}}, // new(unsigned int, align_val_t) + {LibFunc_ZnwjSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1, "Znwm"}}, // new(unsigned int, align_val_t, nothrow) + {LibFunc_Znwm, {OpNewLike, 1, 0, -1, -1, "Znwm"}}, // new(unsigned long) + {LibFunc_ZnwmRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1, "Znwm"}}, // new(unsigned long, nothrow) + {LibFunc_ZnwmSt11align_val_t, {OpNewLike, 2, 0, -1, 1, "Znwm"}}, // new(unsigned long, align_val_t) + {LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1, "Znwm"}}, // new(unsigned long, align_val_t, nothrow) + {LibFunc_Znaj, {OpNewLike, 1, 0, -1, -1, "Znwm"}}, // new[](unsigned int) + {LibFunc_ZnajRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1, "Znwm"}}, // new[](unsigned int, nothrow) + {LibFunc_ZnajSt11align_val_t, {OpNewLike, 2, 0, -1, 1, "Znwm"}}, // new[](unsigned int, align_val_t) + {LibFunc_ZnajSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1, "Znwm"}}, // new[](unsigned int, align_val_t, nothrow) + {LibFunc_Znam, {OpNewLike, 1, 0, -1, -1, "Znwm"}}, // new[](unsigned long) + {LibFunc_ZnamRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1, "Znwm"}}, // new[](unsigned long, nothrow) + {LibFunc_ZnamSt11align_val_t, {OpNewLike, 2, 0, -1, 1, "Znwm"}}, // new[](unsigned long, align_val_t) + {LibFunc_ZnamSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1, "Znwm"}}, // new[](unsigned long, align_val_t, nothrow) + {LibFunc_msvc_new_int, {OpNewLike, 1, 0, -1, -1, "msvc_new_int"}}, // new(unsigned int) + {LibFunc_msvc_new_int_nothrow, {MallocLike, 2, 0, -1, -1, "msvc_new_int"}}, // new(unsigned int, nothrow) + {LibFunc_msvc_new_longlong, {OpNewLike, 1, 0, -1, -1, "msvc_new_int"}}, // new(unsigned long long) + {LibFunc_msvc_new_longlong_nothrow, {MallocLike, 2, 0, -1, -1, "msvc_new_int"}}, // new(unsigned long long, nothrow) + {LibFunc_msvc_new_array_int, {OpNewLike, 1, 0, -1, -1, "msvc_new_int"}}, // new[](unsigned int) + {LibFunc_msvc_new_array_int_nothrow, {MallocLike, 2, 0, -1, -1, "msvc_new_int"}}, // new[](unsigned int, nothrow) + {LibFunc_msvc_new_array_longlong, {OpNewLike, 1, 0, -1, -1, "msvc_new_int"}}, // new[](unsigned long long) + {LibFunc_msvc_new_array_longlong_nothrow, {MallocLike, 2, 0, -1, -1, "msvc_new_int"}}, // new[](unsigned long long, nothrow) + {LibFunc_aligned_alloc, {AlignedAllocLike, 2, 1, -1, 0, "malloc"}}, + {LibFunc_memalign, {AlignedAllocLike, 2, 1, -1, 0, "malloc"}}, + {LibFunc_calloc, {CallocLike, 2, 0, 1, -1, "malloc"}}, + {LibFunc_vec_calloc, {CallocLike, 2, 0, 1, -1, "malloc"}}, + {LibFunc_realloc, {ReallocLike, 2, 1, -1, -1, "malloc"}}, + {LibFunc_vec_realloc, {ReallocLike, 2, 1, -1, -1, "malloc"}}, + {LibFunc_reallocf, {ReallocLike, 2, 1, -1, -1, "malloc"}}, + {LibFunc_strdup, {StrDupLike, 1, -1, -1, -1, "malloc"}}, + {LibFunc_strndup, {StrDupLike, 2, 1, -1, -1, "malloc"}}, + {LibFunc___kmpc_alloc_shared, {MallocLike, 1, 0, -1, -1, "__kmpc_alloc_shared"}}, // TODO: Handle "int posix_memalign(void **, size_t, size_t)" }; +// clang-format on static const Function *getCalledFunction(const Value *V, bool &IsNoBuiltin) { @@ -402,53 +407,83 @@ struct FreeFnsTy { unsigned NumParams; + // Name of default allocator function to group malloc/free calls by family + StringRef Family; }; // clang-format off static const std::pair FreeFnData[] = { - {LibFunc_free, {1}}, - {LibFunc_ZdlPv, {1}}, // operator delete(void*) - {LibFunc_ZdaPv, {1}}, // operator delete[](void*) - {LibFunc_msvc_delete_ptr32, {1}}, // operator delete(void*) - {LibFunc_msvc_delete_ptr64, {1}}, // operator delete(void*) - {LibFunc_msvc_delete_array_ptr32, {1}}, // operator delete[](void*) - {LibFunc_msvc_delete_array_ptr64, {1}}, // operator delete[](void*) - {LibFunc_ZdlPvj, {2}}, // delete(void*, uint) - {LibFunc_ZdlPvm, {2}}, // delete(void*, ulong) - {LibFunc_ZdlPvRKSt9nothrow_t, {2}}, // delete(void*, nothrow) - {LibFunc_ZdlPvSt11align_val_t, {2}}, // delete(void*, align_val_t) - {LibFunc_ZdaPvj, {2}}, // delete[](void*, uint) - {LibFunc_ZdaPvm, {2}}, // delete[](void*, ulong) - {LibFunc_ZdaPvRKSt9nothrow_t, {2}}, // delete[](void*, nothrow) - {LibFunc_ZdaPvSt11align_val_t, {2}}, // delete[](void*, align_val_t) - {LibFunc_msvc_delete_ptr32_int, {2}}, // delete(void*, uint) - {LibFunc_msvc_delete_ptr64_longlong, {2}}, // delete(void*, ulonglong) - {LibFunc_msvc_delete_ptr32_nothrow, {2}}, // delete(void*, nothrow) - {LibFunc_msvc_delete_ptr64_nothrow, {2}}, // delete(void*, nothrow) - {LibFunc_msvc_delete_array_ptr32_int, {2}}, // delete[](void*, uint) - {LibFunc_msvc_delete_array_ptr64_longlong, {2}}, // delete[](void*, ulonglong) - {LibFunc_msvc_delete_array_ptr32_nothrow, {2}}, // delete[](void*, nothrow) - {LibFunc_msvc_delete_array_ptr64_nothrow, {2}}, // delete[](void*, nothrow) - {LibFunc___kmpc_free_shared, {2}}, // OpenMP Offloading RTL free - {LibFunc_ZdaPvSt11align_val_tRKSt9nothrow_t, {3}}, // delete(void*, align_val_t, nothrow) - {LibFunc_ZdlPvSt11align_val_tRKSt9nothrow_t, {3}}, // delete[](void*, align_val_t, nothrow) - {LibFunc_ZdlPvjSt11align_val_t, {3}}, // delete(void*, unsigned long, align_val_t) - {LibFunc_ZdlPvmSt11align_val_t, {3}}, // delete(void*, unsigned long, align_val_t) - {LibFunc_ZdaPvjSt11align_val_t, {3}}, // delete[](void*, unsigned int, align_val_t) - {LibFunc_ZdaPvmSt11align_val_t, {3}}, // delete[](void*, unsigned long, align_val_t) + {LibFunc_free, {1, "malloc"}}, + {LibFunc_ZdlPv, {1, "Znwm"}}, // operator delete(void*) + {LibFunc_ZdaPv, {1, "Znwm"}}, // operator delete[](void*) + {LibFunc_msvc_delete_ptr32, {1, "msvc_new_int"}}, // operator delete(void*) + {LibFunc_msvc_delete_ptr64, {1, "msvc_new_int"}}, // operator delete(void*) + {LibFunc_msvc_delete_array_ptr32, {1, "msvc_new_int"}}, // operator delete[](void*) + {LibFunc_msvc_delete_array_ptr64, {1, "msvc_new_int"}}, // operator delete[](void*) + {LibFunc_ZdlPvj, {2, "Znwm"}}, // delete(void*, uint) + {LibFunc_ZdlPvm, {2, "Znwm"}}, // delete(void*, ulong) + {LibFunc_ZdlPvRKSt9nothrow_t, {2, "Znwm"}}, // delete(void*, nothrow) + {LibFunc_ZdlPvSt11align_val_t, {2, "Znwm"}}, // delete(void*, align_val_t) + {LibFunc_ZdaPvj, {2, "Znwm"}}, // delete[](void*, uint) + {LibFunc_ZdaPvm, {2, "Znwm"}}, // delete[](void*, ulong) + {LibFunc_ZdaPvRKSt9nothrow_t, {2, "Znwm"}}, // delete[](void*, nothrow) + {LibFunc_ZdaPvSt11align_val_t, {2, "Znwm"}}, // delete[](void*, align_val_t) + {LibFunc_msvc_delete_ptr32_int, {2, "msvc_new_int"}}, // delete(void*, uint) + {LibFunc_msvc_delete_ptr64_longlong, {2, "msvc_new_int"}}, // delete(void*, ulonglong) + {LibFunc_msvc_delete_ptr32_nothrow, {2, "msvc_new_int"}}, // delete(void*, nothrow) + {LibFunc_msvc_delete_ptr64_nothrow, {2, "msvc_new_int"}}, // delete(void*, nothrow) + {LibFunc_msvc_delete_array_ptr32_int, {2, "msvc_new_int"}}, // delete[](void*, uint) + {LibFunc_msvc_delete_array_ptr64_longlong, {2, "msvc_new_int"}}, // delete[](void*, ulonglong) + {LibFunc_msvc_delete_array_ptr32_nothrow, {2, "msvc_new_int"}}, // delete[](void*, nothrow) + {LibFunc_msvc_delete_array_ptr64_nothrow, {2, "msvc_new_int"}}, // delete[](void*, nothrow) + {LibFunc___kmpc_free_shared, {2, "__kmpc_alloc_shared"}}, // OpenMP Offloading RTL free + {LibFunc_ZdaPvSt11align_val_tRKSt9nothrow_t, {3, "Znwm"}}, // delete(void*, align_val_t, nothrow) + {LibFunc_ZdlPvSt11align_val_tRKSt9nothrow_t, {3, "Znwm"}}, // delete[](void*, align_val_t, nothrow) + {LibFunc_ZdlPvjSt11align_val_t, {3, "Znwm"}}, // delete(void*, unsigned long, align_val_t) + {LibFunc_ZdlPvmSt11align_val_t, {3, "Znwm"}}, // delete(void*, unsigned long, align_val_t) + {LibFunc_ZdaPvjSt11align_val_t, {3, "Znwm"}}, // delete[](void*, unsigned int, align_val_t) + {LibFunc_ZdaPvmSt11align_val_t, {3, "Znwm"}}, // delete[](void*, unsigned long, align_val_t) }; // clang-format on -/// isLibFreeFunction - Returns true if the function is a builtin free() -bool llvm::isLibFreeFunction(const Function *F, const LibFunc TLIFn) { - const auto *Iter = +Optional getFreeFunctionDataForFunction(const Function *Callee, const LibFunc TLIFn) { + const auto *Iter = find_if(FreeFnData, [TLIFn](const std::pair &P) { return P.first == TLIFn; }); if (Iter == std::end(FreeFnData)) { + return None; + } + return Iter->second; +} + +Optional llvm::getAllocationFamily(const Value *I, const TargetLibraryInfo *TLI) { + bool IsNoBuiltin; + const Function *Callee = getCalledFunction(I, IsNoBuiltin); + if (Callee == nullptr) { + return None; + } + LibFunc TLIFn; + if (!TLI || !TLI->getLibFunc(*Callee, TLIFn) || !TLI->has(TLIFn)) { + return None; +} + const auto AllocData = getAllocationDataForFunction(Callee, AnyAlloc, TLI); + if (AllocData.hasValue()) { + return AllocData.getValue().Family; + } + const auto FreeData = getFreeFunctionDataForFunction(Callee, TLIFn); + if (FreeData.hasValue()) { + return FreeData.getValue().Family; + } + return None; +} + +/// isLibFreeFunction - Returns true if the function is a builtin free() +bool llvm::isLibFreeFunction(const Function *F, const LibFunc TLIFn) { + Optional FnData = getFreeFunctionDataForFunction(F, TLIFn); + if (!FnData.hasValue()) { return false; } - const FreeFnsTy *FnData = &Iter->second; // Check free prototype. // FIXME: workaround for PR5130, this will be obsolete when a nobuiltin diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -2593,6 +2593,7 @@ SmallVectorImpl &Users, const TargetLibraryInfo &TLI) { SmallVector Worklist; + const Optional Family = getAllocationFamily(AI, &TLI); Worklist.push_back(AI); do { @@ -2662,6 +2663,9 @@ } if (isFreeCall(I, &TLI)) { + if (getAllocationFamily(I, &TLI) != Family) { + return false; + } Users.emplace_back(I); continue; } diff --git a/llvm/test/Transforms/InstCombine/malloc-free-mismatched.ll b/llvm/test/Transforms/InstCombine/malloc-free-mismatched.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/malloc-free-mismatched.ll @@ -0,0 +1,36 @@ +; RUN: opt < %s -instcombine -S | FileCheck %s +; CHECK-LABEL: @_Z6answeri +; CHECK: call {{.*}} @_Znam +; CHECK: call void @free +define dso_local i32 @_Z6answeri(i32 %0) #0 { + %2 = alloca i32, align 4 + %3 = alloca i32*, align 8 + %4 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + %5 = call noalias nonnull i8* @_Znam(i64 80) #4 + %6 = bitcast i8* %5 to i32* + store i32* %6, i32** %3, align 8 + store i32 42, i32* %4, align 4 + %7 = load i32*, i32** %3, align 8 + %8 = bitcast i32* %7 to i8* + ; This is a free() of a pointer that was created with ::operator::new, so + ; it shouldn't be optimized away. This can happen in real life without + ; being UB in the case that the definition of ::operator::delete somehow + ; gets inlined and devolves to free(). + call void @free(i8* %8) #5 + %9 = load i32, i32* %4, align 4 + ret i32 %9 +} + +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +declare dso_local nonnull i8* @_Znam(i64) #2 + +declare dso_local void @free(i8*) #3 + +attributes #0 = { mustprogress noinline uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #2 = { nobuiltin allocsize(0) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { nounwind "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #4 = { builtin allocsize(0) } +attributes #5 = { nounwind }