Skip to content

Commit b6a562d

Browse files
bjopetru
authored andcommitted
[DAGCombiner] Fix ReplaceAllUsesOfValueWith mutation bug in visitFREEZE (#104924)
In visitFREEZE we have been collecting a set/vector of MaybePoisonOperands that later was iterated over, applying a freeze to those operands. However, C-level fuzzy testing has discovered that the recursiveness of ReplaceAllUsesOfValueWith may cause later operands in the MaybePoisonOperands vector to be replaced when replacing an earlier operand. That would then turn up as Assertion `N1.getOpcode() != ISD::DELETED_NODE && "Operand is DELETED_NODE!"' failed. failures when trying to freeze those later operands. So we need to make sure that the vector with MaybePoisonOperands is mutated as well when needed. Or as the solution used in this patch, make sure to keep track of operand numbers that should be frozen instead of having a vector of SDValues. And then we can refetch the operands while iterating over operand numbers. The problem was seen after adding SELECT_CC to the set of operations including in "AllowMultipleMaybePoisonOperands". I'm not sure, but I guess that this could happen for other operations as well for which we allow multiple maybe poison operands. (cherry picked from commit 278fc8e)
1 parent 43b455b commit b6a562d

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp

+18-4
Original file line numberDiff line numberDiff line change
@@ -15680,13 +15680,16 @@ SDValue DAGCombiner::visitFREEZE(SDNode *N) {
1568015680
}
1568115681
}
1568215682

15683-
SmallSetVector<SDValue, 8> MaybePoisonOperands;
15684-
for (SDValue Op : N0->ops()) {
15683+
SmallSet<SDValue, 8> MaybePoisonOperands;
15684+
SmallVector<unsigned, 8> MaybePoisonOperandNumbers;
15685+
for (auto [OpNo, Op] : enumerate(N0->ops())) {
1568515686
if (DAG.isGuaranteedNotToBeUndefOrPoison(Op, /*PoisonOnly*/ false,
1568615687
/*Depth*/ 1))
1568715688
continue;
1568815689
bool HadMaybePoisonOperands = !MaybePoisonOperands.empty();
15689-
bool IsNewMaybePoisonOperand = MaybePoisonOperands.insert(Op);
15690+
bool IsNewMaybePoisonOperand = MaybePoisonOperands.insert(Op).second;
15691+
if (IsNewMaybePoisonOperand)
15692+
MaybePoisonOperandNumbers.push_back(OpNo);
1569015693
if (!HadMaybePoisonOperands)
1569115694
continue;
1569215695
if (IsNewMaybePoisonOperand && !AllowMultipleMaybePoisonOperands) {
@@ -15698,7 +15701,18 @@ SDValue DAGCombiner::visitFREEZE(SDNode *N) {
1569815701
// it could create undef or poison due to it's poison-generating flags.
1569915702
// So not finding any maybe-poison operands is fine.
1570015703

15701-
for (SDValue MaybePoisonOperand : MaybePoisonOperands) {
15704+
for (unsigned OpNo : MaybePoisonOperandNumbers) {
15705+
// N0 can mutate during iteration, so make sure to refetch the maybe poison
15706+
// operands via the operand numbers. The typical scenario is that we have
15707+
// something like this
15708+
// t262: i32 = freeze t181
15709+
// t150: i32 = ctlz_zero_undef t262
15710+
// t184: i32 = ctlz_zero_undef t181
15711+
// t268: i32 = select_cc t181, Constant:i32<0>, t184, t186, setne:ch
15712+
// When freezing the t181 operand we get t262 back, and then the
15713+
// ReplaceAllUsesOfValueWith call will not only replace t181 by t262, but
15714+
// also recursively replace t184 by t150.
15715+
SDValue MaybePoisonOperand = N->getOperand(0).getOperand(OpNo);
1570215716
// Don't replace every single UNDEF everywhere with frozen UNDEF, though.
1570315717
if (MaybePoisonOperand.getOpcode() == ISD::UNDEF)
1570415718
continue;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
; RUN: llc -mtriple aarch64 -o /dev/null %s
2+
3+
; This used to fail with:
4+
; Assertion `N1.getOpcode() != ISD::DELETED_NODE &&
5+
; "Operand is DELETED_NODE!"' failed.
6+
; Just make sure we do not crash here.
7+
define void @test_fold_freeze_over_select_cc(i15 %a, ptr %p1, ptr %p2) {
8+
entry:
9+
%a2 = add nsw i15 %a, 1
10+
%sext = sext i15 %a2 to i32
11+
%ashr = ashr i32 %sext, 31
12+
%lshr = lshr i32 %ashr, 7
13+
; Setup an already frozen input to ctlz.
14+
%freeze = freeze i32 %lshr
15+
%ctlz = call i32 @llvm.ctlz.i32(i32 %freeze, i1 true)
16+
store i32 %ctlz, ptr %p1, align 1
17+
; Here is another ctlz, which is used by a frozen select.
18+
; DAGCombiner::visitFREEZE will to try to fold the freeze over a SELECT_CC,
19+
; and when dealing with the condition operand the other SELECT_CC operands
20+
; will be replaced/simplified as well. So the SELECT_CC is mutated while
21+
; freezing the "maybe poison operands". This needs to be handled by
22+
; DAGCombiner::visitFREEZE, as it can't store the list of SDValues that
23+
; should be frozen in a separate data structure that isn't updated when the
24+
; SELECT_CC is mutated.
25+
%ctlz1 = call i32 @llvm.ctlz.i32(i32 %lshr, i1 true)
26+
%icmp = icmp ne i32 %lshr, 0
27+
%select = select i1 %icmp, i32 %ctlz1, i32 0
28+
%freeze1 = freeze i32 %select
29+
store i32 %freeze1, ptr %p2, align 1
30+
ret void
31+
}

0 commit comments

Comments
 (0)