Skip to content

Commit

Permalink
Extract utility function for checking initial value of allocation [NF…
Browse files Browse the repository at this point in the history
…C, 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.
  • Loading branch information
preames committed Jan 7, 2022
1 parent 089b910 commit 6b0ff09
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 25 deletions.
10 changes: 10 additions & 0 deletions llvm/include/llvm/Analysis/MemoryBuiltins.h
Expand Up @@ -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.
//
Expand Down
16 changes: 16 additions & 0 deletions llvm/lib/Analysis/MemoryBuiltins.cpp
Expand Up @@ -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;
Expand Down
10 changes: 3 additions & 7 deletions llvm/lib/Transforms/IPO/Attributor.cpp
Expand Up @@ -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;
Expand Down
19 changes: 9 additions & 10 deletions llvm/lib/Transforms/Scalar/GVN.cpp
Expand Up @@ -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
Expand Down
13 changes: 5 additions & 8 deletions llvm/lib/Transforms/Scalar/NewGVN.cpp
Expand Up @@ -1493,21 +1493,18 @@ 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,
// then the loaded value is undefined.
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;
}
Expand Down
24 changes: 24 additions & 0 deletions llvm/test/Transforms/GVN/calloc-load-removal.ll
Expand Up @@ -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)

0 comments on commit 6b0ff09

Please sign in to comment.