Skip to content

Commit d410128

Browse files
committed
[DAG] visitFREEZE - nodes that allow multiple frozen operands should directly freeze all operands
Avoid the ReplaceAllUsesOfValueWith approach that has caused so many problems in previous attempts at this (#145939). Minor step towards #149798 - eventually we're just going to use this path for all node types, but there's a large number of regressions that need addressing first (see #152107). This exposed a couple of things that need to be addressed here: - we need to revisit frozen nodes once we've merged all frozen/unfrozen uses of a node. - RISCVISD::READ_VLENB is never undef/poison Many of the current regressions are due to us not doing more to avoid freeze(load) nodes - if the load is legal, its no longer going to split. I'm not certain exactly when we can split nodes - #160884 tried to catch up to ValueTracking but that might still be wrong.
1 parent b79f4eb commit d410128

29 files changed

+7695
-6116
lines changed

llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp

Lines changed: 73 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -16918,6 +16918,8 @@ SDValue DAGCombiner::visitFREEZE(SDNode *N) {
1691816918
// creating a cycle in a DAG. Let's undo that by mutating the freeze.
1691916919
assert(N->getOperand(0) == FrozenN0 && "Expected cycle in DAG");
1692016920
DAG.UpdateNodeOperands(N, N0);
16921+
// Revisit the node.
16922+
AddToWorklist(N);
1692116923
return FrozenN0;
1692216924
}
1692316925

@@ -16972,72 +16974,81 @@ SDValue DAGCombiner::visitFREEZE(SDNode *N) {
1697216974
}
1697316975
}
1697416976

16975-
SmallSet<SDValue, 8> MaybePoisonOperands;
16976-
SmallVector<unsigned, 8> MaybePoisonOperandNumbers;
16977-
for (auto [OpNo, Op] : enumerate(N0->ops())) {
16978-
if (DAG.isGuaranteedNotToBeUndefOrPoison(Op, /*PoisonOnly=*/false))
16979-
continue;
16980-
bool HadMaybePoisonOperands = !MaybePoisonOperands.empty();
16981-
bool IsNewMaybePoisonOperand = MaybePoisonOperands.insert(Op).second;
16982-
if (IsNewMaybePoisonOperand)
16983-
MaybePoisonOperandNumbers.push_back(OpNo);
16984-
if (!HadMaybePoisonOperands)
16985-
continue;
16986-
if (IsNewMaybePoisonOperand && !AllowMultipleMaybePoisonOperands) {
16987-
// Multiple maybe-poison ops when not allowed - bail out.
16988-
return SDValue();
16977+
SmallVector<SDValue> Ops;
16978+
if (AllowMultipleMaybePoisonOperands) {
16979+
// Collect and freeze all operands.
16980+
Ops = SmallVector<SDValue>(N0->ops());
16981+
for (SDValue &Op : Ops)
16982+
Op = DAG.getFreeze(Op);
16983+
} else {
16984+
SmallSet<SDValue, 8> MaybePoisonOperands;
16985+
SmallVector<unsigned, 8> MaybePoisonOperandNumbers;
16986+
for (auto [OpNo, Op] : enumerate(N0->ops())) {
16987+
if (DAG.isGuaranteedNotToBeUndefOrPoison(Op, /*PoisonOnly=*/false))
16988+
continue;
16989+
bool HadMaybePoisonOperands = !MaybePoisonOperands.empty();
16990+
bool IsNewMaybePoisonOperand = MaybePoisonOperands.insert(Op).second;
16991+
if (IsNewMaybePoisonOperand)
16992+
MaybePoisonOperandNumbers.push_back(OpNo);
16993+
if (!HadMaybePoisonOperands)
16994+
continue;
16995+
if (IsNewMaybePoisonOperand) {
16996+
// Multiple maybe-poison ops when not allowed - bail out.
16997+
return SDValue();
16998+
}
16999+
}
17000+
// NOTE: the whole op may be not guaranteed to not be undef or poison
17001+
// because it could create undef or poison due to it's poison-generating
17002+
// flags. So not finding any maybe-poison operands is fine.
17003+
17004+
for (unsigned OpNo : MaybePoisonOperandNumbers) {
17005+
// N0 can mutate during iteration, so make sure to refetch the maybe
17006+
// poison operands via the operand numbers. The typical scenario is that
17007+
// we have something like this
17008+
// t262: i32 = freeze t181
17009+
// t150: i32 = ctlz_zero_undef t262
17010+
// t184: i32 = ctlz_zero_undef t181
17011+
// t268: i32 = select_cc t181, Constant:i32<0>, t184, t186, setne:ch
17012+
// When freezing the t181 operand we get t262 back, and then the
17013+
// ReplaceAllUsesOfValueWith call will not only replace t181 by t262, but
17014+
// also recursively replace t184 by t150.
17015+
SDValue MaybePoisonOperand = N->getOperand(0).getOperand(OpNo);
17016+
// Don't replace every single UNDEF everywhere with frozen UNDEF, though.
17017+
if (MaybePoisonOperand.isUndef())
17018+
continue;
17019+
// First, freeze each offending operand.
17020+
SDValue FrozenMaybePoisonOperand = DAG.getFreeze(MaybePoisonOperand);
17021+
// Then, change all other uses of unfrozen operand to use frozen operand.
17022+
DAG.ReplaceAllUsesOfValueWith(MaybePoisonOperand,
17023+
FrozenMaybePoisonOperand);
17024+
if (FrozenMaybePoisonOperand.getOpcode() == ISD::FREEZE &&
17025+
FrozenMaybePoisonOperand.getOperand(0) == FrozenMaybePoisonOperand) {
17026+
// But, that also updated the use in the freeze we just created, thus
17027+
// creating a cycle in a DAG. Let's undo that by mutating the freeze.
17028+
DAG.UpdateNodeOperands(FrozenMaybePoisonOperand.getNode(),
17029+
MaybePoisonOperand);
17030+
}
17031+
17032+
// This node has been merged with another.
17033+
if (N->getOpcode() == ISD::DELETED_NODE)
17034+
return SDValue(N, 0);
1698917035
}
16990-
}
16991-
// NOTE: the whole op may be not guaranteed to not be undef or poison because
16992-
// it could create undef or poison due to it's poison-generating flags.
16993-
// So not finding any maybe-poison operands is fine.
16994-
16995-
for (unsigned OpNo : MaybePoisonOperandNumbers) {
16996-
// N0 can mutate during iteration, so make sure to refetch the maybe poison
16997-
// operands via the operand numbers. The typical scenario is that we have
16998-
// something like this
16999-
// t262: i32 = freeze t181
17000-
// t150: i32 = ctlz_zero_undef t262
17001-
// t184: i32 = ctlz_zero_undef t181
17002-
// t268: i32 = select_cc t181, Constant:i32<0>, t184, t186, setne:ch
17003-
// When freezing the t181 operand we get t262 back, and then the
17004-
// ReplaceAllUsesOfValueWith call will not only replace t181 by t262, but
17005-
// also recursively replace t184 by t150.
17006-
SDValue MaybePoisonOperand = N->getOperand(0).getOperand(OpNo);
17007-
// Don't replace every single UNDEF everywhere with frozen UNDEF, though.
17008-
if (MaybePoisonOperand.isUndef())
17009-
continue;
17010-
// First, freeze each offending operand.
17011-
SDValue FrozenMaybePoisonOperand = DAG.getFreeze(MaybePoisonOperand);
17012-
// Then, change all other uses of unfrozen operand to use frozen operand.
17013-
DAG.ReplaceAllUsesOfValueWith(MaybePoisonOperand, FrozenMaybePoisonOperand);
17014-
if (FrozenMaybePoisonOperand.getOpcode() == ISD::FREEZE &&
17015-
FrozenMaybePoisonOperand.getOperand(0) == FrozenMaybePoisonOperand) {
17016-
// But, that also updated the use in the freeze we just created, thus
17017-
// creating a cycle in a DAG. Let's undo that by mutating the freeze.
17018-
DAG.UpdateNodeOperands(FrozenMaybePoisonOperand.getNode(),
17019-
MaybePoisonOperand);
17020-
}
17021-
17022-
// This node has been merged with another.
17023-
if (N->getOpcode() == ISD::DELETED_NODE)
17024-
return SDValue(N, 0);
17025-
}
1702617036

17027-
assert(N->getOpcode() != ISD::DELETED_NODE && "Node was deleted!");
17037+
assert(N->getOpcode() != ISD::DELETED_NODE && "Node was deleted!");
1702817038

17029-
// The whole node may have been updated, so the value we were holding
17030-
// may no longer be valid. Re-fetch the operand we're `freeze`ing.
17031-
N0 = N->getOperand(0);
17039+
// The whole node may have been updated, so the value we were holding
17040+
// may no longer be valid. Re-fetch the operand we're `freeze`ing.
17041+
N0 = N->getOperand(0);
1703217042

17033-
// Finally, recreate the node, it's operands were updated to use
17034-
// frozen operands, so we just need to use it's "original" operands.
17035-
SmallVector<SDValue> Ops(N0->ops());
17036-
// TODO: ISD::UNDEF and ISD::POISON should get separate handling, but best
17037-
// leave for a future patch.
17038-
for (SDValue &Op : Ops) {
17039-
if (Op.isUndef())
17040-
Op = DAG.getFreeze(Op);
17043+
// Finally, recreate the node, it's operands were updated to use
17044+
// frozen operands, so we just need to use it's "original" operands.
17045+
Ops = SmallVector<SDValue>(N0->ops());
17046+
// TODO: ISD::UNDEF and ISD::POISON should get separate handling, but best
17047+
// leave for a future patch.
17048+
for (SDValue &Op : Ops) {
17049+
if (Op.isUndef())
17050+
Op = DAG.getFreeze(Op);
17051+
}
1704117052
}
1704217053

1704317054
SDLoc DL(N0);

llvm/lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21918,6 +21918,8 @@ bool RISCVTargetLowering::canCreateUndefOrPoisonForTargetNode(
2191821918

2191921919
// TODO: Add more target nodes.
2192021920
switch (Op.getOpcode()) {
21921+
case RISCVISD::READ_VLENB:
21922+
return false;
2192121923
case RISCVISD::SLLW:
2192221924
case RISCVISD::SRAW:
2192321925
case RISCVISD::SRLW:

llvm/test/CodeGen/AMDGPU/amdgpu-codegenprepare-fold-binop-select.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ define i32 @select_sdiv_lhs_opaque_const0_i32(i1 %cond) {
129129
; GCN-NEXT: s_getpc_b64 s[4:5]
130130
; GCN-NEXT: s_add_u32 s4, s4, gv@gotpcrel32@lo+4
131131
; GCN-NEXT: s_addc_u32 s5, s5, gv@gotpcrel32@hi+12
132-
; GCN-NEXT: s_load_dword s4, s[4:5], 0x0
132+
; GCN-NEXT: s_load_dwordx2 s[4:5], s[4:5], 0x0
133133
; GCN-NEXT: v_and_b32_e32 v0, 1, v0
134134
; GCN-NEXT: v_cmp_eq_u32_e32 vcc, 1, v0
135135
; GCN-NEXT: s_waitcnt lgkmcnt(0)
@@ -211,7 +211,7 @@ define i32 @select_sdiv_lhs_opaque_const1_i32(i1 %cond) {
211211
; GCN-NEXT: s_getpc_b64 s[4:5]
212212
; GCN-NEXT: s_add_u32 s4, s4, gv@gotpcrel32@lo+4
213213
; GCN-NEXT: s_addc_u32 s5, s5, gv@gotpcrel32@hi+12
214-
; GCN-NEXT: s_load_dword s4, s[4:5], 0x0
214+
; GCN-NEXT: s_load_dwordx2 s[4:5], s[4:5], 0x0
215215
; GCN-NEXT: v_and_b32_e32 v0, 1, v0
216216
; GCN-NEXT: v_cmp_eq_u32_e32 vcc, 1, v0
217217
; GCN-NEXT: s_waitcnt lgkmcnt(0)

llvm/test/CodeGen/AMDGPU/fptoi.i128.ll

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,25 +1437,15 @@ define i128 @fptoui_f32_to_i128(float %x) {
14371437
}
14381438

14391439
define i128 @fptosi_f16_to_i128(half %x) {
1440-
; SDAG-LABEL: fptosi_f16_to_i128:
1441-
; SDAG: ; %bb.0:
1442-
; SDAG-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
1443-
; SDAG-NEXT: v_cvt_f32_f16_e32 v0, v0
1444-
; SDAG-NEXT: v_cvt_i32_f32_e32 v0, v0
1445-
; SDAG-NEXT: v_ashrrev_i32_e32 v1, 31, v0
1446-
; SDAG-NEXT: v_ashrrev_i32_e32 v2, 31, v1
1447-
; SDAG-NEXT: v_mov_b32_e32 v3, v2
1448-
; SDAG-NEXT: s_setpc_b64 s[30:31]
1449-
;
1450-
; GISEL-LABEL: fptosi_f16_to_i128:
1451-
; GISEL: ; %bb.0:
1452-
; GISEL-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
1453-
; GISEL-NEXT: v_cvt_f32_f16_e32 v0, v0
1454-
; GISEL-NEXT: v_cvt_i32_f32_e32 v0, v0
1455-
; GISEL-NEXT: v_ashrrev_i32_e32 v1, 31, v0
1456-
; GISEL-NEXT: v_mov_b32_e32 v2, v1
1457-
; GISEL-NEXT: v_mov_b32_e32 v3, v1
1458-
; GISEL-NEXT: s_setpc_b64 s[30:31]
1440+
; GCN-LABEL: fptosi_f16_to_i128:
1441+
; GCN: ; %bb.0:
1442+
; GCN-NEXT: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0)
1443+
; GCN-NEXT: v_cvt_f32_f16_e32 v0, v0
1444+
; GCN-NEXT: v_cvt_i32_f32_e32 v0, v0
1445+
; GCN-NEXT: v_ashrrev_i32_e32 v1, 31, v0
1446+
; GCN-NEXT: v_mov_b32_e32 v2, v1
1447+
; GCN-NEXT: v_mov_b32_e32 v3, v1
1448+
; GCN-NEXT: s_setpc_b64 s[30:31]
14591449
%cvt = fptosi half %x to i128
14601450
ret i128 %cvt
14611451
}

0 commit comments

Comments
 (0)