Repository: llvm/llvm-project
File: llvm/lib/Transforms/Vectorize/VPlan.h (C/C++)

Code before:
                          &Start) {
    addOperand(&BackedgeValue);
  }

  VP_CLASSOF_IMPL(VPRecipeBase::VPFirstOrderRecurrencePHISC)

  VPFirstOrderRecurrencePHIRecipe *clone() override {
    return new VPFirstOrderRecurrencePHIRecipe(
        cast<PHINode>(getUnderlyingInstr()), *getOperand(0), *getOperand(1));
  }

  void execute(VPTransformState &State) override;

  /// Return the cost of this first-order recurrence phi recipe.
  InstructionCost computeCost(ElementCount VF,
                              VPCostContext &Ctx) const override;

  /// Returns true if the recipe only uses the first lane of operand \p Op.
  bool usesFirstLaneOnly(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    if (Op == getStartValue())
      return true;

    // For VF=1 the inputs will be scalar and the output will be scalar, so
    // check if the users of this PHI only require the first lane too.
    return all_of(users(),
                  [this](VPUser *U) { return U->usesFirstLaneOnly(this); });
  }

protected:
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  /// Print the recipe.
  void printRecipe(raw_ostream &O, const Twine &Indent,
                   VPSlotTracker &SlotTracker) const override;
#endif
};

/// Possible variants of a reduction.

/// This reduction is ordered and in-loop.
struct RdxOrdered {};
/// This reduction is in-loop.
struct RdxInLoop {};
/// This reduction is unordered with the partial result scaled down by some
/// factor.
struct RdxUnordered {
  unsigned VFScaleFactor;
};
using ReductionStyle = std::variant<RdxOrdered, RdxInLoop, RdxUnordered>;


Review comment (bug):
I am surprised that we dont run into an infite loop here, as the splice will be a user of the phi and the phi a user of the splice?

Overall this seems to work, but we don't have any guarantee that this only triggers for VF = 1 , e.g. all users other than the phi could only use the first lane, but if VF != 1, then the phi itself would still require all lanes and we would incorrectly return it only uses lane 0?

Looking at the related transforms `materializePackAndUnpack` and `materializeBroadcasts`, I think it may be safer to skip repliateByVF for scalar VFs like those transforms do? Nothing should need replicating in that case I think

Code after:
/// second operand.
struct VPFirstOrderRecurrencePHIRecipe : public VPHeaderPHIRecipe {
  VPFirstOrderRecurrencePHIRecipe(PHINode *Phi, VPValue &Start,
                                  VPValue &BackedgeValue)
      : VPHeaderPHIRecipe(VPRecipeBase::VPFirstOrderRecurrencePHISC, Phi,
                          &Start) {
    addOperand(&BackedgeValue);
  }

  VP_CLASSOF_IMPL(VPRecipeBase::VPFirstOrderRecurrencePHISC)

  VPFirstOrderRecurrencePHIRecipe *clone() override {
    return new VPFirstOrderRecurrencePHIRecipe(
        cast<PHINode>(getUnderlyingInstr()), *getOperand(0), *getOperand(1));
  }

  void execute(VPTransformState &State) override;

  /// Return the cost of this first-order recurrence phi recipe.
  InstructionCost computeCost(ElementCount VF,
                              VPCostContext &Ctx) const override;

  /// Returns true if the recipe only uses the first lane of operand \p Op.
  bool usesFirstLaneOnly(const VPValue *Op) const override {
    assert(is_contained(operands(), Op) &&
           "Op must be an operand of the recipe");
    return Op == getStartValue();
  }

protected:
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
  /// Print the recipe.
  void printRecipe(raw_ostream &O, const Twine &Indent,
                   VPSlotTracker &SlotTracker) const override;
#endif
};

/// Possible variants of a reduction.

/// This reduction is ordered and in-loop.
struct RdxOrdered {};
/// This reduction is in-loop.
struct RdxInLoop {};
/// This reduction is unordered with the partial result scaled down by some
/// factor.
struct RdxUnordered {
  unsigned VFScaleFactor;
};
using ReductionStyle = std::variant<RdxOrdered, RdxInLoop, RdxUnordered>;

inline ReductionStyle getReductionStyle(bool InLoop, bool Ordered,