Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

Commit

Permalink
Preserve nonnull metadata on Loads through SROA & mem2reg.
Browse files Browse the repository at this point in the history
Summary:
https://llvm.org/bugs/show_bug.cgi?id=31142 :

SROA was dropping the nonnull metadata on loads from allocas that got optimized out. This patch simply preserves nonnull metadata on loads through SROA and mem2reg.

Reviewers: chandlerc, efriedma

Reviewed By: efriedma

Subscribers: hfinkel, spatel, efriedma, arielb1, davide, llvm-commits

Differential Revision: https://reviews.llvm.org/D27114

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@298540 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
luqmana committed Mar 22, 2017
1 parent e53e585 commit 2b66aee
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 10 deletions.
4 changes: 4 additions & 0 deletions lib/Transforms/Scalar/SROA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2388,6 +2388,10 @@ class llvm::sroa::AllocaSliceRewriter
LI.isVolatile(), LI.getName());
if (LI.isVolatile())
NewLI->setAtomic(LI.getOrdering(), LI.getSynchScope());

// Try to preserve nonnull metadata
if (TargetTy->isPointerTy())
NewLI->copyMetadata(LI, LLVMContext::MD_nonnull);
V = NewLI;

// If this is an integer load past the end of the slice (which means the
Expand Down
57 changes: 47 additions & 10 deletions lib/Transforms/Utils/PromoteMemoryToRegister.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Analysis/AliasSetTracker.h"
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/IteratedDominanceFrontier.h"
#include "llvm/Analysis/ValueTracking.h"
Expand All @@ -38,6 +38,7 @@
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/Transforms/Utils/Local.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
#include <algorithm>
using namespace llvm;

Expand Down Expand Up @@ -301,6 +302,18 @@ struct PromoteMem2Reg {

} // end of anonymous namespace

/// Given a LoadInst LI this adds assume(LI != null) after it.
static void addAssumeNonNull(AssumptionCache *AC, LoadInst *LI) {
Function *AssumeIntrinsic =
Intrinsic::getDeclaration(LI->getModule(), Intrinsic::assume);
ICmpInst *LoadNotNull = new ICmpInst(ICmpInst::ICMP_NE, LI,
Constant::getNullValue(LI->getType()));
LoadNotNull->insertAfter(LI);
CallInst *CI = CallInst::Create(AssumeIntrinsic, {LoadNotNull});
CI->insertAfter(LoadNotNull);
AC->registerAssumption(CI);
}

static void removeLifetimeIntrinsicUsers(AllocaInst *AI) {
// Knowing that this alloca is promotable, we know that it's safe to kill all
// instructions except for load and store.
Expand Down Expand Up @@ -334,9 +347,9 @@ static void removeLifetimeIntrinsicUsers(AllocaInst *AI) {
/// and thus must be phi-ed with undef. We fall back to the standard alloca
/// promotion algorithm in that case.
static bool rewriteSingleStoreAlloca(AllocaInst *AI, AllocaInfo &Info,
LargeBlockInfo &LBI,
DominatorTree &DT,
AliasSetTracker *AST) {
LargeBlockInfo &LBI, DominatorTree &DT,
AliasSetTracker *AST,
AssumptionCache *AC) {
StoreInst *OnlyStore = Info.OnlyStore;
bool StoringGlobalVal = !isa<Instruction>(OnlyStore->getOperand(0));
BasicBlock *StoreBB = OnlyStore->getParent();
Expand Down Expand Up @@ -387,6 +400,14 @@ static bool rewriteSingleStoreAlloca(AllocaInst *AI, AllocaInfo &Info,
// code.
if (ReplVal == LI)
ReplVal = UndefValue::get(LI->getType());

// If the load was marked as nonnull we don't want to lose
// that information when we erase this Load. So we preserve
// it with an assume.
if (AC && LI->getMetadata(LLVMContext::MD_nonnull) &&
!llvm::isKnownNonNullAt(ReplVal, LI, &DT))
addAssumeNonNull(AC, LI);

LI->replaceAllUsesWith(ReplVal);
if (AST && LI->getType()->isPointerTy())
AST->deleteValue(LI);
Expand Down Expand Up @@ -435,7 +456,9 @@ static bool rewriteSingleStoreAlloca(AllocaInst *AI, AllocaInfo &Info,
/// }
static bool promoteSingleBlockAlloca(AllocaInst *AI, const AllocaInfo &Info,
LargeBlockInfo &LBI,
AliasSetTracker *AST) {
AliasSetTracker *AST,
DominatorTree &DT,
AssumptionCache *AC) {
// The trickiest case to handle is when we have large blocks. Because of this,
// this code is optimized assuming that large blocks happen. This does not
// significantly pessimize the small block case. This uses LargeBlockInfo to
Expand Down Expand Up @@ -476,10 +499,17 @@ static bool promoteSingleBlockAlloca(AllocaInst *AI, const AllocaInfo &Info,
// There is no store before this load, bail out (load may be affected
// by the following stores - see main comment).
return false;
}
else
} else {
// Otherwise, there was a store before this load, the load takes its value.
LI->replaceAllUsesWith(std::prev(I)->second->getOperand(0));
// Note, if the load was marked as nonnull we don't want to lose that
// information when we erase it. So we preserve it with an assume.
Value *ReplVal = std::prev(I)->second->getOperand(0);
if (AC && LI->getMetadata(LLVMContext::MD_nonnull) &&
!llvm::isKnownNonNullAt(ReplVal, LI, &DT))
addAssumeNonNull(AC, LI);

LI->replaceAllUsesWith(ReplVal);
}

if (AST && LI->getType()->isPointerTy())
AST->deleteValue(LI);
Expand Down Expand Up @@ -553,7 +583,7 @@ void PromoteMem2Reg::run() {
// If there is only a single store to this value, replace any loads of
// it that are directly dominated by the definition with the value stored.
if (Info.DefiningBlocks.size() == 1) {
if (rewriteSingleStoreAlloca(AI, Info, LBI, DT, AST)) {
if (rewriteSingleStoreAlloca(AI, Info, LBI, DT, AST, AC)) {
// The alloca has been processed, move on.
RemoveFromAllocasList(AllocaNum);
++NumSingleStore;
Expand All @@ -564,7 +594,7 @@ void PromoteMem2Reg::run() {
// If the alloca is only read and written in one basic block, just perform a
// linear sweep over the block to eliminate it.
if (Info.OnlyUsedInOneBlock &&
promoteSingleBlockAlloca(AI, Info, LBI, AST)) {
promoteSingleBlockAlloca(AI, Info, LBI, AST, DT, AC)) {
// The alloca has been processed, move on.
RemoveFromAllocasList(AllocaNum);
continue;
Expand Down Expand Up @@ -940,6 +970,13 @@ void PromoteMem2Reg::RenamePass(BasicBlock *BB, BasicBlock *Pred,

Value *V = IncomingVals[AI->second];

// If the load was marked as nonnull we don't want to lose
// that information when we erase this Load. So we preserve
// it with an assume.
if (AC && LI->getMetadata(LLVMContext::MD_nonnull) &&
!llvm::isKnownNonNullAt(V, LI, &DT))
addAssumeNonNull(AC, LI);

// Anything using the load now uses the current value.
LI->replaceAllUsesWith(V);
if (AST && LI->getType()->isPointerTy())
Expand Down
89 changes: 89 additions & 0 deletions test/Transforms/Mem2Reg/preserve-nonnull-load-metadata.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
; RUN: opt < %s -mem2reg -S | FileCheck %s

; This tests that mem2reg preserves the !nonnull metadata on loads
; from allocas that get optimized out.

; Check the case where the alloca in question has a single store.
define float* @single_store(float** %arg) {
; CHECK-LABEL: define float* @single_store
; CHECK: %arg.load = load float*, float** %arg, align 8
; CHECK: [[ASSUME:%(.*)]] = icmp ne float* %arg.load, null
; CHECK: call void @llvm.assume(i1 {{.*}}[[ASSUME]])
; CHECK: ret float* %arg.load
entry:
%buf = alloca float*
%arg.load = load float*, float** %arg, align 8
store float* %arg.load, float** %buf, align 8
%buf.load = load float*, float **%buf, !nonnull !0
ret float* %buf.load
}

; Check the case where the alloca in question has more than one
; store but still within one basic block.
define float* @single_block(float** %arg) {
; CHECK-LABEL: define float* @single_block
; CHECK: %arg.load = load float*, float** %arg, align 8
; CHECK: [[ASSUME:%(.*)]] = icmp ne float* %arg.load, null
; CHECK: call void @llvm.assume(i1 {{.*}}[[ASSUME]])
; CHECK: ret float* %arg.load
entry:
%buf = alloca float*
%arg.load = load float*, float** %arg, align 8
store float* null, float** %buf, align 8
store float* %arg.load, float** %buf, align 8
%buf.load = load float*, float **%buf, !nonnull !0
ret float* %buf.load
}

; Check the case where the alloca in question has more than one
; store and also reads ands writes in multiple blocks.
define float* @multi_block(float** %arg) {
; CHECK-LABEL: define float* @multi_block
; CHECK-LABEL: entry:
; CHECK: %arg.load = load float*, float** %arg, align 8
; CHECK: br label %next
; CHECK-LABEL: next:
; CHECK: [[ASSUME:%(.*)]] = icmp ne float* %arg.load, null
; CHECK: call void @llvm.assume(i1 {{.*}}[[ASSUME]])
; CHECK: ret float* %arg.load
entry:
%buf = alloca float*
%arg.load = load float*, float** %arg, align 8
store float* null, float** %buf, align 8
br label %next
next:
store float* %arg.load, float** %buf, align 8
%buf.load = load float*, float** %buf, !nonnull !0
ret float* %buf.load
}

; Check that we don't add an assume if it's not
; necessary i.e. the value is already implied to be nonnull
define float* @no_assume(float** %arg) {
; CHECK-LABEL: define float* @no_assume
; CHECK-LABEL: entry:
; CHECK: %arg.load = load float*, float** %arg, align 8
; CHECK: %cn = icmp ne float* %arg.load, null
; CHECK: br i1 %cn, label %next, label %fin
; CHECK-LABEL: next:
; CHECK-NOT: call void @llvm.assume
; CHECK: ret float* %arg.load
; CHECK-LABEL: fin:
; CHECK: ret float* null
entry:
%buf = alloca float*
%arg.load = load float*, float** %arg, align 8
%cn = icmp ne float* %arg.load, null
br i1 %cn, label %next, label %fin
next:
; At this point the above nonnull check ensures that
; the value %arg.load is nonnull in this block and thus
; we need not add the assume.
store float* %arg.load, float** %buf, align 8
%buf.load = load float*, float** %buf, !nonnull !0
ret float* %buf.load
fin:
ret float* null
}

!0 = !{}
26 changes: 26 additions & 0 deletions test/Transforms/SROA/preserve-nonnull.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
; RUN: opt < %s -sroa -S | FileCheck %s
;
; Make sure that SROA doesn't lose nonnull metadata
; on loads from allocas that get optimized out.

; CHECK-LABEL: define float* @yummy_nonnull
; CHECK: [[RETURN:%(.*)]] = load float*, float** %arg, align 8
; CHECK: [[ASSUME:%(.*)]] = icmp ne float* {{.*}}[[RETURN]], null
; CHECK: call void @llvm.assume(i1 {{.*}}[[ASSUME]])
; CHECK: ret float* {{.*}}[[RETURN]]

define float* @yummy_nonnull(float** %arg) {
entry-block:
%buf = alloca float*

%_arg_i8 = bitcast float** %arg to i8*
%_buf_i8 = bitcast float** %buf to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %_buf_i8, i8* %_arg_i8, i64 8, i32 8, i1 false)

%ret = load float*, float** %buf, align 8, !nonnull !0
ret float* %ret
}

declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1)

!0 = !{}

0 comments on commit 2b66aee

Please sign in to comment.