Extract utility function for checking initial value of allocation [NFC, try 2]

This is a reoccuring pattern, we can consolidate three copies into one.  The main motivation is to reduce usages of isMallocLike.

The original commit (which was quickly reverted) didn't account for the allocation function could be an invoke, test coverage for that case added in this commit.
This commit is contained in:
Philip Reames 2022-01-07 08:25:26 -08:00
parent 089b910abc
commit 6b0ff0969d
6 changed files with 67 additions and 25 deletions

View File

@ -115,6 +115,16 @@ inline CallInst *isFreeCall(Value *I, const TargetLibraryInfo *TLI) {
return const_cast<CallInst*>(isFreeCall((const Value*)I, TLI));
}
//===----------------------------------------------------------------------===//
// Properties of allocation functions
//
/// If this allocation function initializes memory to a fixed value, return
/// said value in the requested type. Otherwise, return nullptr.
Constant *getInitialValueOfAllocation(const CallBase *Alloc,
const TargetLibraryInfo *TLI,
Type *Ty);
//===----------------------------------------------------------------------===//
// Utility functions to compute size of objects.
//

View File

@ -312,6 +312,22 @@ bool llvm::isStrdupLikeFn(const Value *V, const TargetLibraryInfo *TLI) {
return getAllocationData(V, StrDupLike, TLI).hasValue();
}
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()
bool llvm::isLibFreeFunction(const Function *F, const LibFunc TLIFn) {
unsigned ExpectedNumParams;

View File

@ -207,13 +207,9 @@ Constant *AA::getInitialValueForObj(Value &Obj, Type &Ty,
const TargetLibraryInfo *TLI) {
if (isa<AllocaInst>(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;
}
if (isAllocationFn(&Obj, TLI))
return getInitialValueOfAllocation(&cast<CallBase>(Obj), TLI, &Ty);
auto *GV = dyn_cast<GlobalVariable>(&Obj);
if (!GV || !GV->hasLocalLinkage())
return nullptr;

View File

@ -1104,20 +1104,19 @@ bool GVNPass::AnalyzeLoadAvailability(LoadInst *Load, MemDepResult DepInfo,
}
assert(DepInfo.isDef() && "follows from above");
// Loading the allocation -> undef.
if (isa<AllocaInst>(DepInst) || isMallocLikeFn(DepInst, TLI) ||
isAlignedAllocLikeFn(DepInst, TLI) ||
// Loading immediately after lifetime begin -> undef.
isLifetimeStart(DepInst)) {
// Loading the alloca -> undef.
// Loading immediately after lifetime begin -> undef.
if (isa<AllocaInst>(DepInst) || isLifetimeStart(DepInst)) {
Res = AvailableValue::get(UndefValue::get(Load->getType()));
return true;
}
// Loading from calloc (which zero initializes memory) -> zero
if (isCallocLikeFn(DepInst, TLI)) {
Res = AvailableValue::get(Constant::getNullValue(Load->getType()));
return true;
}
if (isAllocationFn(DepInst, TLI))
if (auto *InitVal = getInitialValueOfAllocation(cast<CallBase>(DepInst),
TLI, Load->getType())) {
Res = AvailableValue::get(InitVal);
return true;
}
if (StoreInst *S = dyn_cast<StoreInst>(DepInst)) {
// Reject loads and stores that are to the same address but are of

View File

@ -1493,8 +1493,7 @@ NewGVN::performSymbolicLoadCoercion(Type *LoadType, Value *LoadPtr,
// undef value. This can happen when loading for a fresh allocation with no
// intervening stores, for example. Note that this is only true in the case
// that the result of the allocation is pointer equal to the load ptr.
if (isa<AllocaInst>(DepInst) || isMallocLikeFn(DepInst, TLI) ||
isAlignedAllocLikeFn(DepInst, TLI)) {
if (isa<AllocaInst>(DepInst)) {
return createConstantExpression(UndefValue::get(LoadType));
}
// If this load occurs either right after a lifetime begin,
@ -1502,12 +1501,10 @@ NewGVN::performSymbolicLoadCoercion(Type *LoadType, Value *LoadPtr,
else if (auto *II = dyn_cast<IntrinsicInst>(DepInst)) {
if (II->getIntrinsicID() == Intrinsic::lifetime_start)
return createConstantExpression(UndefValue::get(LoadType));
}
// If this load follows a calloc (which zero initializes memory),
// then the loaded value is zero
else if (isCallocLikeFn(DepInst, TLI)) {
return createConstantExpression(Constant::getNullValue(LoadType));
}
} else if (isAllocationFn(DepInst, TLI))
if (auto *InitVal = getInitialValueOfAllocation(cast<CallBase>(DepInst),
TLI, LoadType))
return createConstantExpression(InitVal);
return nullptr;
}

View File

@ -22,4 +22,28 @@ define i32 @test1() {
}
define i32 @as_invoke(i1 %c) personality i32 (...)* undef {
bb3:
%mem = invoke noalias i8* @calloc(i64 1, i64 4)
to label %bb4 unwind label %bb1
bb1:
%lp = landingpad { i8*, i32 } cleanup
ret i32 0
bb4:
%mem.i32 = bitcast i8* %mem to i32*
; This load is trivially constant zero
%res = load i32, i32* %mem.i32, align 4
ret i32 %res
; CHECK-LABEL: @as_invoke(
; CHECK-NOT: %3 = load i32, i32* %2, align 4
; CHECK: ret i32 0
; CHECK_NO_LIBCALLS-LABEL: @as_invoke(
; CHECK_NO_LIBCALLS: load
; CHECK_NO_LIBCALLS: ret i32 %
}
declare noalias i8* @calloc(i64, i64)