Index: llvm/include/llvm/Analysis/MemoryBuiltins.h =================================================================== --- llvm/include/llvm/Analysis/MemoryBuiltins.h +++ 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. // Index: llvm/lib/Analysis/MemoryBuiltins.cpp =================================================================== --- llvm/lib/Analysis/MemoryBuiltins.cpp +++ 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, "vec_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, "_ZnwmSt11align_val_t"}}, // new(unsigned int, align_val_t) + {LibFunc_ZnwjSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1, "_ZnwmSt11align_val_t"}}, // 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, "_ZnwmSt11align_val_t"}}, // new(unsigned long, align_val_t) + {LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1, "_ZnwmSt11align_val_t"}}, // new(unsigned long, align_val_t, nothrow) + {LibFunc_Znaj, {OpNewLike, 1, 0, -1, -1, "_Znam"}}, // new[](unsigned int) + {LibFunc_ZnajRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1, "_Znam"}}, // new[](unsigned int, nothrow) + {LibFunc_ZnajSt11align_val_t, {OpNewLike, 2, 0, -1, 1, "_ZnamSt11align_val_t"}}, // new[](unsigned int, align_val_t) + {LibFunc_ZnajSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1, "_ZnamSt11align_val_t"}}, // new[](unsigned int, align_val_t, nothrow) + {LibFunc_Znam, {OpNewLike, 1, 0, -1, -1, "_Znam"}}, // new[](unsigned long) + {LibFunc_ZnamRKSt9nothrow_t, {MallocLike, 2, 0, -1, -1, "_Znam"}}, // new[](unsigned long, nothrow) + {LibFunc_ZnamSt11align_val_t, {OpNewLike, 2, 0, -1, 1, "_ZnamSt11align_val_t"}}, // new[](unsigned long, align_val_t) + {LibFunc_ZnamSt11align_val_tRKSt9nothrow_t, {MallocLike, 3, 0, -1, 1, "_ZnamSt11align_val_t"}}, // new[](unsigned long, align_val_t, nothrow) + {LibFunc_msvc_new_int, {OpNewLike, 1, 0, -1, -1, "??2@YAPAXI@Z"}}, // new(unsigned int) + {LibFunc_msvc_new_int_nothrow, {MallocLike, 2, 0, -1, -1, "??2@YAPAXI@Z"}}, // new(unsigned int, nothrow) + {LibFunc_msvc_new_longlong, {OpNewLike, 1, 0, -1, -1, "??2@YAPAXI@Z"}}, // new(unsigned long long) + {LibFunc_msvc_new_longlong_nothrow, {MallocLike, 2, 0, -1, -1, "??2@YAPAXI@Z"}}, // new(unsigned long long, nothrow) + {LibFunc_msvc_new_array_int, {OpNewLike, 1, 0, -1, -1, "??_U@YAPAXI@Z"}}, // new[](unsigned int) + {LibFunc_msvc_new_array_int_nothrow, {MallocLike, 2, 0, -1, -1, "??_U@YAPAXI@Z"}}, // new[](unsigned int, nothrow) + {LibFunc_msvc_new_array_longlong, {OpNewLike, 1, 0, -1, -1, "??_U@YAPAXI@Z"}}, // new[](unsigned long long) + {LibFunc_msvc_new_array_longlong_nothrow, {MallocLike, 2, 0, -1, -1, "??_U@YAPAXI@Z"}}, // 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, "vec_malloc"}}, + {LibFunc_realloc, {ReallocLike, 2, 1, -1, -1, "malloc"}}, + {LibFunc_vec_realloc, {ReallocLike, 2, 1, -1, -1, "vec_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,79 @@ 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, "_Znam"}}, // operator delete[](void*) + {LibFunc_msvc_delete_ptr32, {1, "??2@YAPAXI@Z"}}, // operator delete(void*) + {LibFunc_msvc_delete_ptr64, {1, "??2@YAPAXI@Z"}}, // operator delete(void*) + {LibFunc_msvc_delete_array_ptr32, {1, "??_U@YAPAXI@Z"}}, // operator delete[](void*) + {LibFunc_msvc_delete_array_ptr64, {1, "??_U@YAPAXI@Z"}}, // 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, "_ZnwmSt11align_val_t"}}, // delete(void*, align_val_t) + {LibFunc_ZdaPvj, {2, "_Znam"}}, // delete[](void*, uint) + {LibFunc_ZdaPvm, {2, "_Znam"}}, // delete[](void*, ulong) + {LibFunc_ZdaPvRKSt9nothrow_t, {2, "_Znam"}}, // delete[](void*, nothrow) + {LibFunc_ZdaPvSt11align_val_t, {2, "_ZnamSt11align_val_t"}}, // delete[](void*, align_val_t) + {LibFunc_msvc_delete_ptr32_int, {2, "??2@YAPAXI@Z"}}, // delete(void*, uint) + {LibFunc_msvc_delete_ptr64_longlong, {2, "??2@YAPAXI@Z"}}, // delete(void*, ulonglong) + {LibFunc_msvc_delete_ptr32_nothrow, {2, "??2@YAPAXI@Z"}}, // delete(void*, nothrow) + {LibFunc_msvc_delete_ptr64_nothrow, {2, "??2@YAPAXI@Z"}}, // delete(void*, nothrow) + {LibFunc_msvc_delete_array_ptr32_int, {2, "??_U@YAPAXI@Z"}}, // delete[](void*, uint) + {LibFunc_msvc_delete_array_ptr64_longlong, {2, "??_U@YAPAXI@Z"}}, // delete[](void*, ulonglong) + {LibFunc_msvc_delete_array_ptr32_nothrow, {2, "??_U@YAPAXI@Z"}}, // delete[](void*, nothrow) + {LibFunc_msvc_delete_array_ptr64_nothrow, {2, "??_U@YAPAXI@Z"}}, // delete[](void*, nothrow) + {LibFunc___kmpc_free_shared, {2, "__kmpc_alloc_shared"}}, // OpenMP Offloading RTL free + {LibFunc_ZdlPvSt11align_val_tRKSt9nothrow_t, {3, "_ZnwmSt11align_val_t"}}, // delete(void*, align_val_t, nothrow) + {LibFunc_ZdaPvSt11align_val_tRKSt9nothrow_t, {3, "_ZnamSt11align_val_t"}}, // delete[](void*, align_val_t, nothrow) + {LibFunc_ZdlPvjSt11align_val_t, {3, "_ZnwmSt11align_val_t"}}, // delete(void*, unsigned int, align_val_t) + {LibFunc_ZdlPvmSt11align_val_t, {3, "_ZnwmSt11align_val_t"}}, // delete(void*, unsigned long, align_val_t) + {LibFunc_ZdaPvjSt11align_val_t, {3, "_ZnamSt11align_val_t"}}, // delete[](void*, unsigned int, align_val_t) + {LibFunc_ZdaPvmSt11align_val_t, {3, "_ZnamSt11align_val_t"}}, // 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) { +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)) { + 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 Index: llvm/lib/Transforms/InstCombine/InstructionCombining.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ 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,8 @@ } if (isFreeCall(I, &TLI)) { + if (getAllocationFamily(I, &TLI) != Family) + return false; Users.emplace_back(I); continue; } Index: llvm/test/Transforms/InstCombine/malloc-free-mismatched.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/malloc-free-mismatched.ll @@ -0,0 +1,24 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s + +define dso_local i32 @_Z6answeri(i32 %0) { +; CHECK-LABEL: @_Z6answeri( +; CHECK-NEXT: [[TMP2:%.*]] = call noalias nonnull dereferenceable(80) i8* @_Znam(i64 80) #[[ATTR1:[0-9]+]] +; CHECK-NEXT: call void @free(i8* [[TMP2]]) +; CHECK-NEXT: ret i32 42 +; + %2 = call noalias nonnull i8* @_Znam(i64 80) #0 + %3 = bitcast i8* %2 to i32* + %4 = bitcast i32* %3 to i8* + call void @free(i8* %4) + ret i32 42 +} + +; Function Attrs: nobuiltin allocsize(0) +declare dso_local nonnull i8* @_Znam(i64) #1 + +; Function Attrs: nounwind +declare dso_local void @free(i8*) + +attributes #0 = { builtin allocsize(0) } +attributes #1 = { nobuiltin allocsize(0) }