diff --git a/llvm/include/llvm/Transforms/IPO/Attributor.h b/llvm/include/llvm/Transforms/IPO/Attributor.h --- a/llvm/include/llvm/Transforms/IPO/Attributor.h +++ b/llvm/include/llvm/Transforms/IPO/Attributor.h @@ -172,7 +172,8 @@ const Optional &B, Type *Ty); /// Return the initial value of \p Obj with type \p Ty if that is a constant. -Constant *getInitialValueForObj(Value &Obj, Type &Ty); +Constant *getInitialValueForObj(Value &Obj, Type &Ty, + const TargetLibraryInfo *TLI); /// Collect all potential underlying objects of \p Ptr at position \p CtxI in /// \p Objects. Assumed information is used and dependences onto \p QueryingAA diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/TinyPtrVector.h" #include "llvm/Analysis/InlineCost.h" #include "llvm/Analysis/LazyValueInfo.h" +#include "llvm/Analysis/MemoryBuiltins.h" #include "llvm/Analysis/MemorySSAUpdater.h" #include "llvm/Analysis/MustExecute.h" #include "llvm/Analysis/ValueTracking.h" @@ -202,9 +203,17 @@ return NoRecurseAA.isAssumedNoRecurse(); } -Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty) { +Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty, + const TargetLibraryInfo *TLI) { if (isa(Obj)) return UndefValue::get(&Ty); + if (isNoAliasFn(&Obj, TLI)) { + if (isMallocLikeFn(&Obj, TLI) || isAlignedAllocLikeFn(&Obj, TLI)) + return UndefValue::get(&Ty); + if (isCallocLikeFn(&Obj, TLI)) + return Constant::getNullValue(&Ty); + return nullptr; + } auto *GV = dyn_cast(&Obj); if (!GV || !GV->hasLocalLinkage()) return nullptr; @@ -300,6 +309,8 @@ SmallVector PIs; SmallVector NewCopies; + const auto *TLI = + A.getInfoCache().getTargetLibraryInfoForFunction(*SI.getFunction()); for (Value *Obj : Objects) { LLVM_DEBUG(dbgs() << "Visit underlying object " << *Obj << "\n"); if (isa(Obj)) @@ -316,7 +327,8 @@ dbgs() << "Underlying object is a valid nullptr, giving up.\n";); return false; } - if (!isa(Obj) && !isa(Obj)) { + if (!isa(Obj) && !isa(Obj) && + !isNoAliasFn(Obj, TLI)) { LLVM_DEBUG(dbgs() << "Underlying object is not supported yet: " << *Obj << "\n";); return false; 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 @@ -1175,6 +1175,10 @@ return true; }; + const auto *TLI = getAnchorScope() + ? A.getInfoCache().getTargetLibraryInfoForFunction( + *getAnchorScope()) + : nullptr; auto UsePred = [&](const Use &U, bool &Follow) -> bool { Value *CurPtr = U.get(); User *Usr = U.getUser(); @@ -1285,6 +1289,8 @@ if (auto *CB = dyn_cast(Usr)) { if (CB->isLifetimeStartOrEnd()) return true; + if (TLI && isFreeCall(CB, TLI)) + return true; if (CB->isArgOperand(&U)) { unsigned ArgNo = CB->getArgOperandNo(&U); const auto &CSArgPI = A.getAAFor( @@ -5245,6 +5251,8 @@ if (!AA::getAssumedUnderlyingObjects(A, Ptr, Objects, AA, &L)) return false; + const auto *TLI = + A.getInfoCache().getTargetLibraryInfoForFunction(*L.getFunction()); for (Value *Obj : Objects) { LLVM_DEBUG(dbgs() << "Visit underlying object " << *Obj << "\n"); if (isa(Obj)) @@ -5259,9 +5267,10 @@ continue; return false; } - if (!isa(Obj) && !isa(Obj)) + if (!isa(Obj) && !isa(Obj) && + !isNoAliasFn(Obj, TLI)) return false; - Constant *InitialVal = AA::getInitialValueForObj(*Obj, *L.getType()); + Constant *InitialVal = AA::getInitialValueForObj(*Obj, *L.getType(), TLI); if (!InitialVal || !Union(*InitialVal)) return false; diff --git a/llvm/test/Transforms/Attributor/heap_to_stack_gpu.ll b/llvm/test/Transforms/Attributor/heap_to_stack_gpu.ll --- a/llvm/test/Transforms/Attributor/heap_to_stack_gpu.ll +++ b/llvm/test/Transforms/Attributor/heap_to_stack_gpu.ll @@ -494,7 +494,6 @@ ; CHECK: 8: ; CHECK-NEXT: [[TMP9:%.*]] = call noalias i8* @malloc(i64 noundef 4) ; CHECK-NEXT: [[TMP10:%.*]] = bitcast i8* [[TMP9]] to i32* -; CHECK-NEXT: store i32 1, i32* [[TMP10]], align 8 ; CHECK-NEXT: br label [[TMP4]] ; CHECK: 11: ; CHECK-NEXT: ret i32 5 diff --git a/llvm/test/Transforms/Attributor/memory_locations.ll b/llvm/test/Transforms/Attributor/memory_locations.ll --- a/llvm/test/Transforms/Attributor/memory_locations.ll +++ b/llvm/test/Transforms/Attributor/memory_locations.ll @@ -122,11 +122,18 @@ define dso_local i8* @internal_only_rec_static_helper_malloc_noescape(i32 %arg) { ; FIXME: This is actually inaccessiblememonly because the malloced memory does not escape -; CHECK-LABEL: define {{[^@]+}}@internal_only_rec_static_helper_malloc_noescape -; CHECK-SAME: (i32 [[ARG:%.*]]) { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec_static_malloc_noescape(i32 [[ARG]]) -; CHECK-NEXT: ret i8* [[CALL]] +; IS__TUNIT____-LABEL: define {{[^@]+}}@internal_only_rec_static_helper_malloc_noescape +; IS__TUNIT____-SAME: (i32 [[ARG:%.*]]) { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec_static_malloc_noescape(i32 [[ARG]]) +; IS__TUNIT____-NEXT: ret i8* [[CALL]] +; +; IS__CGSCC____: Function Attrs: inaccessiblememonly +; IS__CGSCC____-LABEL: define {{[^@]+}}@internal_only_rec_static_helper_malloc_noescape +; IS__CGSCC____-SAME: (i32 [[ARG:%.*]]) #[[ATTR0]] { +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec_static_malloc_noescape(i32 [[ARG]]) +; IS__CGSCC____-NEXT: ret i8* [[CALL]] ; entry: %call = call i8* @internal_only_rec_static_malloc_noescape(i32 %arg) @@ -135,24 +142,42 @@ define internal i8* @internal_only_rec_static_malloc_noescape(i32 %arg) { ; FIXME: This is actually inaccessiblememonly because the malloced memory does not escape -; CHECK-LABEL: define {{[^@]+}}@internal_only_rec_static_malloc_noescape -; CHECK-SAME: (i32 [[ARG:%.*]]) { -; CHECK-NEXT: entry: -; CHECK-NEXT: [[REM:%.*]] = srem i32 [[ARG]], 2 -; CHECK-NEXT: [[CMP:%.*]] = icmp eq i32 [[REM]], 1 -; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] -; CHECK: if.then: -; CHECK-NEXT: [[DIV:%.*]] = sdiv i32 [[ARG]], 2 -; CHECK-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec(i32 [[DIV]]) -; CHECK-NEXT: br label [[RETURN:%.*]] -; CHECK: if.end: -; CHECK-NEXT: [[CONV:%.*]] = sext i32 [[ARG]] to i64 -; CHECK-NEXT: [[CALL1:%.*]] = call noalias i8* @malloc(i64 [[CONV]]) -; CHECK-NEXT: store i8 0, i8* [[CALL1]], align 1 -; CHECK-NEXT: br label [[RETURN]] -; CHECK: return: -; CHECK-NEXT: [[RETVAL_0:%.*]] = phi i8* [ [[CALL]], [[IF_THEN]] ], [ null, [[IF_END]] ] -; CHECK-NEXT: ret i8* [[RETVAL_0]] +; IS__TUNIT____-LABEL: define {{[^@]+}}@internal_only_rec_static_malloc_noescape +; IS__TUNIT____-SAME: (i32 [[ARG:%.*]]) { +; IS__TUNIT____-NEXT: entry: +; IS__TUNIT____-NEXT: [[REM:%.*]] = srem i32 [[ARG]], 2 +; IS__TUNIT____-NEXT: [[CMP:%.*]] = icmp eq i32 [[REM]], 1 +; IS__TUNIT____-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; IS__TUNIT____: if.then: +; IS__TUNIT____-NEXT: [[DIV:%.*]] = sdiv i32 [[ARG]], 2 +; IS__TUNIT____-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec(i32 [[DIV]]) +; IS__TUNIT____-NEXT: br label [[RETURN:%.*]] +; IS__TUNIT____: if.end: +; IS__TUNIT____-NEXT: [[CONV:%.*]] = sext i32 [[ARG]] to i64 +; IS__TUNIT____-NEXT: [[CALL1:%.*]] = call noalias i8* @malloc(i64 [[CONV]]) +; IS__TUNIT____-NEXT: br label [[RETURN]] +; IS__TUNIT____: return: +; IS__TUNIT____-NEXT: [[RETVAL_0:%.*]] = phi i8* [ [[CALL]], [[IF_THEN]] ], [ null, [[IF_END]] ] +; IS__TUNIT____-NEXT: ret i8* [[RETVAL_0]] +; +; IS__CGSCC____: Function Attrs: inaccessiblememonly +; IS__CGSCC____-LABEL: define {{[^@]+}}@internal_only_rec_static_malloc_noescape +; IS__CGSCC____-SAME: (i32 [[ARG:%.*]]) #[[ATTR0]] { +; IS__CGSCC____-NEXT: entry: +; IS__CGSCC____-NEXT: [[REM:%.*]] = srem i32 [[ARG]], 2 +; IS__CGSCC____-NEXT: [[CMP:%.*]] = icmp eq i32 [[REM]], 1 +; IS__CGSCC____-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; IS__CGSCC____: if.then: +; IS__CGSCC____-NEXT: [[DIV:%.*]] = sdiv i32 [[ARG]], 2 +; IS__CGSCC____-NEXT: [[CALL:%.*]] = call noalias i8* @internal_only_rec(i32 [[DIV]]) +; IS__CGSCC____-NEXT: br label [[RETURN:%.*]] +; IS__CGSCC____: if.end: +; IS__CGSCC____-NEXT: [[CONV:%.*]] = sext i32 [[ARG]] to i64 +; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call noalias i8* @malloc(i64 [[CONV]]) +; IS__CGSCC____-NEXT: br label [[RETURN]] +; IS__CGSCC____: return: +; IS__CGSCC____-NEXT: [[RETVAL_0:%.*]] = phi i8* [ [[CALL]], [[IF_THEN]] ], [ null, [[IF_END]] ] +; IS__CGSCC____-NEXT: ret i8* [[RETVAL_0]] ; entry: %rem = srem i32 %arg, 2