Skip to content

Commit 984bca9

Browse files
authored
[GVN][NewGVN] Take call attributes into account in expressions (#114545)
Drop `canBeReplacedBy` and take call attributes into account in expressions. Address comment #114011 (review).
1 parent a25d91a commit 984bca9

File tree

5 files changed

+101
-32
lines changed

5 files changed

+101
-32
lines changed

llvm/include/llvm/Transforms/Scalar/GVNExpression.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,12 @@ class CallExpression final : public MemoryExpression {
315315
return EB->getExpressionType() == ET_Call;
316316
}
317317

318+
bool equals(const Expression &Other) const override;
319+
bool exactlyEquals(const Expression &Other) const override {
320+
return Expression::exactlyEquals(Other) &&
321+
cast<CallExpression>(Other).Call == Call;
322+
}
323+
318324
// Debugging support
319325
void printInternal(raw_ostream &OS, bool PrintEType) const override {
320326
if (PrintEType)

llvm/lib/Transforms/Scalar/GVN.cpp

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ struct llvm::GVNPass::Expression {
143143
Type *type = nullptr;
144144
SmallVector<uint32_t, 4> varargs;
145145

146+
AttributeList attrs;
147+
146148
Expression(uint32_t o = ~2U) : opcode(o) {}
147149

148150
bool operator==(const Expression &other) const {
@@ -154,6 +156,9 @@ struct llvm::GVNPass::Expression {
154156
return false;
155157
if (varargs != other.varargs)
156158
return false;
159+
if (!attrs.isEmpty() && !other.attrs.isEmpty() &&
160+
!attrs.intersectWith(type->getContext(), other.attrs).has_value())
161+
return false;
157162
return true;
158163
}
159164

@@ -364,6 +369,8 @@ GVNPass::Expression GVNPass::ValueTable::createExpr(Instruction *I) {
364369
} else if (auto *SVI = dyn_cast<ShuffleVectorInst>(I)) {
365370
ArrayRef<int> ShuffleMask = SVI->getShuffleMask();
366371
e.varargs.append(ShuffleMask.begin(), ShuffleMask.end());
372+
} else if (auto *CB = dyn_cast<CallBase>(I)) {
373+
e.attrs = CB->getAttributes();
367374
}
368375

369376
return e;
@@ -2136,16 +2143,6 @@ bool GVNPass::processAssumeIntrinsic(AssumeInst *IntrinsicI) {
21362143
return Changed;
21372144
}
21382145

2139-
// Return true iff V1 can be replaced with V2.
2140-
static bool canBeReplacedBy(Value *V1, Value *V2) {
2141-
if (auto *CB1 = dyn_cast<CallBase>(V1))
2142-
if (auto *CB2 = dyn_cast<CallBase>(V2))
2143-
return CB1->getAttributes()
2144-
.intersectWith(CB2->getContext(), CB2->getAttributes())
2145-
.has_value();
2146-
return true;
2147-
}
2148-
21492146
static void patchAndReplaceAllUsesWith(Instruction *I, Value *Repl) {
21502147
patchReplacementInstruction(I, Repl);
21512148
I->replaceAllUsesWith(Repl);
@@ -2690,7 +2687,7 @@ bool GVNPass::processInstruction(Instruction *I) {
26902687
// Perform fast-path value-number based elimination of values inherited from
26912688
// dominators.
26922689
Value *Repl = findLeader(I->getParent(), Num);
2693-
if (!Repl || !canBeReplacedBy(I, Repl)) {
2690+
if (!Repl) {
26942691
// Failure, just remember this instance for future use.
26952692
LeaderTable.insert(Num, I, I->getParent());
26962693
return false;
@@ -2956,7 +2953,7 @@ bool GVNPass::performScalarPRE(Instruction *CurInst) {
29562953

29572954
uint32_t TValNo = VN.phiTranslate(P, CurrentBlock, ValNo, *this);
29582955
Value *predV = findLeader(P, TValNo);
2959-
if (!predV || !canBeReplacedBy(CurInst, predV)) {
2956+
if (!predV) {
29602957
predMap.push_back(std::make_pair(static_cast<Value *>(nullptr), P));
29612958
PREPred = P;
29622959
++NumWithout;

llvm/lib/Transforms/Scalar/NewGVN.cpp

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,18 @@ bool StoreExpression::equals(const Expression &Other) const {
945945
return true;
946946
}
947947

948+
bool CallExpression::equals(const Expression &Other) const {
949+
if (!MemoryExpression::equals(Other))
950+
return false;
951+
952+
if (auto *RHS = dyn_cast<CallExpression>(&Other))
953+
return Call->getAttributes()
954+
.intersectWith(Call->getContext(), RHS->Call->getAttributes())
955+
.has_value();
956+
957+
return false;
958+
}
959+
948960
// Determine if the edge From->To is a backedge
949961
bool NewGVN::isBackedge(BasicBlock *From, BasicBlock *To) const {
950962
return From == To ||
@@ -3854,16 +3866,6 @@ Value *NewGVN::findPHIOfOpsLeader(const Expression *E,
38543866
return nullptr;
38553867
}
38563868

3857-
// Return true iff V1 can be replaced with V2.
3858-
static bool canBeReplacedBy(Value *V1, Value *V2) {
3859-
if (auto *CB1 = dyn_cast<CallBase>(V1))
3860-
if (auto *CB2 = dyn_cast<CallBase>(V2))
3861-
return CB1->getAttributes()
3862-
.intersectWith(CB2->getContext(), CB2->getAttributes())
3863-
.has_value();
3864-
return true;
3865-
}
3866-
38673869
bool NewGVN::eliminateInstructions(Function &F) {
38683870
// This is a non-standard eliminator. The normal way to eliminate is
38693871
// to walk the dominator tree in order, keeping track of available
@@ -3973,8 +3975,6 @@ bool NewGVN::eliminateInstructions(Function &F) {
39733975
MembersLeft.insert(Member);
39743976
continue;
39753977
}
3976-
if (!canBeReplacedBy(Member, Leader))
3977-
continue;
39783978

39793979
LLVM_DEBUG(dbgs() << "Found replacement " << *(Leader) << " for "
39803980
<< *Member << "\n");
@@ -4082,11 +4082,8 @@ bool NewGVN::eliminateInstructions(Function &F) {
40824082
if (DominatingLeader != Def) {
40834083
// Even if the instruction is removed, we still need to update
40844084
// flags/metadata due to downstreams users of the leader.
4085-
if (!match(DefI, m_Intrinsic<Intrinsic::ssa_copy>())) {
4086-
if (!canBeReplacedBy(DefI, DominatingLeader))
4087-
continue;
4085+
if (!match(DefI, m_Intrinsic<Intrinsic::ssa_copy>()))
40884086
patchReplacementInstruction(DefI, DominatingLeader);
4089-
}
40904087

40914088
markInstructionForDeletion(DefI);
40924089
}
@@ -4134,11 +4131,8 @@ bool NewGVN::eliminateInstructions(Function &F) {
41344131
// original operand, as we already know we can just drop it.
41354132
auto *ReplacedInst = cast<Instruction>(U->get());
41364133
auto *PI = PredInfo->getPredicateInfoFor(ReplacedInst);
4137-
if (!PI || DominatingLeader != PI->OriginalOp) {
4138-
if (!canBeReplacedBy(ReplacedInst, DominatingLeader))
4139-
continue;
4134+
if (!PI || DominatingLeader != PI->OriginalOp)
41404135
patchReplacementInstruction(ReplacedInst, DominatingLeader);
4141-
}
41424136

41434137
LLVM_DEBUG(dbgs()
41444138
<< "Found replacement " << *DominatingLeader << " for "

llvm/test/Transforms/GVN/pr113997.ll

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,39 @@ if.else:
3131
if.then:
3232
ret i1 false
3333
}
34+
35+
; Make sure we don't merge these two users of the incompatible call pair.
36+
37+
define i1 @bucket2(i32 noundef %x) {
38+
; CHECK-LABEL: define i1 @bucket2(
39+
; CHECK-SAME: i32 noundef [[X:%.*]]) {
40+
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[X]], 0
41+
; CHECK-NEXT: [[CTPOP1:%.*]] = tail call range(i32 1, 32) i32 @llvm.ctpop.i32(i32 zeroext [[X]])
42+
; CHECK-NEXT: [[CTPOP1INC:%.*]] = add i32 [[CTPOP1]], 1
43+
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i32 [[CTPOP1INC]], 3
44+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false
45+
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
46+
; CHECK: [[IF_ELSE]]:
47+
; CHECK-NEXT: [[CTPOP2:%.*]] = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X]])
48+
; CHECK-NEXT: [[CTPOP2INC:%.*]] = add i32 [[CTPOP2]], 1
49+
; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[CTPOP2INC]], 2
50+
; CHECK-NEXT: ret i1 [[RES]]
51+
; CHECK: [[IF_THEN]]:
52+
; CHECK-NEXT: ret i1 false
53+
;
54+
%cmp1 = icmp sgt i32 %x, 0
55+
%ctpop1 = tail call range(i32 1, 32) i32 @llvm.ctpop.i32(i32 zeroext %x)
56+
%ctpop1inc = add i32 %ctpop1, 1
57+
%cmp2 = icmp samesign ult i32 %ctpop1inc, 3
58+
%cond = select i1 %cmp1, i1 %cmp2, i1 false
59+
br i1 %cond, label %if.then, label %if.else
60+
61+
if.else:
62+
%ctpop2 = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 %x)
63+
%ctpop2inc = add i32 %ctpop2, 1
64+
%res = icmp eq i32 %ctpop2inc, 2
65+
ret i1 %res
66+
67+
if.then:
68+
ret i1 false
69+
}

llvm/test/Transforms/NewGVN/pr113997.ll

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,39 @@ if.else:
3131
if.then:
3232
ret i1 false
3333
}
34+
35+
; Make sure we don't merge these two users of the incompatible call pair.
36+
37+
define i1 @bucket2(i32 noundef %x) {
38+
; CHECK-LABEL: define i1 @bucket2(
39+
; CHECK-SAME: i32 noundef [[X:%.*]]) {
40+
; CHECK-NEXT: [[CMP1:%.*]] = icmp sgt i32 [[X]], 0
41+
; CHECK-NEXT: [[CTPOP1:%.*]] = tail call range(i32 1, 32) i32 @llvm.ctpop.i32(i32 zeroext [[X]])
42+
; CHECK-NEXT: [[CTPOP1INC:%.*]] = add i32 [[CTPOP1]], 1
43+
; CHECK-NEXT: [[CMP2:%.*]] = icmp samesign ult i32 [[CTPOP1INC]], 3
44+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP1]], i1 [[CMP2]], i1 false
45+
; CHECK-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
46+
; CHECK: [[IF_ELSE]]:
47+
; CHECK-NEXT: [[CTPOP2:%.*]] = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 [[X]])
48+
; CHECK-NEXT: [[CTPOP2INC:%.*]] = add i32 [[CTPOP2]], 1
49+
; CHECK-NEXT: [[RES:%.*]] = icmp eq i32 [[CTPOP2INC]], 2
50+
; CHECK-NEXT: ret i1 [[RES]]
51+
; CHECK: [[IF_THEN]]:
52+
; CHECK-NEXT: ret i1 false
53+
;
54+
%cmp1 = icmp sgt i32 %x, 0
55+
%ctpop1 = tail call range(i32 1, 32) i32 @llvm.ctpop.i32(i32 zeroext %x)
56+
%ctpop1inc = add i32 %ctpop1, 1
57+
%cmp2 = icmp samesign ult i32 %ctpop1inc, 3
58+
%cond = select i1 %cmp1, i1 %cmp2, i1 false
59+
br i1 %cond, label %if.then, label %if.else
60+
61+
if.else:
62+
%ctpop2 = tail call range(i32 0, 33) i32 @llvm.ctpop.i32(i32 %x)
63+
%ctpop2inc = add i32 %ctpop2, 1
64+
%res = icmp eq i32 %ctpop2inc, 2
65+
ret i1 %res
66+
67+
if.then:
68+
ret i1 false
69+
}

0 commit comments

Comments
 (0)