Skip to content

Commit

Permalink
[VPlan] Model masked assumes as replicate recipes, drop them (NFCI).
Browse files Browse the repository at this point in the history
Replace ConditionalAssume set by treating conditional assumes like other
predicated instructions (i.e. create a VPReplicateRecipe with a mask)
and later remove any assume recipes with masks during VPlan cleanup.

This reduces coupling of VPlan construction and Legal by removing a
shared set between the 2 and results in a cleaner code structure
overall.

Reviewed By: Ayal

Differential Revision: https://reviews.llvm.org/D157034
  • Loading branch information
fhahn committed Aug 6, 2023
1 parent a0b1c23 commit aac8acb
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 61 deletions.
25 changes: 7 additions & 18 deletions llvm/include/llvm/Transforms/Vectorize/LoopVectorizationLegality.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,12 +383,6 @@ class LoopVectorizationLegality {
unsigned getNumStores() const { return LAI->getNumStores(); }
unsigned getNumLoads() const { return LAI->getNumLoads(); }

/// Returns all assume calls in predicated blocks. They need to be dropped
/// when flattening the CFG.
const SmallPtrSetImpl<Instruction *> &getConditionalAssumes() const {
return ConditionalAssumes;
}

PredicatedScalarEvolution *getPredicatedScalarEvolution() const {
return &PSE;
}
Expand Down Expand Up @@ -450,13 +444,11 @@ class LoopVectorizationLegality {
/// \p SafePtrs is a list of addresses that are known to be legal and we know
/// that we can read from them without segfault.
/// \p MaskedOp is a list of instructions that have to be transformed into
/// calls to the appropriate masked intrinsic when the loop is vectorized.
/// \p ConditionalAssumes is a list of assume instructions in predicated
/// blocks that must be dropped if the CFG gets flattened.
bool blockCanBePredicated(
BasicBlock *BB, SmallPtrSetImpl<Value *> &SafePtrs,
SmallPtrSetImpl<const Instruction *> &MaskedOp,
SmallPtrSetImpl<Instruction *> &ConditionalAssumes) const;
/// calls to the appropriate masked intrinsic when the loop is vectorized
/// or dropped if the instruction is a conditional assume intrinsic.
bool
blockCanBePredicated(BasicBlock *BB, SmallPtrSetImpl<Value *> &SafePtrs,
SmallPtrSetImpl<const Instruction *> &MaskedOp) const;

/// Updates the vectorization state by adding \p Phi to the inductions list.
/// This can set \p Phi as the main induction of the loop if \p Phi is a
Expand Down Expand Up @@ -539,13 +531,10 @@ class LoopVectorizationLegality {
AssumptionCache *AC;

/// While vectorizing these instructions we have to generate a
/// call to the appropriate masked intrinsic
/// call to the appropriate masked intrinsic or drop them in case of
/// conditional assumes.
SmallPtrSet<const Instruction *, 8> MaskedOp;

/// Assume instructions in predicated blocks must be dropped if the CFG gets
/// flattened.
SmallPtrSet<Instruction *, 8> ConditionalAssumes;

/// BFI and PSI are used to check for profile guided size optimizations.
BlockFrequencyInfo *BFI;
ProfileSummaryInfo *PSI;
Expand Down
31 changes: 12 additions & 19 deletions llvm/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1242,13 +1242,12 @@ bool LoopVectorizationLegality::blockNeedsPredication(BasicBlock *BB) const {

bool LoopVectorizationLegality::blockCanBePredicated(
BasicBlock *BB, SmallPtrSetImpl<Value *> &SafePtrs,
SmallPtrSetImpl<const Instruction *> &MaskedOp,
SmallPtrSetImpl<Instruction *> &ConditionalAssumes) const {
SmallPtrSetImpl<const Instruction *> &MaskedOp) const {
for (Instruction &I : *BB) {
// We can predicate blocks with calls to assume, as long as we drop them in
// case we flatten the CFG via predication.
if (match(&I, m_Intrinsic<Intrinsic::assume>())) {
ConditionalAssumes.insert(&I);
MaskedOp.insert(&I);
continue;
}

Expand Down Expand Up @@ -1345,16 +1344,13 @@ bool LoopVectorizationLegality::canVectorizeWithIfConvert() {
}

// We must be able to predicate all blocks that need to be predicated.
if (blockNeedsPredication(BB)) {
if (!blockCanBePredicated(BB, SafePointers, MaskedOp,
ConditionalAssumes)) {
reportVectorizationFailure(
"Control flow cannot be substituted for a select",
"control flow cannot be substituted for a select",
"NoCFGForSelect", ORE, TheLoop,
BB->getTerminator());
return false;
}
if (blockNeedsPredication(BB) &&
!blockCanBePredicated(BB, SafePointers, MaskedOp)) {
reportVectorizationFailure(
"Control flow cannot be substituted for a select",
"control flow cannot be substituted for a select", "NoCFGForSelect",
ORE, TheLoop, BB->getTerminator());
return false;
}
}

Expand Down Expand Up @@ -1554,14 +1550,14 @@ bool LoopVectorizationLegality::prepareToFoldTailByMasking() {
// The list of pointers that we can safely read and write to remains empty.
SmallPtrSet<Value *, 8> SafePointers;

// Collect masked ops in temporary set first to avoid partially populating
// MaskedOp if a block cannot be predicated.
SmallPtrSet<const Instruction *, 8> TmpMaskedOp;
SmallPtrSet<Instruction *, 8> TmpConditionalAssumes;

// Check and mark all blocks for predication, including those that ordinarily
// do not need predication such as the header block.
for (BasicBlock *BB : TheLoop->blocks()) {
if (!blockCanBePredicated(BB, SafePointers, TmpMaskedOp,
TmpConditionalAssumes)) {
if (!blockCanBePredicated(BB, SafePointers, TmpMaskedOp)) {
LLVM_DEBUG(dbgs() << "LV: Cannot fold tail by masking as requested.\n");
return false;
}
Expand All @@ -1570,9 +1566,6 @@ bool LoopVectorizationLegality::prepareToFoldTailByMasking() {
LLVM_DEBUG(dbgs() << "LV: can fold tail by masking.\n");

MaskedOp.insert(TmpMaskedOp.begin(), TmpMaskedOp.end());
ConditionalAssumes.insert(TmpConditionalAssumes.begin(),
TmpConditionalAssumes.end());

return true;
}

Expand Down
3 changes: 1 addition & 2 deletions llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,8 +377,7 @@ class LoopVectorizationPlanner {
/// returned VPlan is valid for. If no VPlan can be built for the input range,
/// set the largest included VF to the maximum VF for which no plan could be
/// built.
VPlanPtr tryToBuildVPlanWithVPRecipes(
VFRange &Range, SmallPtrSetImpl<Instruction *> &DeadInstructions);
VPlanPtr tryToBuildVPlanWithVPRecipes(VFRange &Range);

/// Build VPlans for power-of-2 VF's between \p MinVF and \p MaxVF inclusive,
/// according to the information gathered by Legal when it checked if it is
Expand Down
22 changes: 4 additions & 18 deletions llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8697,18 +8697,10 @@ void LoopVectorizationPlanner::buildVPlansWithVPRecipes(ElementCount MinVF,
ElementCount MaxVF) {
assert(OrigLoop->isInnermost() && "Inner loop expected.");

// Add assume instructions we need to drop to DeadInstructions, to prevent
// them from being added to the VPlan.
// TODO: We only need to drop assumes in blocks that get flattend. If the
// control flow is preserved, we should keep them.
SmallPtrSet<Instruction *, 4> DeadInstructions;
auto &ConditionalAssumes = Legal->getConditionalAssumes();
DeadInstructions.insert(ConditionalAssumes.begin(), ConditionalAssumes.end());

auto MaxVFTimes2 = MaxVF * 2;
for (ElementCount VF = MinVF; ElementCount::isKnownLT(VF, MaxVFTimes2);) {
VFRange SubRange = {VF, MaxVFTimes2};
if (auto Plan = tryToBuildVPlanWithVPRecipes(SubRange, DeadInstructions)) {
if (auto Plan = tryToBuildVPlanWithVPRecipes(SubRange)) {
// Now optimize the initial VPlan.
VPlanTransforms::optimize(*Plan, *PSE.getSE());
assert(VPlanVerifier::verifyPlanIsValid(*Plan) && "VPlan is invalid");
Expand Down Expand Up @@ -8845,8 +8837,8 @@ static void addUsersInExitBlock(VPBasicBlock *HeaderVPBB,
}
}

VPlanPtr LoopVectorizationPlanner::tryToBuildVPlanWithVPRecipes(
VFRange &Range, SmallPtrSetImpl<Instruction *> &DeadInstructions) {
VPlanPtr
LoopVectorizationPlanner::tryToBuildVPlanWithVPRecipes(VFRange &Range) {

SmallPtrSet<const InterleaveGroup<Instruction> *, 1> InterleaveGroups;

Expand Down Expand Up @@ -8930,14 +8922,8 @@ VPlanPtr LoopVectorizationPlanner::tryToBuildVPlanWithVPRecipes(

// Introduce each ingredient into VPlan.
// TODO: Model and preserve debug intrinsics in VPlan.
for (Instruction &I : BB->instructionsWithoutDebug(false)) {
for (Instruction &I : drop_end(BB->instructionsWithoutDebug(false))) {
Instruction *Instr = &I;

// First filter out irrelevant instructions, to ensure no recipes are
// built for them.
if (isa<BranchInst>(Instr) || DeadInstructions.count(Instr))
continue;

SmallVector<VPValue *, 4> Operands;
auto *Phi = dyn_cast<PHINode>(Instr);
if (Phi && Phi->getParent() == OrigLoop->getHeader()) {
Expand Down
21 changes: 17 additions & 4 deletions llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@
//===----------------------------------------------------------------------===//

#include "VPlanTransforms.h"
#include "VPlanDominatorTree.h"
#include "VPRecipeBuilder.h"
#include "VPlanCFG.h"
#include "VPlanDominatorTree.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/IVDescriptors.h"
#include "llvm/Analysis/VectorUtils.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/PatternMatch.h"

using namespace llvm;

using namespace llvm::PatternMatch;

void VPlanTransforms::VPInstructionsToVPRecipes(
VPlanPtr &Plan,
function_ref<const InductionDescriptor *(PHINode *)>
Expand Down Expand Up @@ -479,10 +482,20 @@ void VPlanTransforms::removeDeadRecipes(VPlan &Plan) {
// The recipes in the block are processed in reverse order, to catch chains
// of dead recipes.
for (VPRecipeBase &R : make_early_inc_range(reverse(*VPBB))) {
if (R.mayHaveSideEffects() || any_of(R.definedValues(), [](VPValue *V) {
return V->getNumUsers() > 0;
}))
// A user keeps R alive:
if (any_of(R.definedValues(),
[](VPValue *V) { return V->getNumUsers(); }))
continue;

// Having side effects keeps R alive, but do remove conditional assume
// instructions as their conditions may be flattened.
auto *RepR = dyn_cast<VPReplicateRecipe>(&R);
bool IsConditionalAssume =
RepR && RepR->isPredicated() &&
match(RepR->getUnderlyingInstr(), m_Intrinsic<Intrinsic::assume>());
if (R.mayHaveSideEffects() && !IsConditionalAssume)
continue;

R.eraseFromParent();
}
}
Expand Down

0 comments on commit aac8acb

Please sign in to comment.