Skip to content

Commit 5ce47a5

Browse files
vitalybukaaengelke
andauthored
Reland "[Support] Assert that DomTree nodes share parent" (#102782)
A dominance query of a block that is in a different function is ill-defined, so assert that getNode() is only called for blocks that are in the same function. There are three cases, where this behavior did occur. LoopFuse didn't explicitly do this, but didn't invalidate the SCEV block dispositions, leaving dangling pointers to free'ed basic blocks behind, causing use-after-free. We do, however, want to be able to dereference basic blocks inside the dominator tree, so that we can refer to them by a number stored inside the basic block. Reverts #102780 Reland #101198 Fixes #102784 Co-authored-by: Alexis Engelke <[email protected]>
1 parent 93f5c61 commit 5ce47a5

File tree

7 files changed

+98
-3
lines changed

7 files changed

+98
-3
lines changed

llvm/include/llvm/Support/GenericDomTree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@ class DominatorTreeBase {
397397
/// may (but is not required to) be null for a forward (backwards)
398398
/// statically unreachable block.
399399
DomTreeNodeBase<NodeT> *getNode(const NodeT *BB) const {
400+
assert((!BB || Parent == NodeTrait::getParent(const_cast<NodeT *>(BB))) &&
401+
"cannot get DomTreeNode of block with different parent");
400402
if (auto Idx = getNodeIndex(BB); Idx && *Idx < DomTreeNodes.size())
401403
return DomTreeNodes[*Idx].get();
402404
return nullptr;

llvm/lib/Analysis/TypeMetadataUtils.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ findCallsAtConstantOffset(SmallVectorImpl<DevirtCallSite> &DevirtCalls,
3333
// after indirect call promotion and inlining, where we may have uses
3434
// of the vtable pointer guarded by a function pointer check, and a fallback
3535
// indirect call.
36+
if (CI->getFunction() != User->getFunction())
37+
continue;
3638
if (!DT.dominates(CI, User))
3739
continue;
3840
if (isa<BitCastInst>(User)) {

llvm/lib/Transforms/Scalar/AlignmentFromAssumptions.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ bool AlignmentFromAssumptionsPass::processAssumption(CallInst *ACall,
208208
continue;
209209

210210
if (Instruction *K = dyn_cast<Instruction>(J))
211+
if (K->getFunction() == ACall->getFunction())
211212
WorkList.push_back(K);
212213
}
213214

llvm/lib/Transforms/Scalar/LoopFuse.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,7 +1729,9 @@ struct LoopFuser {
17291729
// mergeLatch may remove the only block in FC1.
17301730
SE.forgetLoop(FC1.L);
17311731
SE.forgetLoop(FC0.L);
1732-
SE.forgetLoopDispositions();
1732+
// Forget block dispositions as well, so that there are no dangling
1733+
// pointers to erased/free'ed blocks.
1734+
SE.forgetBlockAndLoopDispositions();
17331735

17341736
// Move instructions from FC0.Latch to FC1.Latch.
17351737
// Note: mergeLatch requires an updated DT.
@@ -2023,7 +2025,9 @@ struct LoopFuser {
20232025
// mergeLatch may remove the only block in FC1.
20242026
SE.forgetLoop(FC1.L);
20252027
SE.forgetLoop(FC0.L);
2026-
SE.forgetLoopDispositions();
2028+
// Forget block dispositions as well, so that there are no dangling
2029+
// pointers to erased/free'ed blocks.
2030+
SE.forgetBlockAndLoopDispositions();
20272031

20282032
// Move instructions from FC0.Latch to FC1.Latch.
20292033
// Note: mergeLatch requires an updated DT.

llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6108,14 +6108,19 @@ BoUpSLP::collectUserStores(const BoUpSLP::TreeEntry *TE) const {
61086108
DenseMap<Value *, SmallVector<StoreInst *>> PtrToStoresMap;
61096109
for (unsigned Lane : seq<unsigned>(0, TE->Scalars.size())) {
61106110
Value *V = TE->Scalars[Lane];
6111+
// Don't iterate over the users of constant data.
6112+
if (isa<ConstantData>(V))
6113+
continue;
61116114
// To save compilation time we don't visit if we have too many users.
61126115
if (V->hasNUsesOrMore(UsesLimit))
61136116
break;
61146117

61156118
// Collect stores per pointer object.
61166119
for (User *U : V->users()) {
61176120
auto *SI = dyn_cast<StoreInst>(U);
6118-
if (SI == nullptr || !SI->isSimple() ||
6121+
// Test whether we can handle the store. V might be a global, which could
6122+
// be used in a different function.
6123+
if (SI == nullptr || !SI->isSimple() || SI->getFunction() != F ||
61196124
!isValidElementType(SI->getValueOperand()->getType()))
61206125
continue;
61216126
// Skip entry if already
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -passes=alignment-from-assumptions -S < %s | FileCheck %s
3+
4+
; The alignment assumption is a global, which has users in a different
5+
; function. Test that in this case the dominator tree is only queried with
6+
; blocks from the same function.
7+
8+
@global = external constant [192 x i8]
9+
10+
define void @fn1() {
11+
; CHECK-LABEL: define void @fn1() {
12+
; CHECK-NEXT: call void @llvm.assume(i1 false) [ "align"(ptr @global, i64 1) ]
13+
; CHECK-NEXT: ret void
14+
;
15+
call void @llvm.assume(i1 false) [ "align"(ptr @global, i64 1) ]
16+
ret void
17+
}
18+
19+
define void @fn2() {
20+
; CHECK-LABEL: define void @fn2() {
21+
; CHECK-NEXT: ret void
22+
; CHECK: [[LOOP:.*]]:
23+
; CHECK-NEXT: [[GEP:%.*]] = getelementptr inbounds i8, ptr @global, i64 0
24+
; CHECK-NEXT: [[LOAD:%.*]] = load i64, ptr [[GEP]], align 1
25+
; CHECK-NEXT: br label %[[LOOP]]
26+
;
27+
ret void
28+
29+
loop:
30+
%gep = getelementptr inbounds i8, ptr @global, i64 0
31+
%load = load i64, ptr %gep, align 1
32+
br label %loop
33+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
2+
; RUN: opt -S -mtriple=x86_64 -passes=slp-vectorizer < %s | FileCheck %s
3+
4+
; Test that SLP vectorize doesn't crash if a stored constant is used in multiple
5+
; functions.
6+
7+
@p = external global [64 x float]
8+
9+
define void @_Z1hPfl() {
10+
; CHECK-LABEL: define void @_Z1hPfl() {
11+
; CHECK-NEXT: [[ENTRY:.*:]]
12+
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i8, ptr @p, i64 28
13+
; CHECK-NEXT: store <2 x float> <float 0.000000e+00, float 1.000000e+00>, ptr [[TMP0]], align 4
14+
; CHECK-NEXT: ret void
15+
;
16+
entry:
17+
%0 = getelementptr i8, ptr @p, i64 28
18+
store float 0.000000e+00, ptr %0, align 4
19+
%1 = getelementptr i8, ptr @p, i64 32
20+
store float 1.000000e+00, ptr %1, align 16
21+
ret void
22+
}
23+
24+
define void @_Z1mv(i64 %arrayidx4.i.2.idx) {
25+
; CHECK-LABEL: define void @_Z1mv(
26+
; CHECK-SAME: i64 [[ARRAYIDX4_I_2_IDX:%.*]]) {
27+
; CHECK-NEXT: [[ENTRY:.*:]]
28+
; CHECK-NEXT: ret void
29+
; CHECK: [[FOR_COND1_PREHEADER_LR_PH_I:.*:]]
30+
; CHECK-NEXT: br label %[[FOR_COND1_PREHEADER_I:.*]]
31+
; CHECK: [[FOR_COND1_PREHEADER_I]]:
32+
; CHECK-NEXT: store float 1.000000e+00, ptr @p, align 4
33+
; CHECK-NEXT: [[ARRAYIDX4_I_2:%.*]] = getelementptr i8, ptr @p, i64 [[ARRAYIDX4_I_2_IDX]]
34+
; CHECK-NEXT: store float 0.000000e+00, ptr [[ARRAYIDX4_I_2]], align 4
35+
; CHECK-NEXT: br label %[[FOR_COND1_PREHEADER_I]]
36+
;
37+
entry:
38+
ret void
39+
40+
for.cond1.preheader.lr.ph.i: ; No predecessors!
41+
br label %for.cond1.preheader.i
42+
43+
for.cond1.preheader.i: ; preds = %for.cond1.preheader.i, %for.cond1.preheader.lr.ph.i
44+
store float 1.000000e+00, ptr @p, align 4
45+
%arrayidx4.i.2 = getelementptr i8, ptr @p, i64 %arrayidx4.i.2.idx
46+
store float 0.000000e+00, ptr %arrayidx4.i.2, align 4
47+
br label %for.cond1.preheader.i
48+
}

0 commit comments

Comments
 (0)