Skip to content

Commit 278fc8e

Browse files
authored
[DAGCombiner] Fix ReplaceAllUsesOfValueWith mutation bug in visitFREEZE (llvm#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.
1 parent c0d2222 commit 278fc8e

File tree

2 files changed

+49
-4
lines changed

2 files changed

+49
-4
lines changed

llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15808,13 +15808,16 @@ SDValue DAGCombiner::visitFREEZE(SDNode *N) {
1580815808
}
1580915809
}
1581015810

15811-
SmallSetVector<SDValue, 8> MaybePoisonOperands;
15812-
for (SDValue Op : N0->ops()) {
15811+
SmallSet<SDValue, 8> MaybePoisonOperands;
15812+
SmallVector<unsigned, 8> MaybePoisonOperandNumbers;
15813+
for (auto [OpNo, Op] : enumerate(N0->ops())) {
1581315814
if (DAG.isGuaranteedNotToBeUndefOrPoison(Op, /*PoisonOnly*/ false,
1581415815
/*Depth*/ 1))
1581515816
continue;
1581615817
bool HadMaybePoisonOperands = !MaybePoisonOperands.empty();
15817-
bool IsNewMaybePoisonOperand = MaybePoisonOperands.insert(Op);
15818+
bool IsNewMaybePoisonOperand = MaybePoisonOperands.insert(Op).second;
15819+
if (IsNewMaybePoisonOperand)
15820+
MaybePoisonOperandNumbers.push_back(OpNo);
1581815821
if (!HadMaybePoisonOperands)
1581915822
continue;
1582015823
if (IsNewMaybePoisonOperand && !AllowMultipleMaybePoisonOperands) {
@@ -15826,7 +15829,18 @@ SDValue DAGCombiner::visitFREEZE(SDNode *N) {
1582615829
// it could create undef or poison due to it's poison-generating flags.
1582715830
// So not finding any maybe-poison operands is fine.
1582815831

15829-
for (SDValue MaybePoisonOperand : MaybePoisonOperands) {
15832+
for (unsigned OpNo : MaybePoisonOperandNumbers) {
15833+
// N0 can mutate during iteration, so make sure to refetch the maybe poison
15834+
// operands via the operand numbers. The typical scenario is that we have
15835+
// something like this
15836+
// t262: i32 = freeze t181
15837+
// t150: i32 = ctlz_zero_undef t262
15838+
// t184: i32 = ctlz_zero_undef t181
15839+
// t268: i32 = select_cc t181, Constant:i32<0>, t184, t186, setne:ch
15840+
// When freezing the t181 operand we get t262 back, and then the
15841+
// ReplaceAllUsesOfValueWith call will not only replace t181 by t262, but
15842+
// also recursively replace t184 by t150.
15843+
SDValue MaybePoisonOperand = N->getOperand(0).getOperand(OpNo);
1583015844
// Don't replace every single UNDEF everywhere with frozen UNDEF, though.
1583115845
if (MaybePoisonOperand.getOpcode() == ISD::UNDEF)
1583215846
continue;
Lines changed: 31 additions & 0 deletions
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)