#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
+#include "llvm/IR/PatternMatch.h"
#include "llvm/IR/Value.h"
#include "llvm/InitializePasses.h"
#include "llvm/Pass.h"
#include <utility>
using namespace llvm;
+using namespace PatternMatch;
#define DEBUG_TYPE "dse"
auto *MD = dyn_cast_or_null<MemoryDef>(MA);
if (MD && State.MemDefs.size() < MemorySSADefsPerBlockLimit &&
- State.getLocForWriteEx(&I))
+ (State.getLocForWriteEx(&I) || State.isMemTerminatorInst(&I)))
State.MemDefs.push_back(MD);
// Track whether alloca and alloca-like objects are visible in the
return true;
}
+ /// If \p I is a memory terminator like llvm.lifetime.end or free, return a
+ /// pair with the MemoryLocation terminated by \p I and a boolean flag
+ /// indicating whether \p I is a free-like call.
+ Optional<std::pair<MemoryLocation, bool>>
+ getLocForTerminator(Instruction *I) const {
+ uint64_t Len;
+ Value *Ptr;
+ if (match(I, m_Intrinsic<Intrinsic::lifetime_end>(m_ConstantInt(Len),
+ m_Value(Ptr))))
+ return {std::make_pair(MemoryLocation(Ptr, Len), false)};
+
+ if (auto *CB = dyn_cast<CallBase>(I)) {
+ if (isFreeCall(I, &TLI))
+ return {std::make_pair(MemoryLocation(CB->getArgOperand(0)), true)};
+ }
+
+ return None;
+ }
+
+ /// Returns true if \p I is a memory terminator instruction like
+ /// llvm.lifetime.end or free.
+ bool isMemTerminatorInst(Instruction *I) const {
+ IntrinsicInst *II = dyn_cast<IntrinsicInst>(I);
+ return (II && II->getIntrinsicID() == Intrinsic::lifetime_end) ||
+ isFreeCall(I, &TLI);
+ }
+
+ /// Returns true if \p MaybeTerm is a memory terminator for the same
+ /// underlying object as \p DefLoc.
+ bool isMemTerminator(MemoryLocation DefLoc, Instruction *MaybeTerm) const {
+ Optional<std::pair<MemoryLocation, bool>> MaybeTermLoc =
+ getLocForTerminator(MaybeTerm);
+
+ if (!MaybeTermLoc)
+ return false;
+
+ // If the terminator is a free-like call, all accesses to the underlying
+ // object can be considered terminated.
+ if (MaybeTermLoc->second) {
+ DataLayout DL = MaybeTerm->getParent()->getModule()->getDataLayout();
+ DefLoc = MemoryLocation(GetUnderlyingObject(DefLoc.Ptr, DL));
+ }
+ return AA.isMustAlias(MaybeTermLoc->first, DefLoc);
+ }
+
// Returns true if \p Use may read from \p DefLoc.
bool isReadClobber(MemoryLocation DefLoc, Instruction *UseInst) const {
if (!UseInst->mayReadFromMemory())
continue;
}
+ // A memory terminator kills all preceeding MemoryDefs and all succeeding
+ // MemoryAccesses. We do not have to check it's users.
+ if (isMemTerminator(DefLoc, UseInst))
+ continue;
+
// Uses which may read the original MemoryDef mean we cannot eliminate the
// original MD. Stop walk.
if (isReadClobber(DefLoc, UseInst)) {
Instruction *SI = KillingDef->getMemoryInst();
auto MaybeSILoc = State.getLocForWriteEx(SI);
+ if (State.isMemTerminatorInst(SI))
+ MaybeSILoc = State.getLocForTerminator(SI).map(
+ [](const std::pair<MemoryLocation, bool> &P) { return P.first; });
+ else
+ MaybeSILoc = State.getLocForWriteEx(SI);
+
if (!MaybeSILoc) {
LLVM_DEBUG(dbgs() << "Failed to find analyzable write location for "
<< *SI << "\n");
continue;
MemoryLocation NILoc = *State.getLocForWriteEx(NI);
- // Check if NI overwrites SI.
- int64_t InstWriteOffset, DepWriteOffset;
- auto Iter = State.IOLs.insert(
- std::make_pair<BasicBlock *, InstOverlapIntervalsTy>(
- NI->getParent(), InstOverlapIntervalsTy()));
- auto &IOL = Iter.first->second;
- OverwriteResult OR = isOverwrite(SILoc, NILoc, DL, TLI, DepWriteOffset,
- InstWriteOffset, NI, IOL, AA, &F);
-
- if (EnablePartialStoreMerging && OR == OW_PartialEarlierWithFullLater) {
- auto *Earlier = dyn_cast<StoreInst>(NI);
- auto *Later = dyn_cast<StoreInst>(SI);
- if (Constant *Merged = tryToMergePartialOverlappingStores(
- Earlier, Later, InstWriteOffset, DepWriteOffset, DL, &AA,
- &DT)) {
-
- // Update stored value of earlier store to merged constant.
- Earlier->setOperand(0, Merged);
- ++NumModifiedStores;
- MadeChange = true;
-
- // Remove later store and remove any outstanding overlap intervals for
- // the updated store.
- State.deleteDeadInstruction(Later);
- auto I = State.IOLs.find(Earlier->getParent());
- if (I != State.IOLs.end())
- I->second.erase(Earlier);
- break;
- }
- }
- if (OR == OW_Complete) {
+ if (State.isMemTerminatorInst(SI)) {
+ const Value *NIUnd = GetUnderlyingObject(NILoc.Ptr, DL);
+ if (!SILocUnd || SILocUnd != NIUnd)
+ continue;
LLVM_DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: " << *NI
<< "\n KILLER: " << *SI << '\n');
State.deleteDeadInstruction(NI);
++NumFastStores;
MadeChange = true;
+ } else {
+ // Check if NI overwrites SI.
+ int64_t InstWriteOffset, DepWriteOffset;
+ auto Iter = State.IOLs.insert(
+ std::make_pair<BasicBlock *, InstOverlapIntervalsTy>(
+ NI->getParent(), InstOverlapIntervalsTy()));
+ auto &IOL = Iter.first->second;
+ OverwriteResult OR = isOverwrite(SILoc, NILoc, DL, TLI, DepWriteOffset,
+ InstWriteOffset, NI, IOL, AA, &F);
+
+ if (EnablePartialStoreMerging && OR == OW_PartialEarlierWithFullLater) {
+ auto *Earlier = dyn_cast<StoreInst>(NI);
+ auto *Later = dyn_cast<StoreInst>(SI);
+ if (Constant *Merged = tryToMergePartialOverlappingStores(
+ Earlier, Later, InstWriteOffset, DepWriteOffset, DL, &AA,
+ &DT)) {
+
+ // Update stored value of earlier store to merged constant.
+ Earlier->setOperand(0, Merged);
+ ++NumModifiedStores;
+ MadeChange = true;
+
+ // Remove later store and remove any outstanding overlap intervals
+ // for the updated store.
+ State.deleteDeadInstruction(Later);
+ auto I = State.IOLs.find(Earlier->getParent());
+ if (I != State.IOLs.end())
+ I->second.erase(Earlier);
+ break;
+ }
+ }
+
+ if (OR == OW_Complete) {
+ LLVM_DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: " << *NI
+ << "\n KILLER: " << *SI << '\n');
+ State.deleteDeadInstruction(NI);
+ ++NumFastStores;
+ MadeChange = true;
+ }
}
}
}
define void @test16(i32* noalias %P) {
; CHECK-LABEL: @test16(
; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8*
-; CHECK-NEXT: store i32 1, i32* [[P]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB3:%.*]]
; CHECK: bb1:
-; CHECK-NEXT: store i32 1, i32* [[P]]
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: call void @free(i8* [[P2]])
+; CHECK-NEXT: store i32 1, i32* [[P]]
; CHECK-NEXT: ret void
;
%P2 = bitcast i32* %P to i8*
br label %bb3
bb3:
call void @free(i8* %P2)
+ store i32 1, i32* %P
ret void
}
define void @test17(i32* noalias %P) {
; CHECK-LABEL: @test17(
; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8*
-; CHECK-NEXT: store i32 1, i32* [[P]]
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB3:%.*]]
; CHECK: bb1:
; CHECK-NEXT: call void @unknown_func()
-; CHECK-NEXT: store i32 1, i32* [[P]]
; CHECK-NEXT: br label [[BB3]]
; CHECK: bb3:
; CHECK-NEXT: call void @free(i8* [[P2]])
ret void
}
+define void @test17_read_after_free(i32* noalias %P) {
+; CHECK-LABEL: @test17_read_after_free(
+; CHECK-NEXT: [[P2:%.*]] = bitcast i32* [[P:%.*]] to i8*
+; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB3:%.*]]
+; CHECK: bb1:
+; CHECK-NEXT: br label [[BB3]]
+; CHECK: bb3:
+; CHECK-NEXT: call void @free(i8* [[P2]])
+; CHECK-NEXT: [[LV:%.*]] = load i8, i8* [[P2]]
+; CHECK-NEXT: ret void
+;
+ %P2 = bitcast i32* %P to i8*
+ store i32 1, i32* %P
+ br i1 true, label %bb1, label %bb3
+bb1:
+ store i32 1, i32* %P
+ br label %bb3
+bb3:
+ call void @free(i8* %P2)
+ %lv = load i8, i8* %P2
+ ret void
+}
+
+
define void @test6(i32* noalias %P) {
; CHECK-LABEL: @test6(
; CHECK-NEXT: br i1 true, label [[BB1:%.*]], label [[BB2:%.*]]