diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h --- a/llvm/include/llvm/CodeGen/SelectionDAG.h +++ b/llvm/include/llvm/CodeGen/SelectionDAG.h @@ -1973,6 +1973,32 @@ /*PoisonOnly*/ true, Depth); } + /// Return true if Op can create undef or poison from non-undef & non-poison + /// operands. The DemandedElts argument limits the check to the requested + /// vector elements. + /// + /// \p ConsiderFlags controls whether poison producing flags on the + /// instruction are considered. This can be used to see if the instruction + /// could still introduce undef or poison even without poison generating flags + /// which might be on the instruction. (i.e. could the result of + /// Op->dropPoisonGeneratingFlags() still create poison or undef) + bool canCreateUndefOrPoison(SDValue Op, const APInt &DemandedElts, + bool PoisonOnly = false, + bool ConsiderFlags = true, + unsigned Depth = 0) const; + + /// Return true if Op can create undef or poison from non-undef & non-poison + /// operands. + /// + /// \p ConsiderFlags controls whether poison producing flags on the + /// instruction are considered. This can be used to see if the instruction + /// could still introduce undef or poison even without poison generating flags + /// which might be on the instruction. (i.e. could the result of + /// Op->dropPoisonGeneratingFlags() still create poison or undef) + bool canCreateUndefOrPoison(SDValue Op, bool PoisonOnly = false, + bool ConsiderFlags = true, + unsigned Depth = 0) const; + /// Return true if the specified operand is an ISD::ADD with a ConstantSDNode /// on the right-hand side, or if it is an ISD::OR with a ConstantSDNode that /// is guaranteed to have the same semantics as an ADD. This handles the diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -3776,6 +3776,14 @@ SDValue Op, const APInt &DemandedElts, const SelectionDAG &DAG, bool PoisonOnly, unsigned Depth) const; + /// Return true if Op can create undef or poison from non-undef & non-poison + /// operands. The DemandedElts argument limits the check to the requested + /// vector elements. + virtual bool + canCreateUndefOrPoisonForTargetNode(SDValue Op, const APInt &DemandedElts, + const SelectionDAG &DAG, bool PoisonOnly, + bool ConsiderFlags, unsigned Depth) const; + /// Tries to build a legal vector shuffle using the provided parameters /// or equivalent variations. The Mask argument maybe be modified as the /// function tries different variations. diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -13863,13 +13863,15 @@ if (DAG.isGuaranteedNotToBeUndefOrPoison(N0, /*PoisonOnly*/ false)) return N0; - // Fold freeze(bitcast(x)) -> bitcast(freeze(x)). - // TODO: Replace with pushFreezeToPreventPoisonFromPropagating fold. - if (N0.getOpcode() == ISD::BITCAST) - return DAG.getBitcast(N->getValueType(0), - DAG.getNode(ISD::FREEZE, SDLoc(N0), - N0.getOperand(0).getValueType(), - N0.getOperand(0))); + // Fold freeze(unaryop(x)) -> unaryop(freeze(x)). + // TODO: Replace with pushFreezeToPreventPoisonFromPropagating fold and + // support getNumOperands() >= 1. + if (N0.getNumOperands() == 1 && + !DAG.canCreateUndefOrPoison(N0, /*PoisonOnly*/ false) && N0->hasOneUse()) + return DAG.getNode(N0.getOpcode(), SDLoc(N0), N->getValueType(0), + DAG.getNode(ISD::FREEZE, SDLoc(N0), + N0.getOperand(0).getValueType(), + N0.getOperand(0))); return SDValue(); } diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -4535,6 +4535,48 @@ return false; } +bool SelectionDAG::canCreateUndefOrPoison(SDValue Op, bool PoisonOnly, + bool ConsiderFlags, + unsigned Depth) const { + // TODO: Assume we don't know anything for now. + EVT VT = Op.getValueType(); + if (VT.isScalableVector()) + return true; + + APInt DemandedElts = VT.isVector() + ? APInt::getAllOnes(VT.getVectorNumElements()) + : APInt(1, 1); + return canCreateUndefOrPoison(Op, DemandedElts, PoisonOnly, ConsiderFlags, + Depth); +} + +bool SelectionDAG::canCreateUndefOrPoison(SDValue Op, const APInt &DemandedElts, + bool PoisonOnly, bool ConsiderFlags, + unsigned Depth) const { + // TODO: Assume we don't know anything for now. + EVT VT = Op.getValueType(); + if (VT.isScalableVector()) + return true; + + unsigned Opcode = Op.getOpcode(); + switch (Opcode) { + case ISD::BITCAST: + case ISD::FREEZE: + return false; + + default: + // Allow the target to implement this method for its nodes. + if (Opcode >= ISD::BUILTIN_OP_END || Opcode == ISD::INTRINSIC_WO_CHAIN || + Opcode == ISD::INTRINSIC_W_CHAIN || Opcode == ISD::INTRINSIC_VOID) + return TLI->canCreateUndefOrPoisonForTargetNode( + Op, DemandedElts, *this, PoisonOnly, ConsiderFlags, Depth); + break; + } + + // Be conservative and return true. + return true; +} + bool SelectionDAG::isBaseWithConstantOffset(SDValue Op) const { if ((Op.getOpcode() != ISD::ADD && Op.getOpcode() != ISD::OR) || !isa(Op.getOperand(1))) diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -3580,6 +3580,19 @@ return false; } +bool TargetLowering::canCreateUndefOrPoisonForTargetNode( + SDValue Op, const APInt &DemandedElts, const SelectionDAG &DAG, + bool PoisonOnly, bool ConsiderFlags, unsigned Depth) const { + assert((Op.getOpcode() >= ISD::BUILTIN_OP_END || + Op.getOpcode() == ISD::INTRINSIC_WO_CHAIN || + Op.getOpcode() == ISD::INTRINSIC_W_CHAIN || + Op.getOpcode() == ISD::INTRINSIC_VOID) && + "Should use canCreateUndefOrPoison if you don't know whether Op" + " is a target node!"); + // Be conservative and return true. + return true; +} + bool TargetLowering::isKnownNeverNaNForTargetNode(SDValue Op, const SelectionDAG &DAG, bool SNaN,