Skip to content

[SelectionDAG] Add SDNode::user_begin() and use it in some places #120509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions llvm/include/llvm/CodeGen/SelectionDAGNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ END_TWO_BYTE_PACK()
bool use_empty() const { return UseList == nullptr; }

/// Return true if there is exactly one use of this node.
bool hasOneUse() const { return hasSingleElement(uses()); }
bool hasOneUse() const { return hasSingleElement(users()); }

/// Return the number of uses of this node. This method takes
/// time proportional to the number of uses.
Expand Down Expand Up @@ -844,10 +844,19 @@ END_TWO_BYTE_PACK()

static use_iterator use_end() { return use_iterator(nullptr); }

inline iterator_range<use_iterator> uses() {
/// Provide iteration support to walk over all users of an SDNode.
/// For now, this should only be used to get a pointer to the first user.
/// FIXME: Rename use_iterator to user_iterator. Add user_end().
use_iterator user_begin() const { return use_iterator(UseList); }

// Dereferencing use_iterator returns the user SDNode* making it closer to a
// user_iterator thus this function is called users() to reflect that.
// FIXME: Rename to user_iterator and introduce a use_iterator that returns
// SDUse*.
inline iterator_range<use_iterator> users() {
return make_range(use_begin(), use_end());
}
inline iterator_range<use_iterator> uses() const {
inline iterator_range<use_iterator> users() const {
return make_range(use_begin(), use_end());
}

Expand Down
108 changes: 54 additions & 54 deletions llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ namespace {
/// When an instruction is simplified, add all users of the instruction to
/// the work lists because they might get more simplified now.
void AddUsersToWorklist(SDNode *N) {
for (SDNode *Node : N->uses())
for (SDNode *Node : N->users())
AddToWorklist(Node);
}

Expand Down Expand Up @@ -1113,7 +1113,7 @@ bool DAGCombiner::reassociationCanBreakAddressingModePattern(unsigned Opc,
: N1.getConstantOperandVal(1)));
if (Opc == ISD::SUB)
ScalableOffset = -ScalableOffset;
if (all_of(N->uses(), [&](SDNode *Node) {
if (all_of(N->users(), [&](SDNode *Node) {
if (auto *LoadStore = dyn_cast<MemSDNode>(Node);
LoadStore && LoadStore->getBasePtr().getNode() == N) {
TargetLoweringBase::AddrMode AM;
Expand Down Expand Up @@ -1151,7 +1151,7 @@ bool DAGCombiner::reassociationCanBreakAddressingModePattern(unsigned Opc,
return false;
const int64_t CombinedValue = CombinedValueIntVal.getSExtValue();

for (SDNode *Node : N->uses()) {
for (SDNode *Node : N->users()) {
if (auto *LoadStore = dyn_cast<MemSDNode>(Node)) {
// Is x[offset2] already not a legal addressing mode? If so then
// reassociating the constants breaks nothing (we test offset2 because
Expand All @@ -1176,7 +1176,7 @@ bool DAGCombiner::reassociationCanBreakAddressingModePattern(unsigned Opc,
if (GA->getOpcode() == ISD::GlobalAddress && TLI.isOffsetFoldingLegal(GA))
return false;

for (SDNode *Node : N->uses()) {
for (SDNode *Node : N->users()) {
auto *LoadStore = dyn_cast<MemSDNode>(Node);
if (!LoadStore)
return false;
Expand Down Expand Up @@ -2136,8 +2136,8 @@ SDValue DAGCombiner::visitTokenFactor(SDNode *N) {
// If the sole user is a token factor, we should make sure we have a
// chance to merge them together. This prevents TF chains from inhibiting
// optimizations.
if (N->hasOneUse() && N->use_begin()->getOpcode() == ISD::TokenFactor)
AddToWorklist(*(N->use_begin()));
if (N->hasOneUse() && N->user_begin()->getOpcode() == ISD::TokenFactor)
AddToWorklist(*(N->user_begin()));

SmallVector<SDNode *, 8> TFs; // List of token factors to visit.
SmallVector<SDValue, 8> Ops; // Ops for replacing token factor.
Expand Down Expand Up @@ -4720,7 +4720,7 @@ SDValue DAGCombiner::useDivRem(SDNode *Node) {
SDValue Op0 = Node->getOperand(0);
SDValue Op1 = Node->getOperand(1);
SDValue combined;
for (SDNode *User : Op0->uses()) {
for (SDNode *User : Op0->users()) {
if (User == Node || User->getOpcode() == ISD::DELETED_NODE ||
User->use_empty())
continue;
Expand Down Expand Up @@ -10369,7 +10369,7 @@ static SDValue combineShiftToMULH(SDNode *N, const SDLoc &DL, SelectionDAG &DAG,
unsigned MulLoHiOp = IsSignExt ? ISD::SMUL_LOHI : ISD::UMUL_LOHI;
if (!ShiftOperand.hasOneUse() &&
TLI.isOperationLegalOrCustom(MulLoHiOp, NarrowVT) &&
llvm::any_of(ShiftOperand->uses(), UserOfLowerBits)) {
llvm::any_of(ShiftOperand->users(), UserOfLowerBits)) {
return SDValue();
}

Expand Down Expand Up @@ -10906,15 +10906,15 @@ SDValue DAGCombiner::visitSRL(SDNode *N) {
// which we plan to do. This workaround can be removed once the DAG is
// processed in topological order.
if (N->hasOneUse()) {
SDNode *Use = *N->use_begin();
SDNode *User = *N->user_begin();

// Look pass the truncate.
if (Use->getOpcode() == ISD::TRUNCATE && Use->hasOneUse())
Use = *Use->use_begin();
if (User->getOpcode() == ISD::TRUNCATE && User->hasOneUse())
User = *User->user_begin();
Comment on lines +10912 to +10913
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should have a getOneUser helper

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an equivalent in IR?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IR has hasOneUser, which is strictly less useful than returning the user.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasOneUser in IR returns whether or not the User of all Uses is unique. Should your proposed getOneUser() return the unique User or all Uses? Or should it only return the User if there is exactly one Use? Most of these places probably expect the latter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, the point is to handle instructions where the same value is used multiple times. It's a single user, which is usually what you want. It returns the single unique instruction which may use the value multiple times as an operand


if (Use->getOpcode() == ISD::BRCOND || Use->getOpcode() == ISD::AND ||
Use->getOpcode() == ISD::OR || Use->getOpcode() == ISD::XOR)
AddToWorklist(Use);
if (User->getOpcode() == ISD::BRCOND || User->getOpcode() == ISD::AND ||
User->getOpcode() == ISD::OR || User->getOpcode() == ISD::XOR)
AddToWorklist(User);
}

// Try to transform this shift into a multiply-high if
Expand Down Expand Up @@ -12917,7 +12917,7 @@ SDValue DAGCombiner::visitSETCC(SDNode *N) {
// also lend itself to numerous combines and, as a result, it is desired
// we keep the argument to a brcond as a setcc as much as possible.
bool PreferSetCC =
N->hasOneUse() && N->use_begin()->getOpcode() == ISD::BRCOND;
N->hasOneUse() && N->user_begin()->getOpcode() == ISD::BRCOND;

ISD::CondCode Cond = cast<CondCodeSDNode>(N->getOperand(2))->get();
EVT VT = N->getValueType(0);
Expand Down Expand Up @@ -13570,7 +13570,7 @@ static SDValue tryToFoldExtOfLoad(SelectionDAG &DAG, DAGCombiner &Combiner,
if (NonNegZExt) {
assert(ExtLoadType == ISD::ZEXTLOAD && ExtOpc == ISD::ZERO_EXTEND &&
"Unexpected load type or opcode");
for (SDNode *User : N0->uses()) {
for (SDNode *User : N0->users()) {
if (User->getOpcode() == ISD::SETCC) {
ISD::CondCode CC = cast<CondCodeSDNode>(User->getOperand(2))->get();
if (ISD::isSignedIntSetCC(CC)) {
Expand Down Expand Up @@ -14825,7 +14825,7 @@ SDValue DAGCombiner::reduceLoadWidth(SDNode *N) {

// If the SRL is only used by a masking AND, we may be able to adjust
// the ExtVT to make the AND redundant.
SDNode *Mask = *(SRL->use_begin());
SDNode *Mask = *(SRL->user_begin());
if (SRL.hasOneUse() && Mask->getOpcode() == ISD::AND &&
isa<ConstantSDNode>(Mask->getOperand(1))) {
unsigned Offset, ActiveBits;
Expand Down Expand Up @@ -15364,7 +15364,7 @@ SDValue DAGCombiner::visitTRUNCATE(SDNode *N) {
}

// If this is anyext(trunc), don't fold it, allow ourselves to be folded.
if (N->hasOneUse() && (N->use_begin()->getOpcode() == ISD::ANY_EXTEND))
if (N->hasOneUse() && (N->user_begin()->getOpcode() == ISD::ANY_EXTEND))
return SDValue();

// Fold extract-and-trunc into a narrow extract. For example:
Expand Down Expand Up @@ -17673,7 +17673,7 @@ SDValue DAGCombiner::combineRepeatedFPDivisors(SDNode *N) {
// Find all FDIV users of the same divisor.
// Use a set because duplicates may be present in the user list.
SetVector<SDNode *> Users;
for (auto *U : N1->uses()) {
for (auto *U : N1->users()) {
if (U->getOpcode() == ISD::FDIV && U->getOperand(1) == N1) {
// Skip X/sqrt(X) that has not been simplified to sqrt(X) yet.
if (U->getOperand(1).getOpcode() == ISD::FSQRT &&
Expand Down Expand Up @@ -18370,7 +18370,7 @@ SDValue DAGCombiner::visitFP_EXTEND(SDNode *N) {
return FoldedVOp;

// If this is fp_round(fpextend), don't fold it, allow ourselves to be folded.
if (N->hasOneUse() && N->use_begin()->getOpcode() == ISD::FP_ROUND)
if (N->hasOneUse() && N->user_begin()->getOpcode() == ISD::FP_ROUND)
return SDValue();

// fold (fp_extend c1fp) -> c1fp
Expand Down Expand Up @@ -18965,15 +18965,15 @@ bool DAGCombiner::CombineToPreIndexedLoadStore(SDNode *N) {
// Now check for #3 and #4.
bool RealUse = false;

for (SDNode *Use : Ptr->uses()) {
if (Use == N)
for (SDNode *User : Ptr->users()) {
if (User == N)
continue;
if (SDNode::hasPredecessorHelper(Use, Visited, Worklist, MaxSteps))
if (SDNode::hasPredecessorHelper(User, Visited, Worklist, MaxSteps))
return false;

// If Ptr may be folded in addressing mode of other use, then it's
// not profitable to do this transformation.
if (!canFoldInAddressingMode(Ptr.getNode(), Use, DAG, TLI))
if (!canFoldInAddressingMode(Ptr.getNode(), User, DAG, TLI))
RealUse = true;
}

Expand Down Expand Up @@ -19089,29 +19089,29 @@ static bool shouldCombineToPostInc(SDNode *N, SDValue Ptr, SDNode *PtrUse,

SmallPtrSet<const SDNode *, 32> Visited;
unsigned MaxSteps = SelectionDAG::getHasPredecessorMaxSteps();
for (SDNode *Use : BasePtr->uses()) {
if (Use == Ptr.getNode())
for (SDNode *User : BasePtr->users()) {
if (User == Ptr.getNode())
continue;

// No if there's a later user which could perform the index instead.
if (isa<MemSDNode>(Use)) {
if (isa<MemSDNode>(User)) {
bool IsLoad = true;
bool IsMasked = false;
SDValue OtherPtr;
if (getCombineLoadStoreParts(Use, ISD::POST_INC, ISD::POST_DEC, IsLoad,
if (getCombineLoadStoreParts(User, ISD::POST_INC, ISD::POST_DEC, IsLoad,
IsMasked, OtherPtr, TLI)) {
SmallVector<const SDNode *, 2> Worklist;
Worklist.push_back(Use);
Worklist.push_back(User);
if (SDNode::hasPredecessorHelper(N, Visited, Worklist, MaxSteps))
return false;
}
}

// If all the uses are load / store addresses, then don't do the
// transformation.
if (Use->getOpcode() == ISD::ADD || Use->getOpcode() == ISD::SUB) {
for (SDNode *UseUse : Use->uses())
if (canFoldInAddressingMode(Use, UseUse, DAG, TLI))
if (User->getOpcode() == ISD::ADD || User->getOpcode() == ISD::SUB) {
for (SDNode *UserUser : User->users())
if (canFoldInAddressingMode(User, UserUser, DAG, TLI))
return false;
}
}
Expand All @@ -19136,7 +19136,7 @@ static SDNode *getPostIndexedLoadStoreOp(SDNode *N, bool &IsLoad,
// nor a successor of N. Otherwise, if Op is folded that would
// create a cycle.
unsigned MaxSteps = SelectionDAG::getHasPredecessorMaxSteps();
for (SDNode *Op : Ptr->uses()) {
for (SDNode *Op : Ptr->users()) {
// Check for #1.
if (!shouldCombineToPostInc(N, Ptr, Op, BasePtr, Offset, AM, DAG, TLI))
continue;
Expand Down Expand Up @@ -19847,17 +19847,17 @@ struct LoadedSlice {
bool canMergeExpensiveCrossRegisterBankCopy() const {
if (!Inst || !Inst->hasOneUse())
return false;
SDNode *Use = *Inst->use_begin();
if (Use->getOpcode() != ISD::BITCAST)
SDNode *User = *Inst->user_begin();
if (User->getOpcode() != ISD::BITCAST)
return false;
assert(DAG && "Missing context");
const TargetLowering &TLI = DAG->getTargetLoweringInfo();
EVT ResVT = Use->getValueType(0);
EVT ResVT = User->getValueType(0);
const TargetRegisterClass *ResRC =
TLI.getRegClassFor(ResVT.getSimpleVT(), Use->isDivergent());
TLI.getRegClassFor(ResVT.getSimpleVT(), User->isDivergent());
const TargetRegisterClass *ArgRC =
TLI.getRegClassFor(Use->getOperand(0).getValueType().getSimpleVT(),
Use->getOperand(0)->isDivergent());
TLI.getRegClassFor(User->getOperand(0).getValueType().getSimpleVT(),
User->getOperand(0)->isDivergent());
if (ArgRC == ResRC || !TLI.isOperationLegal(ISD::LOAD, ResVT))
return false;

Expand Down Expand Up @@ -20069,7 +20069,7 @@ bool DAGCombiner::SliceUpLoad(SDNode *N) {
if (User->getOpcode() == ISD::SRL && User->hasOneUse() &&
isa<ConstantSDNode>(User->getOperand(1))) {
Shift = User->getConstantOperandVal(1);
User = *User->use_begin();
User = *User->user_begin();
}

// At this point, User is a Truncate, iff we encountered, trunc or
Expand Down Expand Up @@ -20515,24 +20515,24 @@ bool DAGCombiner::isMulAddWithConstProfitable(SDNode *MulNode, SDValue AddNode,
return true;

// Walk all the users of the constant with which we're multiplying.
for (SDNode *Use : ConstNode->uses()) {
if (Use == MulNode) // This use is the one we're on right now. Skip it.
for (SDNode *User : ConstNode->users()) {
if (User == MulNode) // This use is the one we're on right now. Skip it.
continue;

if (Use->getOpcode() == ISD::MUL) { // We have another multiply use.
if (User->getOpcode() == ISD::MUL) { // We have another multiply use.
SDNode *OtherOp;
SDNode *MulVar = AddNode.getOperand(0).getNode();

// OtherOp is what we're multiplying against the constant.
if (Use->getOperand(0) == ConstNode)
OtherOp = Use->getOperand(1).getNode();
if (User->getOperand(0) == ConstNode)
OtherOp = User->getOperand(1).getNode();
else
OtherOp = Use->getOperand(0).getNode();
OtherOp = User->getOperand(0).getNode();

// Check to see if multiply is with the same operand of our "add".
//
// ConstNode = CONST
// Use = ConstNode * A <-- visiting Use. OtherOp is A.
// User = ConstNode * A <-- visiting User. OtherOp is A.
// ...
// AddNode = (A + c1) <-- MulVar is A.
// = AddNode * ConstNode <-- current visiting instruction.
Expand All @@ -20550,7 +20550,7 @@ bool DAGCombiner::isMulAddWithConstProfitable(SDNode *MulNode, SDValue AddNode,
// ... = AddNode * ConstNode <-- current visiting instruction.
// ...
// OtherOp = (A + c2)
// Use = OtherOp * ConstNode <-- visiting Use.
// User = OtherOp * ConstNode <-- visiting User.
//
// If we make this transformation, we will have a common
// multiply (CONST * A) after we also do the same transformation
Expand Down Expand Up @@ -22902,7 +22902,7 @@ bool DAGCombiner::refineExtractVectorEltIntoMultipleNarrowExtractVectorElts(
// Did we fail to model any of the users of the Producer?
bool ProducerIsLeaf = false;
// Look at each user of this Producer.
for (SDNode *User : E.Producer->uses()) {
for (SDNode *User : E.Producer->users()) {
switch (User->getOpcode()) {
// TODO: support ISD::BITCAST
// TODO: support ISD::ANY_EXTEND
Expand Down Expand Up @@ -23176,14 +23176,14 @@ SDValue DAGCombiner::visitEXTRACT_VECTOR_ELT(SDNode *N) {

// If only EXTRACT_VECTOR_ELT nodes use the source vector we can
// simplify it based on the (valid) extraction indices.
if (llvm::all_of(VecOp->uses(), [&](SDNode *Use) {
if (llvm::all_of(VecOp->users(), [&](SDNode *Use) {
return Use->getOpcode() == ISD::EXTRACT_VECTOR_ELT &&
Use->getOperand(0) == VecOp &&
isa<ConstantSDNode>(Use->getOperand(1));
})) {
APInt DemandedElts = APInt::getZero(NumElts);
for (SDNode *Use : VecOp->uses()) {
auto *CstElt = cast<ConstantSDNode>(Use->getOperand(1));
for (SDNode *User : VecOp->users()) {
auto *CstElt = cast<ConstantSDNode>(User->getOperand(1));
if (CstElt->getAPIntValue().ult(NumElts))
DemandedElts.setBit(CstElt->getZExtValue());
}
Expand Down Expand Up @@ -27302,7 +27302,7 @@ SDValue DAGCombiner::visitGET_FPENV_MEM(SDNode *N) {
// Check if the memory, where FP state is written to, is used only in a single
// load operation.
LoadSDNode *LdNode = nullptr;
for (auto *U : Ptr->uses()) {
for (auto *U : Ptr->users()) {
if (U == N)
continue;
if (auto *Ld = dyn_cast<LoadSDNode>(U)) {
Expand Down Expand Up @@ -27352,7 +27352,7 @@ SDValue DAGCombiner::visitSET_FPENV_MEM(SDNode *N) {

// Check if the address of FP state is used also in a store operation only.
StoreSDNode *StNode = nullptr;
for (auto *U : Ptr->uses()) {
for (auto *U : Ptr->users()) {
if (U == N)
continue;
if (auto *St = dyn_cast<StoreSDNode>(U)) {
Expand Down
6 changes: 3 additions & 3 deletions llvm/lib/CodeGen/SelectionDAG/InstrEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ void InstrEmitter::EmitCopyFromReg(SDNode *Node, unsigned ResNo, bool IsClone,
if (TLI->isTypeLegal(VT))
UseRC = TLI->getRegClassFor(VT, Node->isDivergent());

for (SDNode *User : Node->uses()) {
for (SDNode *User : Node->users()) {
bool Match = true;
if (User->getOpcode() == ISD::CopyToReg &&
User->getOperand(2).getNode() == Node &&
Expand Down Expand Up @@ -225,7 +225,7 @@ void InstrEmitter::CreateVirtualRegisters(SDNode *Node,
}

if (!VRBase && !IsClone && !IsCloned)
for (SDNode *User : Node->uses()) {
for (SDNode *User : Node->users()) {
if (User->getOpcode() == ISD::CopyToReg &&
User->getOperand(2).getNode() == Node &&
User->getOperand(2).getResNo() == i) {
Expand Down Expand Up @@ -502,7 +502,7 @@ void InstrEmitter::EmitSubregNode(SDNode *Node, VRBaseMapType &VRBaseMap,

// If the node is only used by a CopyToReg and the dest reg is a vreg, use
// the CopyToReg'd destination register instead of creating a new vreg.
for (SDNode *User : Node->uses()) {
for (SDNode *User : Node->users()) {
if (User->getOpcode() == ISD::CopyToReg &&
User->getOperand(2).getNode() == Node) {
Register DestReg = cast<RegisterSDNode>(User->getOperand(1))->getReg();
Expand Down
Loading
Loading