Introduce a new VPRegionValue for the header mask, managed by loop regions similar to the canonical IV. The main benefit is we do not need to materialize the mask early and it is trivial to find (no more need for patterns hard-coded in findHeaderMask). It is currently materialized before computing costs, as we compute costs for part of it currently. PR: https://github.com/llvm/llvm-project/pull/196199
2251 lines
95 KiB
C++
2251 lines
95 KiB
C++
//===-- VPlanConstruction.cpp - Transforms for initial VPlan construction -===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// This file implements transforms for initial VPlan construction.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "LoopVectorizationPlanner.h"
|
|
#include "VPlan.h"
|
|
#include "VPlanAnalysis.h"
|
|
#include "VPlanCFG.h"
|
|
#include "VPlanDominatorTree.h"
|
|
#include "VPlanHelpers.h"
|
|
#include "VPlanPatternMatch.h"
|
|
#include "VPlanTransforms.h"
|
|
#include "VPlanUtils.h"
|
|
#include "llvm/ADT/SmallVectorExtras.h"
|
|
#include "llvm/Analysis/Loads.h"
|
|
#include "llvm/Analysis/LoopInfo.h"
|
|
#include "llvm/Analysis/LoopIterator.h"
|
|
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
|
|
#include "llvm/Analysis/ScalarEvolution.h"
|
|
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
|
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
#include "llvm/IR/InstrTypes.h"
|
|
#include "llvm/IR/MDBuilder.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Transforms/Utils/LoopUtils.h"
|
|
#include "llvm/Transforms/Utils/LoopVersioning.h"
|
|
#include "llvm/Transforms/Vectorize/LoopVectorize.h"
|
|
|
|
#define DEBUG_TYPE "vplan"
|
|
|
|
using namespace llvm;
|
|
using namespace LoopVectorizationUtils;
|
|
using namespace VPlanPatternMatch;
|
|
|
|
namespace {
|
|
// Class that is used to build the plain CFG for the incoming IR.
|
|
class PlainCFGBuilder {
|
|
// The outermost loop of the input loop nest considered for vectorization.
|
|
Loop *TheLoop;
|
|
|
|
// Loop Info analysis.
|
|
LoopInfo *LI;
|
|
|
|
// Loop versioning for alias metadata.
|
|
LoopVersioning *LVer;
|
|
|
|
// Vectorization plan that we are working on.
|
|
std::unique_ptr<VPlan> Plan;
|
|
|
|
// Builder of the VPlan instruction-level representation.
|
|
VPBuilder VPIRBuilder;
|
|
|
|
// NOTE: The following maps are intentionally destroyed after the plain CFG
|
|
// construction because subsequent VPlan-to-VPlan transformation may
|
|
// invalidate them.
|
|
// Map incoming BasicBlocks to their newly-created VPBasicBlocks.
|
|
DenseMap<BasicBlock *, VPBasicBlock *> BB2VPBB;
|
|
// Map incoming Value definitions to their newly-created VPValues.
|
|
DenseMap<Value *, VPValue *> IRDef2VPValue;
|
|
|
|
// Hold phi node's that need to be fixed once the plain CFG has been built.
|
|
SmallVector<PHINode *, 8> PhisToFix;
|
|
|
|
// Utility functions.
|
|
void setVPBBPredsFromBB(VPBasicBlock *VPBB, BasicBlock *BB);
|
|
void fixHeaderPhis();
|
|
VPBasicBlock *getOrCreateVPBB(BasicBlock *BB);
|
|
#ifndef NDEBUG
|
|
bool isExternalDef(Value *Val);
|
|
#endif
|
|
VPValue *getOrCreateVPOperand(Value *IRVal);
|
|
void createVPInstructionsForVPBB(VPBasicBlock *VPBB, BasicBlock *BB);
|
|
|
|
public:
|
|
PlainCFGBuilder(Loop *Lp, LoopInfo *LI, LoopVersioning *LVer, Type *IdxTy)
|
|
: TheLoop(Lp), LI(LI), LVer(LVer),
|
|
Plan(std::make_unique<VPlan>(Lp, IdxTy)) {}
|
|
|
|
/// Build plain CFG for TheLoop and connect it to Plan's entry.
|
|
std::unique_ptr<VPlan> buildPlainCFG();
|
|
};
|
|
} // anonymous namespace
|
|
|
|
// Set predecessors of \p VPBB in the same order as they are in \p BB. \p VPBB
|
|
// must have no predecessors.
|
|
void PlainCFGBuilder::setVPBBPredsFromBB(VPBasicBlock *VPBB, BasicBlock *BB) {
|
|
// Collect VPBB predecessors.
|
|
SmallVector<VPBlockBase *, 2> VPBBPreds;
|
|
for (BasicBlock *Pred : predecessors(BB))
|
|
VPBBPreds.push_back(getOrCreateVPBB(Pred));
|
|
VPBB->setPredecessors(VPBBPreds);
|
|
}
|
|
|
|
static bool isHeaderBB(BasicBlock *BB, Loop *L) {
|
|
return L && BB == L->getHeader();
|
|
}
|
|
|
|
// Add operands to VPInstructions representing phi nodes from the input IR.
|
|
void PlainCFGBuilder::fixHeaderPhis() {
|
|
for (auto *Phi : PhisToFix) {
|
|
assert(IRDef2VPValue.count(Phi) && "Missing VPInstruction for PHINode.");
|
|
VPValue *VPVal = IRDef2VPValue[Phi];
|
|
assert(isa<VPPhi>(VPVal) && "Expected VPPhi for phi node.");
|
|
auto *PhiR = cast<VPPhi>(VPVal);
|
|
assert(PhiR->getNumOperands() == 0 && "Expected VPPhi with no operands.");
|
|
assert(isHeaderBB(Phi->getParent(), LI->getLoopFor(Phi->getParent())) &&
|
|
"Expected Phi in header block.");
|
|
assert(Phi->getNumOperands() == 2 &&
|
|
"header phi must have exactly 2 operands");
|
|
for (BasicBlock *Pred : predecessors(Phi->getParent()))
|
|
PhiR->addIncoming(
|
|
getOrCreateVPOperand(Phi->getIncomingValueForBlock(Pred)));
|
|
}
|
|
}
|
|
|
|
// Create a new empty VPBasicBlock for an incoming BasicBlock or retrieve an
|
|
// existing one if it was already created.
|
|
VPBasicBlock *PlainCFGBuilder::getOrCreateVPBB(BasicBlock *BB) {
|
|
if (auto *VPBB = BB2VPBB.lookup(BB)) {
|
|
// Retrieve existing VPBB.
|
|
return VPBB;
|
|
}
|
|
|
|
// Create new VPBB.
|
|
StringRef Name = BB->getName();
|
|
LLVM_DEBUG(dbgs() << "Creating VPBasicBlock for " << Name << "\n");
|
|
VPBasicBlock *VPBB = Plan->createVPBasicBlock(Name);
|
|
BB2VPBB[BB] = VPBB;
|
|
return VPBB;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
// Return true if \p Val is considered an external definition. An external
|
|
// definition is either:
|
|
// 1. A Value that is not an Instruction. This will be refined in the future.
|
|
// 2. An Instruction that is outside of the IR region represented in VPlan,
|
|
// i.e., is not part of the loop nest.
|
|
bool PlainCFGBuilder::isExternalDef(Value *Val) {
|
|
// All the Values that are not Instructions are considered external
|
|
// definitions for now.
|
|
Instruction *Inst = dyn_cast<Instruction>(Val);
|
|
if (!Inst)
|
|
return true;
|
|
|
|
// Check whether Instruction definition is in loop body.
|
|
return !TheLoop->contains(Inst);
|
|
}
|
|
#endif
|
|
|
|
// Create a new VPValue or retrieve an existing one for the Instruction's
|
|
// operand \p IRVal. This function must only be used to create/retrieve VPValues
|
|
// for *Instruction's operands* and not to create regular VPInstruction's. For
|
|
// the latter, please, look at 'createVPInstructionsForVPBB'.
|
|
VPValue *PlainCFGBuilder::getOrCreateVPOperand(Value *IRVal) {
|
|
auto VPValIt = IRDef2VPValue.find(IRVal);
|
|
if (VPValIt != IRDef2VPValue.end())
|
|
// Operand has an associated VPInstruction or VPValue that was previously
|
|
// created.
|
|
return VPValIt->second;
|
|
|
|
// Operand doesn't have a previously created VPInstruction/VPValue. This
|
|
// means that operand is:
|
|
// A) a definition external to VPlan,
|
|
// B) any other Value without specific representation in VPlan.
|
|
// For now, we use VPValue to represent A and B and classify both as external
|
|
// definitions. We may introduce specific VPValue subclasses for them in the
|
|
// future.
|
|
assert(isExternalDef(IRVal) && "Expected external definition as operand.");
|
|
|
|
// A and B: Create VPValue and add it to the pool of external definitions and
|
|
// to the Value->VPValue map.
|
|
VPValue *NewVPVal = Plan->getOrAddLiveIn(IRVal);
|
|
IRDef2VPValue[IRVal] = NewVPVal;
|
|
return NewVPVal;
|
|
}
|
|
|
|
// Create new VPInstructions in a VPBasicBlock, given its BasicBlock
|
|
// counterpart. This function must be invoked in RPO so that the operands of a
|
|
// VPInstruction in \p BB have been visited before (except for Phi nodes).
|
|
void PlainCFGBuilder::createVPInstructionsForVPBB(VPBasicBlock *VPBB,
|
|
BasicBlock *BB) {
|
|
VPIRBuilder.setInsertPoint(VPBB);
|
|
// TODO: Model and preserve debug intrinsics in VPlan.
|
|
for (Instruction &InstRef : *BB) {
|
|
Instruction *Inst = &InstRef;
|
|
|
|
// There shouldn't be any VPValue for Inst at this point. Otherwise, we
|
|
// visited Inst when we shouldn't, breaking the RPO traversal order.
|
|
assert(!IRDef2VPValue.count(Inst) &&
|
|
"Instruction shouldn't have been visited.");
|
|
|
|
if (isa<UncondBrInst>(Inst))
|
|
// Skip the rest of the Instruction processing for Branch instructions.
|
|
continue;
|
|
|
|
if (auto *Br = dyn_cast<CondBrInst>(Inst)) {
|
|
// Conditional branch instruction are represented using BranchOnCond
|
|
// recipes.
|
|
VPValue *Cond = getOrCreateVPOperand(Br->getCondition());
|
|
VPIRBuilder.createNaryOp(VPInstruction::BranchOnCond, {Cond}, Inst, {},
|
|
VPIRMetadata(*Inst), Inst->getDebugLoc());
|
|
continue;
|
|
}
|
|
|
|
if (auto *SI = dyn_cast<SwitchInst>(Inst)) {
|
|
// Don't emit recipes for unconditional switch instructions.
|
|
if (SI->getNumCases() == 0)
|
|
continue;
|
|
SmallVector<VPValue *> Ops = {getOrCreateVPOperand(SI->getCondition())};
|
|
for (auto Case : SI->cases())
|
|
Ops.push_back(getOrCreateVPOperand(Case.getCaseValue()));
|
|
VPIRBuilder.createNaryOp(Instruction::Switch, Ops, Inst, {},
|
|
VPIRMetadata(*Inst), Inst->getDebugLoc());
|
|
continue;
|
|
}
|
|
|
|
VPSingleDefRecipe *NewR;
|
|
if (auto *Phi = dyn_cast<PHINode>(Inst)) {
|
|
// Phi node's operands may not have been visited at this point. We create
|
|
// an empty VPInstruction that we will fix once the whole plain CFG has
|
|
// been built.
|
|
NewR = VPIRBuilder.createScalarPhi({}, Phi->getDebugLoc(), "vec.phi",
|
|
*Phi, Phi->getType());
|
|
NewR->setUnderlyingValue(Phi);
|
|
if (isHeaderBB(Phi->getParent(), LI->getLoopFor(Phi->getParent()))) {
|
|
// Header phis need to be fixed after the VPBB for the latch has been
|
|
// created.
|
|
PhisToFix.push_back(Phi);
|
|
} else {
|
|
// Add operands for VPPhi in the order matching its predecessors in
|
|
// VPlan.
|
|
DenseMap<const VPBasicBlock *, VPValue *> VPPredToIncomingValue;
|
|
for (unsigned I = 0; I != Phi->getNumOperands(); ++I) {
|
|
VPPredToIncomingValue[BB2VPBB[Phi->getIncomingBlock(I)]] =
|
|
getOrCreateVPOperand(Phi->getIncomingValue(I));
|
|
}
|
|
for (VPBlockBase *Pred : VPBB->getPredecessors())
|
|
cast<VPPhi>(NewR)->addIncoming(
|
|
VPPredToIncomingValue.lookup(Pred->getExitingBasicBlock()));
|
|
}
|
|
} else {
|
|
// Build VPIRMetadata from the instruction and add loop versioning
|
|
// metadata for loads and stores.
|
|
VPIRMetadata MD(*Inst);
|
|
if (isa<LoadInst, StoreInst>(Inst) && LVer) {
|
|
const auto &[AliasScopeMD, NoAliasMD] =
|
|
LVer->getNoAliasMetadataFor(Inst);
|
|
if (AliasScopeMD)
|
|
MD.setMetadata(LLVMContext::MD_alias_scope, AliasScopeMD);
|
|
if (NoAliasMD)
|
|
MD.setMetadata(LLVMContext::MD_noalias, NoAliasMD);
|
|
}
|
|
|
|
// Translate LLVM-IR operands into VPValue operands and set them in the
|
|
// new VPInstruction.
|
|
SmallVector<VPValue *, 4> VPOperands;
|
|
for (Value *Op : Inst->operands())
|
|
VPOperands.push_back(getOrCreateVPOperand(Op));
|
|
|
|
if (auto *CI = dyn_cast<CastInst>(Inst)) {
|
|
NewR = VPIRBuilder.createScalarCast(CI->getOpcode(), VPOperands[0],
|
|
CI->getType(), CI->getDebugLoc(),
|
|
VPIRFlags(*CI), MD);
|
|
NewR->setUnderlyingValue(CI);
|
|
} else if (auto *LI = dyn_cast<LoadInst>(Inst)) {
|
|
NewR = VPIRBuilder.createScalarLoad(LI->getType(), VPOperands[0],
|
|
LI->getDebugLoc(), MD);
|
|
NewR->setUnderlyingValue(LI);
|
|
} else {
|
|
// Build VPInstruction for any arbitrary Instruction without specific
|
|
// representation in VPlan.
|
|
NewR = VPIRBuilder.createNaryOp(
|
|
Inst->getOpcode(), VPOperands, Inst, VPIRFlags(*Inst), MD,
|
|
Inst->getDebugLoc(), "", Inst->getType());
|
|
}
|
|
}
|
|
|
|
IRDef2VPValue[Inst] = NewR;
|
|
}
|
|
}
|
|
|
|
// Main interface to build the plain CFG.
|
|
std::unique_ptr<VPlan> PlainCFGBuilder::buildPlainCFG() {
|
|
VPIRBasicBlock *Entry = cast<VPIRBasicBlock>(Plan->getEntry());
|
|
BB2VPBB[Entry->getIRBasicBlock()] = Entry;
|
|
for (VPIRBasicBlock *ExitVPBB : Plan->getExitBlocks())
|
|
BB2VPBB[ExitVPBB->getIRBasicBlock()] = ExitVPBB;
|
|
|
|
// 1. Scan the body of the loop in a topological order to visit each basic
|
|
// block after having visited its predecessor basic blocks. Create a VPBB for
|
|
// each BB and link it to its successor and predecessor VPBBs. Note that
|
|
// predecessors must be set in the same order as they are in the incomming IR.
|
|
// Otherwise, there might be problems with existing phi nodes and algorithm
|
|
// based on predecessors traversal.
|
|
|
|
// Loop PH needs to be explicitly visited since it's not taken into account by
|
|
// LoopBlocksDFS.
|
|
BasicBlock *ThePreheaderBB = TheLoop->getLoopPreheader();
|
|
assert((ThePreheaderBB->getTerminator()->getNumSuccessors() == 1) &&
|
|
"Unexpected loop preheader");
|
|
for (auto &I : *ThePreheaderBB) {
|
|
if (I.getType()->isVoidTy())
|
|
continue;
|
|
IRDef2VPValue[&I] = Plan->getOrAddLiveIn(&I);
|
|
}
|
|
|
|
LoopBlocksRPO RPO(TheLoop);
|
|
RPO.perform(LI);
|
|
|
|
for (BasicBlock *BB : RPO) {
|
|
// Create or retrieve the VPBasicBlock for this BB.
|
|
VPBasicBlock *VPBB = getOrCreateVPBB(BB);
|
|
// Set VPBB predecessors in the same order as they are in the incoming BB.
|
|
setVPBBPredsFromBB(VPBB, BB);
|
|
|
|
// Create VPInstructions for BB.
|
|
createVPInstructionsForVPBB(VPBB, BB);
|
|
|
|
// Set VPBB successors. We create empty VPBBs for successors if they don't
|
|
// exist already. Recipes will be created when the successor is visited
|
|
// during the RPO traversal.
|
|
if (auto *SI = dyn_cast<SwitchInst>(BB->getTerminator())) {
|
|
SmallVector<VPBlockBase *> Succs = {
|
|
getOrCreateVPBB(SI->getDefaultDest())};
|
|
for (auto Case : SI->cases())
|
|
Succs.push_back(getOrCreateVPBB(Case.getCaseSuccessor()));
|
|
VPBB->setSuccessors(Succs);
|
|
continue;
|
|
}
|
|
if (auto *BI = dyn_cast<UncondBrInst>(BB->getTerminator())) {
|
|
VPBB->setOneSuccessor(getOrCreateVPBB(BI->getSuccessor()));
|
|
continue;
|
|
}
|
|
auto *BI = cast<CondBrInst>(BB->getTerminator());
|
|
BasicBlock *IRSucc0 = BI->getSuccessor(0);
|
|
BasicBlock *IRSucc1 = BI->getSuccessor(1);
|
|
VPBasicBlock *Successor0 = getOrCreateVPBB(IRSucc0);
|
|
VPBasicBlock *Successor1 = getOrCreateVPBB(IRSucc1);
|
|
VPBB->setTwoSuccessors(Successor0, Successor1);
|
|
}
|
|
|
|
for (auto *EB : Plan->getExitBlocks())
|
|
setVPBBPredsFromBB(EB, EB->getIRBasicBlock());
|
|
|
|
// 2. The whole CFG has been built at this point so all the input Values must
|
|
// have a VPlan counterpart. Fix VPlan header phi by adding their
|
|
// corresponding VPlan operands.
|
|
fixHeaderPhis();
|
|
|
|
Plan->getEntry()->setOneSuccessor(getOrCreateVPBB(TheLoop->getHeader()));
|
|
Plan->getEntry()->setPlan(&*Plan);
|
|
|
|
// Fix VPlan loop-closed-ssa exit phi's by adding incoming operands to the
|
|
// VPIRInstructions wrapping them.
|
|
// // Note that the operand order corresponds to IR predecessor order, and may
|
|
// need adjusting when VPlan predecessors are added, if an exit block has
|
|
// multiple predecessor.
|
|
for (auto *EB : Plan->getExitBlocks()) {
|
|
for (VPRecipeBase &R : EB->phis()) {
|
|
auto *PhiR = cast<VPIRPhi>(&R);
|
|
PHINode &Phi = PhiR->getIRPhi();
|
|
assert(PhiR->getNumOperands() == 0 &&
|
|
"no phi operands should be added yet");
|
|
for (BasicBlock *Pred : predecessors(EB->getIRBasicBlock()))
|
|
PhiR->addIncoming(
|
|
getOrCreateVPOperand(Phi.getIncomingValueForBlock(Pred)));
|
|
}
|
|
}
|
|
|
|
LLVM_DEBUG(Plan->setName("Plain CFG\n"); dbgs() << *Plan);
|
|
return std::move(Plan);
|
|
}
|
|
|
|
/// Checks if \p HeaderVPB is a loop header block in the plain CFG; that is, it
|
|
/// has exactly 2 predecessors (preheader and latch), where the block
|
|
/// dominates the latch and the preheader dominates the block. If it is a
|
|
/// header block return true and canonicalize the predecessors of the header
|
|
/// (making sure the preheader appears first and the latch second) and the
|
|
/// successors of the latch (making sure the loop exit comes first). Otherwise
|
|
/// return false.
|
|
static bool canonicalHeaderAndLatch(VPBlockBase *HeaderVPB,
|
|
const VPDominatorTree &VPDT) {
|
|
ArrayRef<VPBlockBase *> Preds = HeaderVPB->getPredecessors();
|
|
if (Preds.size() != 2)
|
|
return false;
|
|
|
|
auto *PreheaderVPBB = Preds[0];
|
|
auto *LatchVPBB = Preds[1];
|
|
if (!VPDT.dominates(PreheaderVPBB, HeaderVPB) ||
|
|
!VPDT.dominates(HeaderVPB, LatchVPBB)) {
|
|
std::swap(PreheaderVPBB, LatchVPBB);
|
|
|
|
if (!VPDT.dominates(PreheaderVPBB, HeaderVPB) ||
|
|
!VPDT.dominates(HeaderVPB, LatchVPBB))
|
|
return false;
|
|
|
|
// Canonicalize predecessors of header so that preheader is first and
|
|
// latch second.
|
|
HeaderVPB->swapPredecessors();
|
|
for (VPRecipeBase &R : cast<VPBasicBlock>(HeaderVPB)->phis())
|
|
R.swapOperands();
|
|
}
|
|
|
|
// The two successors of conditional branch match the condition, with the
|
|
// first successor corresponding to true and the second to false. We
|
|
// canonicalize the successors of the latch when introducing the region, such
|
|
// that the latch exits the region when its condition is true; invert the
|
|
// original condition if the original CFG branches to the header on true.
|
|
// Note that the exit edge is not yet connected for top-level loops.
|
|
if (LatchVPBB->getSingleSuccessor() ||
|
|
LatchVPBB->getSuccessors()[0] != HeaderVPB)
|
|
return true;
|
|
|
|
assert(LatchVPBB->getNumSuccessors() == 2 && "Must have 2 successors");
|
|
auto *Term = cast<VPBasicBlock>(LatchVPBB)->getTerminator();
|
|
assert(cast<VPInstruction>(Term)->getOpcode() ==
|
|
VPInstruction::BranchOnCond &&
|
|
"terminator must be a BranchOnCond");
|
|
auto *Not = new VPInstruction(VPInstruction::Not, {Term->getOperand(0)});
|
|
Not->insertBefore(Term);
|
|
Term->setOperand(0, Not);
|
|
LatchVPBB->swapSuccessors();
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Create a new VPRegionBlock for the loop starting at \p HeaderVPB. For the
|
|
/// outermost loop adjust the regions exiting terminator to be based on the
|
|
/// canonical IV.
|
|
static void createLoopRegion(VPlan &Plan, VPBlockBase *HeaderVPB, DebugLoc DL) {
|
|
auto *PreheaderVPBB = HeaderVPB->getPredecessors()[0];
|
|
auto *LatchVPBB = cast<VPBasicBlock>(HeaderVPB->getPredecessors()[1]);
|
|
auto *OutermostHeaderVPBB =
|
|
VPBlockUtils::getPlainCFGHeaderAndLatch(Plan).first;
|
|
|
|
VPBlockUtils::disconnectBlocks(PreheaderVPBB, HeaderVPB);
|
|
VPBlockUtils::disconnectBlocks(LatchVPBB, HeaderVPB);
|
|
|
|
// Create an empty region first and insert it between PreheaderVPBB and
|
|
// the exit blocks, taking care to preserve the original predecessor &
|
|
// successor order of blocks. Set region entry and exiting after both
|
|
// HeaderVPB and LatchVPBB have been disconnected from their
|
|
// predecessors/successors. Only the outermost loop has a canonical IV. Nested
|
|
// loops are assigned a canonical IV of null type and unknown debug location.
|
|
bool IsOutermost = HeaderVPB == OutermostHeaderVPBB;
|
|
Type *CanIVTy = nullptr;
|
|
if (IsOutermost)
|
|
CanIVTy = Plan.getVectorTripCount().getType();
|
|
else
|
|
DL = DebugLoc::getUnknown();
|
|
auto *R = Plan.createLoopRegion(CanIVTy, DL);
|
|
|
|
// Transfer latch's successors to the region.
|
|
VPBlockUtils::transferSuccessors(LatchVPBB, R);
|
|
|
|
VPBlockUtils::connectBlocks(PreheaderVPBB, R);
|
|
R->setEntry(HeaderVPB);
|
|
R->setExiting(LatchVPBB);
|
|
|
|
// All VPBB's reachable shallowly from HeaderVPB belong to the current region.
|
|
for (VPBlockBase *VPBB : vp_depth_first_shallow(HeaderVPB))
|
|
VPBB->setParent(R);
|
|
|
|
if (!IsOutermost)
|
|
return;
|
|
|
|
auto *LatchTerm = LatchVPBB->getTerminator();
|
|
VPBuilder Builder(LatchTerm);
|
|
// Add a VPInstruction to increment the scalar canonical IV by VF * UF.
|
|
// Initially the induction increment is guaranteed to not wrap, but that may
|
|
// change later, e.g. when tail-folding, when the flags need to be dropped.
|
|
auto *CanonicalIVIncrement = Builder.createAdd(
|
|
R->getCanonicalIV(), &Plan.getVFxUF(), DL, "index.next", {true, false});
|
|
|
|
if (match(LatchTerm, m_BranchOnTwoConds())) {
|
|
auto *IsLatchExitTaken = Builder.createICmp(
|
|
CmpInst::ICMP_EQ, CanonicalIVIncrement, &Plan.getVectorTripCount());
|
|
LatchTerm->setOperand(1, IsLatchExitTaken);
|
|
} else {
|
|
// We are replacing the branch to exit the region. Remove the original
|
|
// BranchOnCond.
|
|
assert(match(LatchTerm, m_BranchOnCond()) && "Unexpected terminator");
|
|
DebugLoc LatchDL = LatchTerm->getDebugLoc();
|
|
Builder.createNaryOp(VPInstruction::BranchOnCount,
|
|
{CanonicalIVIncrement, &Plan.getVectorTripCount()},
|
|
LatchDL);
|
|
LatchTerm->eraseFromParent();
|
|
}
|
|
}
|
|
|
|
/// Creates extracts for values in \p Plan defined in a loop region and used
|
|
/// outside a loop region.
|
|
static void createExtractsForLiveOuts(VPlan &Plan, VPBasicBlock *MiddleVPBB) {
|
|
VPBuilder B(MiddleVPBB, MiddleVPBB->getFirstNonPhi());
|
|
for (VPBasicBlock *EB : Plan.getExitBlocks()) {
|
|
if (!is_contained(EB->predecessors(), MiddleVPBB))
|
|
continue;
|
|
|
|
for (VPRecipeBase &R : EB->phis()) {
|
|
auto *ExitIRI = cast<VPIRPhi>(&R);
|
|
VPValue *Exiting = ExitIRI->getIncomingValueForBlock(MiddleVPBB);
|
|
if (isa<VPIRValue>(Exiting))
|
|
continue;
|
|
Exiting = B.createNaryOp(VPInstruction::ExtractLastPart, Exiting);
|
|
Exiting = B.createNaryOp(VPInstruction::ExtractLastLane, Exiting);
|
|
ExitIRI->setIncomingValueForBlock(MiddleVPBB, Exiting);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void addInitialSkeleton(VPlan &Plan, Type *InductionTy,
|
|
PredicatedScalarEvolution &PSE, Loop *TheLoop) {
|
|
VPDominatorTree VPDT(Plan);
|
|
|
|
auto *HeaderVPBB = cast<VPBasicBlock>(Plan.getEntry()->getSingleSuccessor());
|
|
canonicalHeaderAndLatch(HeaderVPBB, VPDT);
|
|
auto *LatchVPBB = cast<VPBasicBlock>(HeaderVPBB->getPredecessors()[1]);
|
|
|
|
VPBasicBlock *VecPreheader = Plan.createVPBasicBlock("vector.ph");
|
|
VPBlockUtils::insertBlockAfter(VecPreheader, Plan.getEntry());
|
|
|
|
VPBasicBlock *MiddleVPBB = Plan.createVPBasicBlock("middle.block");
|
|
// The canonical LatchVPBB has the header block as last successor. If it has
|
|
// another successor, this successor is an exit block - insert middle block on
|
|
// its edge. Otherwise, add middle block as another successor retaining header
|
|
// as last. In the latter case, the latch has no conditional terminator yet,
|
|
// so insert a placeholder BranchOnCond that always continues to the header.
|
|
// It will be canonicalized to a BranchOnCount later
|
|
if (LatchVPBB->getNumSuccessors() == 2) {
|
|
VPBlockBase *LatchExitVPB = LatchVPBB->getSuccessors()[0];
|
|
VPBlockUtils::insertOnEdge(LatchVPBB, LatchExitVPB, MiddleVPBB);
|
|
} else {
|
|
VPBlockUtils::connectBlocks(LatchVPBB, MiddleVPBB);
|
|
LatchVPBB->swapSuccessors();
|
|
VPBuilder(LatchVPBB).createNaryOp(VPInstruction::BranchOnCond,
|
|
{Plan.getFalse()});
|
|
}
|
|
|
|
// Create SCEV and VPValue for the trip count.
|
|
// We use the symbolic max backedge-taken-count, which works also when
|
|
// vectorizing loops with uncountable early exits.
|
|
const SCEV *BackedgeTakenCountSCEV = PSE.getSymbolicMaxBackedgeTakenCount();
|
|
assert(!isa<SCEVCouldNotCompute>(BackedgeTakenCountSCEV) &&
|
|
"Invalid backedge-taken count");
|
|
ScalarEvolution &SE = *PSE.getSE();
|
|
const SCEV *TripCount = SE.getTripCountFromExitCount(BackedgeTakenCountSCEV,
|
|
InductionTy, TheLoop);
|
|
Plan.setTripCount(vputils::getOrCreateVPValueForSCEVExpr(Plan, TripCount));
|
|
|
|
VPBasicBlock *ScalarPH = Plan.createVPBasicBlock("scalar.ph");
|
|
VPBlockUtils::connectBlocks(ScalarPH, Plan.getScalarHeader());
|
|
|
|
// The connection order corresponds to the operands of the conditional branch,
|
|
// with the middle block already connected to the exit block.
|
|
VPBlockUtils::connectBlocks(MiddleVPBB, ScalarPH);
|
|
// Also connect the entry block to the scalar preheader.
|
|
// TODO: Also introduce a branch recipe together with the minimum trip count
|
|
// check.
|
|
VPBlockUtils::connectBlocks(Plan.getEntry(), ScalarPH);
|
|
Plan.getEntry()->swapSuccessors();
|
|
|
|
createExtractsForLiveOuts(Plan, MiddleVPBB);
|
|
|
|
// Create resume phis in the scalar preheader for each phi in the scalar loop.
|
|
// Their incoming value from the vector loop will be the last lane of the
|
|
// corresponding vector loop header phi.
|
|
VPBuilder MiddleBuilder(MiddleVPBB, MiddleVPBB->getFirstNonPhi());
|
|
VPBuilder ScalarPHBuilder(ScalarPH);
|
|
assert(equal(ScalarPH->getPredecessors(),
|
|
ArrayRef<VPBlockBase *>({MiddleVPBB, Plan.getEntry()})) &&
|
|
"unexpected predecessor order of scalar ph");
|
|
for (const auto &[PhiR, ScalarPhiR] :
|
|
zip_equal(HeaderVPBB->phis(), Plan.getScalarHeader()->phis())) {
|
|
auto *VectorPhiR = cast<VPPhi>(&PhiR);
|
|
VPValue *BackedgeVal = VectorPhiR->getOperand(1);
|
|
VPValue *ResumeFromVectorLoop =
|
|
MiddleBuilder.createNaryOp(VPInstruction::ExtractLastPart, BackedgeVal);
|
|
ResumeFromVectorLoop = MiddleBuilder.createNaryOp(
|
|
VPInstruction::ExtractLastLane, ResumeFromVectorLoop);
|
|
// Create scalar resume phi, with the first operand being the incoming value
|
|
// from the middle block and the second operand coming from the entry block.
|
|
auto *ResumePhiR = ScalarPHBuilder.createScalarPhi(
|
|
{ResumeFromVectorLoop, VectorPhiR->getOperand(0)},
|
|
VectorPhiR->getDebugLoc());
|
|
cast<VPIRPhi>(&ScalarPhiR)->addIncoming(ResumePhiR);
|
|
}
|
|
}
|
|
|
|
/// Check \p Plan's live-in and replace them with constants, if they can be
|
|
/// simplified via SCEV.
|
|
static void simplifyLiveInsWithSCEV(VPlan &Plan,
|
|
PredicatedScalarEvolution &PSE) {
|
|
auto GetSimplifiedLiveInViaSCEV = [&](VPValue *VPV) -> VPValue * {
|
|
const SCEV *Expr = vputils::getSCEVExprForVPValue(VPV, PSE);
|
|
if (auto *C = dyn_cast<SCEVConstant>(Expr))
|
|
return Plan.getOrAddLiveIn(C->getValue());
|
|
return nullptr;
|
|
};
|
|
|
|
for (VPValue *LiveIn : to_vector(Plan.getLiveIns())) {
|
|
if (VPValue *SimplifiedLiveIn = GetSimplifiedLiveInViaSCEV(LiveIn))
|
|
LiveIn->replaceAllUsesWith(SimplifiedLiveIn);
|
|
}
|
|
}
|
|
|
|
/// To make RUN_VPLAN_PASS print initial VPlan.
|
|
static void printAfterInitialConstruction(VPlan &) {}
|
|
|
|
std::unique_ptr<VPlan>
|
|
VPlanTransforms::buildVPlan0(Loop *TheLoop, LoopInfo &LI, Type *InductionTy,
|
|
PredicatedScalarEvolution &PSE,
|
|
LoopVersioning *LVer) {
|
|
PlainCFGBuilder Builder(TheLoop, &LI, LVer, InductionTy);
|
|
std::unique_ptr<VPlan> VPlan0 = Builder.buildPlainCFG();
|
|
addInitialSkeleton(*VPlan0, InductionTy, PSE, TheLoop);
|
|
simplifyLiveInsWithSCEV(*VPlan0, PSE);
|
|
|
|
RUN_VPLAN_PASS_NO_VERIFY(printAfterInitialConstruction, *VPlan0);
|
|
return VPlan0;
|
|
}
|
|
|
|
/// Creates a VPWidenIntOrFpInductionRecipe or VPWidenPointerInductionRecipe
|
|
/// for \p Phi based on \p IndDesc.
|
|
static VPHeaderPHIRecipe *
|
|
createWidenInductionRecipe(PHINode *Phi, VPPhi *PhiR, VPIRValue *Start,
|
|
const InductionDescriptor &IndDesc, VPlan &Plan,
|
|
PredicatedScalarEvolution &PSE, Loop &OrigLoop,
|
|
DebugLoc DL) {
|
|
[[maybe_unused]] ScalarEvolution &SE = *PSE.getSE();
|
|
assert(SE.isLoopInvariant(IndDesc.getStep(), &OrigLoop) &&
|
|
"step must be loop invariant");
|
|
assert((Plan.getLiveIn(IndDesc.getStartValue()) == Start ||
|
|
(SE.isSCEVable(IndDesc.getStartValue()->getType()) &&
|
|
PSE.getSCEV(IndDesc.getStartValue()) ==
|
|
vputils::getSCEVExprForVPValue(Start, PSE))) &&
|
|
"Start VPValue must match IndDesc's start value");
|
|
|
|
VPValue *Step =
|
|
vputils::getOrCreateVPValueForSCEVExpr(Plan, IndDesc.getStep());
|
|
|
|
VPValue *BackedgeVal = PhiR->getOperand(1);
|
|
// Replace live-out extracts of WideIV's backedge value by ExitingIVValue
|
|
// recipes. optimizeInductionLiveOutUsers will later compute the proper
|
|
// DerivedIV.
|
|
//
|
|
// For an IV that requires SCEV predicate, keep extracting the exit values
|
|
// from the loop directly, as the pre-computed exit value as-is would be
|
|
// incorrect outside the loop.
|
|
auto ReplaceExtractsWithExitingIVValueIfPossible = [&](VPWidenInductionRecipe
|
|
*WideIV) {
|
|
bool IsPredicated = !WideIV->getNoWrapPredicates().empty();
|
|
for (VPUser *U : to_vector(BackedgeVal->users())) {
|
|
if (!match(U, m_ExtractLastPart(m_VPValue())))
|
|
continue;
|
|
auto *ExtractLastPart = cast<VPInstruction>(U);
|
|
VPUser *ExtractLastPartUser = ExtractLastPart->getSingleUser();
|
|
assert(ExtractLastPartUser && "must have a single user");
|
|
if (!match(ExtractLastPartUser, m_ExtractLastLane(m_VPValue())))
|
|
continue;
|
|
auto *ExtractLastLane = cast<VPInstruction>(ExtractLastPartUser);
|
|
assert(is_contained(ExtractLastLane->getParent()->successors(),
|
|
Plan.getScalarPreheader()) &&
|
|
"last lane must be extracted in the middle block");
|
|
// Keep the vector extract for exit-block live-out uses of a predicated
|
|
// IV.
|
|
if (IsPredicated &&
|
|
any_of(ExtractLastLane->users(), [&](VPUser *LaneUser) {
|
|
auto *R = cast<VPRecipeBase>(LaneUser);
|
|
return Plan.isExitBlock(R->getParent());
|
|
}))
|
|
continue;
|
|
VPBuilder Builder(ExtractLastLane);
|
|
ExtractLastLane->replaceAllUsesWith(
|
|
Builder.createNaryOp(VPInstruction::ExitingIVValue, {WideIV}));
|
|
ExtractLastLane->eraseFromParent();
|
|
ExtractLastPart->eraseFromParent();
|
|
}
|
|
};
|
|
|
|
if (IndDesc.getKind() == InductionDescriptor::IK_PtrInduction) {
|
|
auto *WideIV = new VPWidenPointerInductionRecipe(
|
|
Phi, Start, Step, &Plan.getVFxUF(), IndDesc, DL);
|
|
ReplaceExtractsWithExitingIVValueIfPossible(WideIV);
|
|
return WideIV;
|
|
}
|
|
|
|
assert((IndDesc.getKind() == InductionDescriptor::IK_IntInduction ||
|
|
IndDesc.getKind() == InductionDescriptor::IK_FpInduction) &&
|
|
"must have an integer or float induction at this point");
|
|
|
|
// Update wide induction increments to use the same step as the corresponding
|
|
// wide induction. This enables detecting induction increments directly in
|
|
// VPlan and removes redundant splats.
|
|
if (match(BackedgeVal, m_Add(m_Specific(PhiR), m_VPValue())))
|
|
BackedgeVal->getDefiningRecipe()->setOperand(1, Step);
|
|
|
|
// It is always safe to copy over the NoWrap and FastMath flags. In
|
|
// particular, when folding tail by masking, the masked-off lanes are never
|
|
// used, so it is safe.
|
|
VPIRFlags Flags = vputils::getFlagsFromIndDesc(IndDesc);
|
|
|
|
auto *WideIV = new VPWidenIntOrFpInductionRecipe(
|
|
Phi, Start, Step, &Plan.getVF(), IndDesc, Flags, DL);
|
|
|
|
ReplaceExtractsWithExitingIVValueIfPossible(WideIV);
|
|
return WideIV;
|
|
}
|
|
|
|
/// Try to sink users of \p FOR after \p Previous. \returns true if sinking
|
|
/// succeeded or was not necessary, and false otherwise.
|
|
static bool
|
|
sinkRecurrenceUsersAfterPrevious(VPFirstOrderRecurrencePHIRecipe *FOR,
|
|
VPRecipeBase *Previous,
|
|
const VPDominatorTree &VPDT) {
|
|
// Collect recipes that need sinking.
|
|
SmallVector<VPRecipeBase *> WorkList;
|
|
SmallPtrSet<VPRecipeBase *, 8> Seen;
|
|
Seen.insert(Previous);
|
|
auto TryToPushSinkCandidate = [&](VPRecipeBase *SinkCandidate) {
|
|
// The previous value must not depend on the users of the recurrence phi.
|
|
// In that case, FOR is not a fixed order recurrence.
|
|
if (SinkCandidate == Previous)
|
|
return false;
|
|
|
|
if (isa<VPHeaderPHIRecipe>(SinkCandidate) ||
|
|
!Seen.insert(SinkCandidate).second ||
|
|
VPDT.properlyDominates(Previous, SinkCandidate))
|
|
return true;
|
|
|
|
if (vputils::cannotHoistOrSinkRecipe(*SinkCandidate, /*Sinking=*/true))
|
|
return false;
|
|
|
|
WorkList.push_back(SinkCandidate);
|
|
return true;
|
|
};
|
|
|
|
// Recursively sink users of FOR after Previous.
|
|
WorkList.push_back(FOR);
|
|
for (unsigned I = 0; I != WorkList.size(); ++I) {
|
|
VPRecipeBase *Current = WorkList[I];
|
|
assert(Current->getNumDefinedValues() == 1 &&
|
|
"only recipes with a single defined value expected");
|
|
|
|
for (VPUser *User : Current->getVPSingleValue()->users()) {
|
|
if (!TryToPushSinkCandidate(cast<VPRecipeBase>(User)))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Keep recipes to sink ordered by dominance so earlier instructions are
|
|
// processed first.
|
|
sort(WorkList, [&VPDT](const VPRecipeBase *A, const VPRecipeBase *B) {
|
|
return VPDT.properlyDominates(A, B);
|
|
});
|
|
|
|
for (VPRecipeBase *SinkCandidate : WorkList) {
|
|
if (SinkCandidate == FOR)
|
|
continue;
|
|
|
|
SinkCandidate->moveAfter(Previous);
|
|
Previous = SinkCandidate;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// Try to hoist \p Previous and its operands before all users of \p FOR.
|
|
/// \returns true if hoisting succeeded or was not necessary, and false
|
|
/// otherwise.
|
|
static bool hoistPreviousBeforeFORUsers(VPFirstOrderRecurrencePHIRecipe *FOR,
|
|
VPRecipeBase *Previous,
|
|
const VPDominatorTree &VPDT) {
|
|
if (vputils::cannotHoistOrSinkRecipe(*Previous))
|
|
return false;
|
|
|
|
// Collect recipes that need hoisting.
|
|
SmallVector<VPRecipeBase *> HoistCandidates;
|
|
SmallPtrSet<VPRecipeBase *, 8> Visited;
|
|
// Find the closest hoist point by looking at all users of FOR and selecting
|
|
// the recipe dominating all other users.
|
|
VPRecipeBase *HoistPoint = nullptr;
|
|
for (VPUser *U : FOR->users()) {
|
|
auto *R = cast<VPRecipeBase>(U);
|
|
if (!HoistPoint || VPDT.properlyDominates(R, HoistPoint))
|
|
HoistPoint = R;
|
|
}
|
|
assert(all_of(FOR->users(),
|
|
[&VPDT, HoistPoint](VPUser *U) {
|
|
auto *R = cast<VPRecipeBase>(U);
|
|
return HoistPoint == R ||
|
|
VPDT.properlyDominates(HoistPoint, R);
|
|
}) &&
|
|
"HoistPoint must dominate all users of FOR");
|
|
|
|
auto NeedsHoisting = [HoistPoint, &VPDT,
|
|
&Visited](VPValue *HoistCandidateV) -> VPRecipeBase * {
|
|
VPRecipeBase *HoistCandidate = HoistCandidateV->getDefiningRecipe();
|
|
if (!HoistCandidate)
|
|
return nullptr;
|
|
// Hoist candidate was already visited, no need to hoist.
|
|
if (!Visited.insert(HoistCandidate).second)
|
|
return nullptr;
|
|
// If we reached a recipe that dominates HoistPoint, we don't need to
|
|
// hoist the recipe.
|
|
if (VPDT.properlyDominates(HoistCandidate, HoistPoint))
|
|
return nullptr;
|
|
return HoistCandidate;
|
|
};
|
|
|
|
if (!NeedsHoisting(Previous->getVPSingleValue()))
|
|
return true;
|
|
|
|
// Recursively try to hoist Previous and its operands before all users of
|
|
// FOR.
|
|
HoistCandidates.push_back(Previous);
|
|
|
|
for (unsigned I = 0; I != HoistCandidates.size(); ++I) {
|
|
VPRecipeBase *Current = HoistCandidates[I];
|
|
assert(Current->getNumDefinedValues() == 1 &&
|
|
"only recipes with a single defined value expected");
|
|
if (vputils::cannotHoistOrSinkRecipe(*Current))
|
|
return false;
|
|
|
|
for (VPValue *Op : Current->operands()) {
|
|
// If we reach FOR, it means the original Previous depends on some other
|
|
// recurrence that in turn depends on FOR. If that is the case, we would
|
|
// also need to hoist recipes involving the other FOR, which may break
|
|
// dependencies.
|
|
if (Op == FOR)
|
|
return false;
|
|
|
|
if (auto *R = NeedsHoisting(Op)) {
|
|
// Bail out if the recipe defines multiple values.
|
|
// TODO: Hoisting such recipes requires additional handling.
|
|
if (R->getNumDefinedValues() != 1)
|
|
return false;
|
|
HoistCandidates.push_back(R);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Order recipes to hoist by dominance so earlier instructions are processed
|
|
// first.
|
|
sort(HoistCandidates, [&VPDT](const VPRecipeBase *A, const VPRecipeBase *B) {
|
|
return VPDT.properlyDominates(A, B);
|
|
});
|
|
|
|
for (VPRecipeBase *HoistCandidate : HoistCandidates) {
|
|
HoistCandidate->moveBefore(*HoistPoint->getParent(),
|
|
HoistPoint->getIterator());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Sink users of fixed-order recurrences past or hoist before the recipe
|
|
/// defining the previous value, introduce FirstOrderRecurrenceSplice
|
|
/// VPInstructions, and replace FOR uses. Returns false if hoisting or sinking
|
|
/// fails.
|
|
static bool tryToSinkOrHoistRecurrenceUsers(VPBasicBlock *HeaderVPBB,
|
|
const VPDominatorTree &VPDT) {
|
|
auto FORs =
|
|
map_to_vector(make_filter_range(HeaderVPBB->phis(),
|
|
IsaPred<VPFirstOrderRecurrencePHIRecipe>),
|
|
[](VPRecipeBase &R) {
|
|
return cast<VPFirstOrderRecurrencePHIRecipe>(&R);
|
|
});
|
|
for (VPFirstOrderRecurrencePHIRecipe *FOR : FORs) {
|
|
// Follow through FOR phi chains to find the actual Previous recipe.
|
|
// Fixed-order recurrences do not contain cycles, so this loop is
|
|
// guaranteed to terminate.
|
|
SmallPtrSet<VPFirstOrderRecurrencePHIRecipe *, 4> SeenPhis;
|
|
VPRecipeBase *Previous = FOR->getBackedgeValue()->getDefiningRecipe();
|
|
while (auto *PrevPhi =
|
|
dyn_cast_or_null<VPFirstOrderRecurrencePHIRecipe>(Previous)) {
|
|
assert(PrevPhi->getParent() == FOR->getParent() &&
|
|
"PrevPhi must be in same block as FOR");
|
|
assert(SeenPhis.insert(PrevPhi).second &&
|
|
"PrevPhi must not be visited multiple times");
|
|
Previous = PrevPhi->getBackedgeValue()->getDefiningRecipe();
|
|
}
|
|
|
|
VPBasicBlock *InsertBlock = FOR->getParent();
|
|
VPBasicBlock::iterator InsertPt = InsertBlock->getFirstNonPhi();
|
|
if (Previous) {
|
|
// Sink FOR users after Previous or hoist Previous before FOR users.
|
|
if (!sinkRecurrenceUsersAfterPrevious(FOR, Previous, VPDT) &&
|
|
!hoistPreviousBeforeFORUsers(FOR, Previous, VPDT))
|
|
return false;
|
|
InsertBlock = Previous->getParent();
|
|
InsertPt = isa<VPHeaderPHIRecipe>(Previous)
|
|
? InsertBlock->getFirstNonPhi()
|
|
: std::next(Previous->getIterator());
|
|
}
|
|
|
|
// Create FirstOrderRecurrenceSplice and replace FOR uses.
|
|
VPBuilder LoopBuilder(InsertBlock, InsertPt);
|
|
auto *RecurSplice =
|
|
LoopBuilder.createNaryOp(VPInstruction::FirstOrderRecurrenceSplice,
|
|
{FOR, FOR->getBackedgeValue()});
|
|
FOR->replaceUsesWithIf(RecurSplice, [RecurSplice](VPUser &U, unsigned) {
|
|
return &U != RecurSplice;
|
|
});
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool VPlanTransforms::createHeaderPhiRecipes(
|
|
VPlan &Plan, PredicatedScalarEvolution &PSE, Loop &OrigLoop,
|
|
const VPDominatorTree &VPDT,
|
|
const MapVector<PHINode *, InductionDescriptor> &Inductions,
|
|
const MapVector<PHINode *, RecurrenceDescriptor> &Reductions,
|
|
const SmallPtrSetImpl<const PHINode *> &FixedOrderRecurrences,
|
|
const SmallPtrSetImpl<PHINode *> &InLoopReductions, bool AllowReordering) {
|
|
// Retrieve the header manually from the intial plain-CFG VPlan.
|
|
auto [HeaderVPBB, LatchVPBB] = VPBlockUtils::getPlainCFGHeaderAndLatch(Plan);
|
|
assert(VPDT.dominates(HeaderVPBB, LatchVPBB) &&
|
|
"header must dominate its latch");
|
|
|
|
auto CreateHeaderPhiRecipe = [&](VPPhi *PhiR) -> VPHeaderPHIRecipe * {
|
|
// TODO: Gradually replace uses of underlying instruction by analyses on
|
|
// VPlan.
|
|
auto *Phi = cast<PHINode>(PhiR->getUnderlyingInstr());
|
|
assert(PhiR->getNumOperands() == 2 &&
|
|
"Must have 2 operands for header phis");
|
|
|
|
// Extract common values once.
|
|
VPIRValue *Start = cast<VPIRValue>(PhiR->getOperand(0));
|
|
VPValue *BackedgeValue = PhiR->getOperand(1);
|
|
|
|
if (FixedOrderRecurrences.contains(Phi)) {
|
|
// TODO: Currently fixed-order recurrences are modeled as chains of
|
|
// first-order recurrences. If there are no users of the intermediate
|
|
// recurrences in the chain, the fixed order recurrence should be
|
|
// modeled directly, enabling more efficient codegen.
|
|
return new VPFirstOrderRecurrencePHIRecipe(Phi, *Start, *BackedgeValue);
|
|
}
|
|
|
|
auto InductionIt = Inductions.find(Phi);
|
|
if (InductionIt != Inductions.end())
|
|
return createWidenInductionRecipe(Phi, PhiR, Start, InductionIt->second,
|
|
Plan, PSE, OrigLoop,
|
|
PhiR->getDebugLoc());
|
|
|
|
assert(Reductions.contains(Phi) && "only reductions are expected now");
|
|
const RecurrenceDescriptor &RdxDesc = Reductions.lookup(Phi);
|
|
assert(RdxDesc.getRecurrenceStartValue() ==
|
|
Phi->getIncomingValueForBlock(OrigLoop.getLoopPreheader()) &&
|
|
"incoming value must match start value");
|
|
// Will be updated later to >1 if reduction is partial.
|
|
unsigned ScaleFactor = 1;
|
|
bool UseOrderedReductions = !AllowReordering && RdxDesc.isOrdered();
|
|
return new VPReductionPHIRecipe(
|
|
Phi, RdxDesc.getRecurrenceKind(), *Start, *BackedgeValue,
|
|
getReductionStyle(InLoopReductions.contains(Phi), UseOrderedReductions,
|
|
ScaleFactor),
|
|
Phi->getType()->isFloatingPointTy() ? RdxDesc.getFastMathFlags()
|
|
: VPIRFlags(),
|
|
RdxDesc.hasUsesOutsideReductionChain());
|
|
};
|
|
|
|
for (VPRecipeBase &R : make_early_inc_range(HeaderVPBB->phis())) {
|
|
auto *PhiR = cast<VPPhi>(&R);
|
|
VPHeaderPHIRecipe *HeaderPhiR = CreateHeaderPhiRecipe(PhiR);
|
|
HeaderPhiR->insertBefore(PhiR);
|
|
PhiR->replaceAllUsesWith(HeaderPhiR);
|
|
PhiR->eraseFromParent();
|
|
}
|
|
|
|
if (!tryToSinkOrHoistRecurrenceUsers(HeaderVPBB, VPDT))
|
|
return false;
|
|
|
|
// Skip renaming resume phi recipes, if any header phi has been removed.
|
|
if (range_size(HeaderVPBB->phis()) !=
|
|
range_size(Plan.getScalarPreheader()->phis()))
|
|
return true;
|
|
for (const auto &[HeaderPhiR, ScalarPhiR] :
|
|
zip_equal(HeaderVPBB->phis(), Plan.getScalarPreheader()->phis())) {
|
|
auto *ResumePhiR = cast<VPPhi>(&ScalarPhiR);
|
|
if (isa<VPFirstOrderRecurrencePHIRecipe>(&HeaderPhiR)) {
|
|
ResumePhiR->setName("scalar.recur.init");
|
|
auto *ExtractLastLane = cast<VPInstruction>(ResumePhiR->getOperand(0));
|
|
ExtractLastLane->setName("vector.recur.extract");
|
|
continue;
|
|
}
|
|
ResumePhiR->setName(isa<VPWidenInductionRecipe>(HeaderPhiR)
|
|
? "bc.resume.val"
|
|
: "bc.merge.rdx");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VPlanTransforms::finalizeSCEVPredicates(VPlan &Plan,
|
|
PredicatedScalarEvolution &PSE,
|
|
bool OptForSize,
|
|
unsigned SCEVCheckThreshold,
|
|
OptimizationRemarkEmitter *ORE,
|
|
Loop *TheLoop) {
|
|
// Collect which wide IVs have predicates and add them to PSE.
|
|
auto [HeaderVPBB, _] = VPBlockUtils::getPlainCFGHeaderAndLatch(Plan);
|
|
SmallPtrSet<VPWidenInductionRecipe *, 4> PredicatedIVs;
|
|
for (auto &R : HeaderVPBB->phis()) {
|
|
auto *WideIV = dyn_cast<VPWidenInductionRecipe>(&R);
|
|
if (!WideIV || WideIV->getNoWrapPredicates().empty())
|
|
continue;
|
|
PredicatedIVs.insert(WideIV);
|
|
for (const auto *P : WideIV->getNoWrapPredicates())
|
|
PSE.addPredicate(*P);
|
|
}
|
|
|
|
unsigned TotalComplexity = PSE.getPredicate().getComplexity();
|
|
if (TotalComplexity && OptForSize) {
|
|
LLVM_DEBUG(
|
|
dbgs() << "LV: Not vectorizing: SCEV predicates needed for induction "
|
|
"but optimizing for size\n");
|
|
reportVectorizationFailure(
|
|
"Runtime SCEV check is required with -Os/-Oz",
|
|
"runtime SCEV checks needed but optimizing for size",
|
|
"CantVersionLoopWithOptForSize", ORE, TheLoop);
|
|
return false;
|
|
}
|
|
|
|
if (TotalComplexity > SCEVCheckThreshold) {
|
|
LLVM_DEBUG(dbgs() << "LV: Not vectorizing: Too many SCEV checks needed ("
|
|
<< TotalComplexity << " > " << SCEVCheckThreshold
|
|
<< ")\n");
|
|
reportVectorizationFailure(
|
|
"Too many SCEV checks needed",
|
|
"Too many SCEV assumptions need to be made and checked at runtime",
|
|
"TooManySCEVRunTimeChecks", ORE, TheLoop);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VPlanTransforms::createInLoopReductionRecipes(VPlan &Plan,
|
|
ElementCount MinVF) {
|
|
VPBasicBlock *Header = Plan.getVectorLoopRegion()->getEntryBasicBlock();
|
|
SmallVector<VPRecipeBase *> ToDelete;
|
|
|
|
for (VPRecipeBase &R : Header->phis()) {
|
|
auto *PhiR = dyn_cast<VPReductionPHIRecipe>(&R);
|
|
if (!PhiR || !PhiR->isInLoop() || (MinVF.isScalar() && !PhiR->isOrdered()))
|
|
continue;
|
|
|
|
RecurKind Kind = PhiR->getRecurrenceKind();
|
|
assert(!RecurrenceDescriptor::isFindLastRecurrenceKind(Kind) &&
|
|
!RecurrenceDescriptor::isAnyOfRecurrenceKind(Kind) &&
|
|
!RecurrenceDescriptor::isFindIVRecurrenceKind(Kind) &&
|
|
"AnyOf and Find reductions are not allowed for in-loop reductions");
|
|
|
|
bool IsFPRecurrence =
|
|
RecurrenceDescriptor::isFloatingPointRecurrenceKind(Kind);
|
|
FastMathFlags FMFs =
|
|
IsFPRecurrence ? FastMathFlags::getFast() : FastMathFlags();
|
|
|
|
// Collect the chain of "link" recipes for the reduction starting at PhiR.
|
|
SetVector<VPSingleDefRecipe *> Worklist;
|
|
Worklist.insert(PhiR);
|
|
for (unsigned I = 0; I != Worklist.size(); ++I) {
|
|
VPSingleDefRecipe *Cur = Worklist[I];
|
|
for (VPUser *U : Cur->users()) {
|
|
auto *UserRecipe = cast<VPSingleDefRecipe>(U);
|
|
if (!UserRecipe->getParent()->getEnclosingLoopRegion()) {
|
|
assert((UserRecipe->getParent() == Plan.getMiddleBlock() ||
|
|
UserRecipe->getParent() == Plan.getScalarPreheader()) &&
|
|
"U must be either in the loop region, the middle block or the "
|
|
"scalar preheader.");
|
|
continue;
|
|
}
|
|
|
|
// Stores using instructions will be sunk later.
|
|
if (match(UserRecipe, m_VPInstruction<Instruction::Store>()))
|
|
continue;
|
|
Worklist.insert(UserRecipe);
|
|
}
|
|
}
|
|
|
|
// Visit operation "Links" along the reduction chain top-down starting from
|
|
// the phi until LoopExitValue. We keep track of the previous item
|
|
// (PreviousLink) to tell which of the two operands of a Link will remain
|
|
// scalar and which will be reduced. For minmax by select(cmp), Link will be
|
|
// the select instructions. Blend recipes of in-loop reduction phi's will
|
|
// get folded to their non-phi operand, as the reduction recipe handles the
|
|
// condition directly.
|
|
VPSingleDefRecipe *PreviousLink = PhiR; // Aka Worklist[0].
|
|
for (VPSingleDefRecipe *CurrentLink : drop_begin(Worklist)) {
|
|
if (auto *Blend = dyn_cast<VPBlendRecipe>(CurrentLink)) {
|
|
assert(Blend->getNumIncomingValues() == 2 &&
|
|
"Blend must have 2 incoming values");
|
|
unsigned PhiRIdx = Blend->getIncomingValue(0) == PhiR ? 0 : 1;
|
|
assert(Blend->getIncomingValue(PhiRIdx) == PhiR &&
|
|
"PhiR must be an operand of the blend");
|
|
Blend->replaceAllUsesWith(Blend->getIncomingValue(1 - PhiRIdx));
|
|
continue;
|
|
}
|
|
|
|
if (IsFPRecurrence) {
|
|
FastMathFlags CurFMF =
|
|
cast<VPRecipeWithIRFlags>(CurrentLink)->getFastMathFlagsOrNone();
|
|
if (match(CurrentLink, m_Select(m_VPValue(), m_VPValue(), m_VPValue())))
|
|
CurFMF |= cast<VPRecipeWithIRFlags>(CurrentLink->getOperand(0))
|
|
->getFastMathFlagsOrNone();
|
|
FMFs &= CurFMF;
|
|
}
|
|
|
|
Instruction *CurrentLinkI = CurrentLink->getUnderlyingInstr();
|
|
|
|
// Recognize a call to the llvm.fmuladd intrinsic.
|
|
bool IsFMulAdd = Kind == RecurKind::FMulAdd;
|
|
VPValue *VecOp;
|
|
VPBasicBlock *LinkVPBB = CurrentLink->getParent();
|
|
if (IsFMulAdd) {
|
|
assert(RecurrenceDescriptor::isFMulAddIntrinsic(CurrentLinkI) &&
|
|
"Expected current VPInstruction to be a call to the "
|
|
"llvm.fmuladd intrinsic");
|
|
assert(CurrentLink->getOperand(2) == PreviousLink &&
|
|
"expected a call where the previous link is the added operand");
|
|
|
|
// If the instruction is a call to the llvm.fmuladd intrinsic then we
|
|
// need to create an fmul recipe (multiplying the first two operands of
|
|
// the fmuladd together) to use as the vector operand for the fadd
|
|
// reduction.
|
|
auto *FMulRecipe = new VPInstruction(
|
|
Instruction::FMul,
|
|
{CurrentLink->getOperand(0), CurrentLink->getOperand(1)},
|
|
CurrentLinkI->getFastMathFlags());
|
|
LinkVPBB->insert(FMulRecipe, CurrentLink->getIterator());
|
|
VecOp = FMulRecipe;
|
|
} else if (Kind == RecurKind::AddChainWithSubs &&
|
|
match(CurrentLink, m_Sub(m_VPValue(), m_VPValue()))) {
|
|
Type *PhiTy = PhiR->getScalarType();
|
|
auto *Zero = Plan.getConstantInt(PhiTy, 0);
|
|
VPBuilder Builder(LinkVPBB, CurrentLink->getIterator());
|
|
auto *Sub = Builder.createSub(Zero, CurrentLink->getOperand(1),
|
|
CurrentLinkI->getDebugLoc());
|
|
Sub->setUnderlyingValue(CurrentLinkI);
|
|
VecOp = Sub;
|
|
} else {
|
|
// Index of the first operand which holds a non-mask vector operand.
|
|
unsigned IndexOfFirstOperand = 0;
|
|
if (RecurrenceDescriptor::isMinMaxRecurrenceKind(Kind)) {
|
|
if (match(CurrentLink, m_Cmp(m_VPValue(), m_VPValue())))
|
|
continue;
|
|
assert(match(CurrentLink,
|
|
m_Select(m_VPValue(), m_VPValue(), m_VPValue())) &&
|
|
"must be a select recipe");
|
|
IndexOfFirstOperand = 1;
|
|
}
|
|
// Note that for non-commutable operands (cmp-selects), the semantics of
|
|
// the cmp-select are captured in the recurrence kind.
|
|
unsigned VecOpId =
|
|
CurrentLink->getOperand(IndexOfFirstOperand) == PreviousLink
|
|
? IndexOfFirstOperand + 1
|
|
: IndexOfFirstOperand;
|
|
VecOp = CurrentLink->getOperand(VecOpId);
|
|
assert(
|
|
VecOp != PreviousLink &&
|
|
CurrentLink->getOperand(
|
|
cast<VPInstruction>(CurrentLink)->getNumOperandsWithoutMask() -
|
|
1 - (VecOpId - IndexOfFirstOperand)) == PreviousLink &&
|
|
"PreviousLink must be the operand other than VecOp");
|
|
}
|
|
|
|
assert(PhiR->getVFScaleFactor() == 1 &&
|
|
"inloop reductions must be unscaled");
|
|
VPValue *CondOp = cast<VPInstruction>(CurrentLink)->getMask();
|
|
auto *RedRecipe = new VPReductionRecipe(
|
|
Kind, FMFs, CurrentLinkI, PreviousLink, VecOp, CondOp,
|
|
getReductionStyle(/*IsInLoop=*/true, PhiR->isOrdered(), 1),
|
|
CurrentLinkI->getDebugLoc());
|
|
// Append the recipe to the end of the VPBasicBlock because we need to
|
|
// ensure that it comes after all of it's inputs, including CondOp.
|
|
// Delete CurrentLink as it will be invalid if its operand is replaced
|
|
// with a reduction defined at the bottom of the block in the next link.
|
|
if (LinkVPBB->getNumSuccessors() == 0)
|
|
RedRecipe->insertBefore(&*std::prev(std::prev(LinkVPBB->end())));
|
|
else
|
|
LinkVPBB->appendRecipe(RedRecipe);
|
|
|
|
CurrentLink->replaceAllUsesWith(RedRecipe);
|
|
// Move any store recipes using the RedRecipe that appear before it in the
|
|
// same block to just after the RedRecipe.
|
|
for (VPUser *U : make_early_inc_range(RedRecipe->users())) {
|
|
auto *UserR = dyn_cast<VPRecipeBase>(U);
|
|
if (!UserR || UserR->getParent() != LinkVPBB)
|
|
continue;
|
|
if (!match(UserR, m_VPInstruction<Instruction::Store>()))
|
|
continue;
|
|
UserR->moveAfter(RedRecipe);
|
|
}
|
|
ToDelete.push_back(CurrentLink);
|
|
PreviousLink = RedRecipe;
|
|
}
|
|
}
|
|
|
|
for (VPRecipeBase *R : ToDelete)
|
|
R->eraseFromParent();
|
|
}
|
|
|
|
/// Check if all loads in the loop are dereferenceable. Iterates over the
|
|
/// loop body blocks reachable from \p HeaderVPBB. Returns false if any
|
|
/// non-dereferenceable load is found.
|
|
static bool areAllLoadsDereferenceable(VPBasicBlock *HeaderVPBB, Loop *TheLoop,
|
|
PredicatedScalarEvolution &PSE,
|
|
DominatorTree &DT, AssumptionCache *AC) {
|
|
ScalarEvolution &SE = *PSE.getSE();
|
|
const DataLayout &DL = TheLoop->getHeader()->getDataLayout();
|
|
for (VPBasicBlock *VPBB : vp_rpo_plain_cfg_loop_body(HeaderVPBB)) {
|
|
for (VPRecipeBase &R : *VPBB) {
|
|
auto *VPI = dyn_cast<VPInstructionWithType>(&R);
|
|
if (!VPI || VPI->getOpcode() != Instruction::Load) {
|
|
assert(!R.mayReadFromMemory() && "unexpected recipe reading memory");
|
|
continue;
|
|
}
|
|
|
|
// Get the pointer SCEV for dereferenceability checking.
|
|
VPValue *Ptr = VPI->getOperand(0);
|
|
const SCEV *PtrSCEV = vputils::getSCEVExprForVPValue(Ptr, PSE, TheLoop);
|
|
if (isa<SCEVCouldNotCompute>(PtrSCEV)) {
|
|
LLVM_DEBUG(dbgs() << "LV: Not vectorizing: Found non-dereferenceable "
|
|
"load with SCEVCouldNotCompute pointer\n");
|
|
return false;
|
|
}
|
|
|
|
// Check dereferenceability using the SCEV-based version.
|
|
Type *LoadTy = VPI->getScalarType();
|
|
const SCEV *SizeSCEV =
|
|
SE.getStoreSizeOfExpr(DL.getIndexType(PtrSCEV->getType()), LoadTy);
|
|
auto *Load = cast<LoadInst>(VPI->getUnderlyingValue());
|
|
SmallVector<const SCEVPredicate *> Preds;
|
|
if (isDereferenceableAndAlignedInLoop(PtrSCEV, Load->getAlign(), SizeSCEV,
|
|
TheLoop, SE, DT, AC, &Preds))
|
|
continue;
|
|
|
|
LLVM_DEBUG(
|
|
dbgs() << "LV: Not vectorizing: Auto-vectorization of loops with "
|
|
"potentially faulting load is not supported.\n");
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VPlanTransforms::handleEarlyExits(VPlan &Plan, UncountableExitStyle Style,
|
|
Loop *TheLoop,
|
|
PredicatedScalarEvolution &PSE,
|
|
DominatorTree &DT, AssumptionCache *AC) {
|
|
auto *MiddleVPBB = VPBlockUtils::getPlainCFGMiddleBlock(Plan);
|
|
auto [HeaderVPBB, LatchVPBB] = VPBlockUtils::getPlainCFGHeaderAndLatch(Plan);
|
|
|
|
// TODO: We would like to detect uncountable exits and stores within loops
|
|
// with such exits from the VPlan alone. Exit detection can be moved
|
|
// here from handleUncountableEarlyExits, but we need to improve
|
|
// detection of recipes which may write to memory.
|
|
if (Style != UncountableExitStyle::NoUncountableExit) {
|
|
// Dereferenceability is checked separately for uncountable exit loops with
|
|
// stores, as only the loads contributing to the exit condition need to
|
|
// be checked.
|
|
if (Style == UncountableExitStyle::ReadOnly &&
|
|
!areAllLoadsDereferenceable(HeaderVPBB, TheLoop, PSE, DT, AC))
|
|
return false;
|
|
// TODO: Check target preference for style.
|
|
return handleUncountableEarlyExits(Plan, HeaderVPBB, LatchVPBB, MiddleVPBB,
|
|
TheLoop, PSE, DT, AC, Style);
|
|
}
|
|
|
|
// Disconnect countable early exits from the loop, leaving it with a single
|
|
// exit from the latch. Countable early exits are left for a scalar epilog.
|
|
for (VPIRBasicBlock *EB : Plan.getExitBlocks()) {
|
|
for (VPBlockBase *Pred : to_vector(EB->getPredecessors())) {
|
|
if (Pred == MiddleVPBB)
|
|
continue;
|
|
|
|
// Remove phi operands for the early exiting block.
|
|
for (VPRecipeBase &R : EB->phis())
|
|
cast<VPIRPhi>(&R)->removeIncomingValueFor(Pred);
|
|
auto *EarlyExitingVPBB = cast<VPBasicBlock>(Pred);
|
|
EarlyExitingVPBB->getTerminator()->eraseFromParent();
|
|
VPBlockUtils::disconnectBlocks(Pred, EB);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void VPlanTransforms::addMiddleCheck(VPlan &Plan) {
|
|
auto *MiddleVPBB = VPBlockUtils::getPlainCFGMiddleBlock(Plan);
|
|
// If MiddleVPBB has a single successor then the original loop does not exit
|
|
// via the latch and the single successor must be the scalar preheader.
|
|
// There's no need to add a runtime check to MiddleVPBB.
|
|
if (MiddleVPBB->getNumSuccessors() == 1) {
|
|
assert(MiddleVPBB->getSingleSuccessor() == Plan.getScalarPreheader() &&
|
|
"must have ScalarPH as single successor");
|
|
return;
|
|
}
|
|
|
|
assert(MiddleVPBB->getNumSuccessors() == 2 && "must have 2 successors");
|
|
|
|
// Add a check in the middle block to see if we have completed all of the
|
|
// iterations in the first vector loop.
|
|
//
|
|
// Three cases:
|
|
// 1) If we require a scalar epilogue, the scalar ph must execute. Set the
|
|
// condition to false.
|
|
// 2) If (N - N%VF) == N, then we *don't* need to run the
|
|
// remainder. Thus if tail is to be folded, we know we don't need to run
|
|
// the remainder and we can set the condition to true.
|
|
// 3) Otherwise, construct a runtime check.
|
|
|
|
// We use the same DebugLoc as the scalar loop latch terminator instead of
|
|
// the corresponding compare because they may have ended up with different
|
|
// line numbers and we want to avoid awkward line stepping while debugging.
|
|
// E.g., if the compare has got a line number inside the loop.
|
|
auto *LatchVPBB = cast<VPBasicBlock>(MiddleVPBB->getSinglePredecessor());
|
|
DebugLoc LatchDL = LatchVPBB->getTerminator()->getDebugLoc();
|
|
VPBuilder Builder(MiddleVPBB);
|
|
VPValue *Cmp =
|
|
Builder.createICmp(CmpInst::ICMP_EQ, Plan.getTripCount(),
|
|
&Plan.getVectorTripCount(), LatchDL, "cmp.n");
|
|
Builder.createNaryOp(VPInstruction::BranchOnCond, {Cmp}, LatchDL);
|
|
}
|
|
|
|
void VPlanTransforms::createLoopRegions(VPlan &Plan, DebugLoc DL) {
|
|
VPDominatorTree VPDT(Plan);
|
|
PostOrderTraversal<VPBlockShallowTraversalWrapper<VPBlockBase *>> POT(
|
|
Plan.getEntry());
|
|
for (VPBlockBase *HeaderVPB : POT)
|
|
if (canonicalHeaderAndLatch(HeaderVPB, VPDT))
|
|
createLoopRegion(Plan, HeaderVPB, DL);
|
|
|
|
VPRegionBlock *TopRegion = Plan.getVectorLoopRegion();
|
|
TopRegion->setName("vector loop");
|
|
TopRegion->getEntryBasicBlock()->setName("vector.body");
|
|
}
|
|
|
|
void VPlanTransforms::foldTailByMasking(VPlan &Plan) {
|
|
assert(Plan.getExitBlocks().size() == 1 &&
|
|
"only a single-exit block is supported currently");
|
|
assert(Plan.getExitBlocks().front()->getSinglePredecessor() ==
|
|
Plan.getMiddleBlock() &&
|
|
"the exit block must have middle block as single predecessor");
|
|
|
|
VPRegionBlock *LoopRegion = Plan.getVectorLoopRegion();
|
|
assert(LoopRegion->getSingleSuccessor() == Plan.getMiddleBlock() &&
|
|
"The vector loop region must have the middle block as its single "
|
|
"successor for now");
|
|
VPBasicBlock *Header = LoopRegion->getEntryBasicBlock();
|
|
|
|
Header->splitAt(Header->getFirstNonPhi());
|
|
|
|
// Abstract header mask, materialized into concrete recipes later.
|
|
VPValue *HeaderMask = LoopRegion->createHeaderMask();
|
|
VPBuilder Builder(Header, Header->getFirstNonPhi());
|
|
Builder.createNaryOp(VPInstruction::BranchOnCond, HeaderMask);
|
|
|
|
VPBasicBlock *OrigLatch = LoopRegion->getExitingBasicBlock();
|
|
VPValue *IVInc;
|
|
[[maybe_unused]] bool TermBranchOnCount =
|
|
match(OrigLatch->getTerminator(),
|
|
m_BranchOnCount(m_VPValue(IVInc),
|
|
m_Specific(&Plan.getVectorTripCount())));
|
|
assert(TermBranchOnCount &&
|
|
match(IVInc, m_Add(m_Specific(LoopRegion->getCanonicalIV()),
|
|
m_Specific(&Plan.getVFxUF()))) &&
|
|
std::next(IVInc->getDefiningRecipe()->getIterator()) ==
|
|
OrigLatch->getTerminator()->getIterator() &&
|
|
"Unexpected canonical iv increment");
|
|
|
|
// Split the latch at the IV update, and branch to it from the header mask.
|
|
VPBasicBlock *Latch =
|
|
OrigLatch->splitAt(IVInc->getDefiningRecipe()->getIterator());
|
|
Latch->setName("vector.latch");
|
|
VPBlockUtils::connectBlocks(Header, Latch);
|
|
|
|
// Collect any values defined in the loop that need a phi. Currently this
|
|
// includes header phi backedges and live-outs extracted in the middle block.
|
|
// TODO: Handle early exits via Plan.getExitBlocks()
|
|
MapVector<VPValue *, SmallVector<VPUser *>> NeedsPhi;
|
|
for (VPRecipeBase &R : Header->phis())
|
|
if (!isa<VPWidenInductionRecipe>(R))
|
|
NeedsPhi[cast<VPHeaderPHIRecipe>(R).getBackedgeValue()].push_back(&R);
|
|
|
|
VPValue *V;
|
|
for (VPRecipeBase &R : *Plan.getMiddleBlock())
|
|
if (match(&R, m_ExtractLastPart(m_VPValue(V))))
|
|
NeedsPhi[V].push_back(&R);
|
|
|
|
// Insert phis for values coming past the end of the tail.
|
|
Builder.setInsertPoint(Latch, Latch->begin());
|
|
for (const auto &[V, Users] : NeedsPhi) {
|
|
if (isa<VPIRValue>(V))
|
|
continue;
|
|
VPValue *TailVal = Plan.getPoison(V->getScalarType());
|
|
VPIRFlags Flags;
|
|
assert(llvm::count_if(Users, IsaPred<VPReductionPHIRecipe>) <= 1 &&
|
|
"Value used by more than two reduction phis?");
|
|
auto *RedIt = find_if(Users, IsaPred<VPReductionPHIRecipe>);
|
|
auto *RdxPhi =
|
|
RedIt != Users.end() ? cast<VPReductionPHIRecipe>(*RedIt) : nullptr;
|
|
if (RdxPhi && !RdxPhi->isInLoop()) {
|
|
TailVal = RdxPhi;
|
|
Flags = *RdxPhi;
|
|
}
|
|
|
|
VPInstruction *Phi = Builder.createScalarPhi({V, TailVal}, {}, "", Flags);
|
|
for (VPUser *U : Users)
|
|
U->replaceUsesOfWith(V, Phi);
|
|
}
|
|
|
|
// Any extract of the last element must be updated to extract from the last
|
|
// active lane of the header mask instead (i.e., the lane corresponding to the
|
|
// last active iteration).
|
|
Builder.setInsertPoint(Plan.getMiddleBlock()->getTerminator());
|
|
for (VPRecipeBase &R : *Plan.getMiddleBlock()) {
|
|
VPValue *Op;
|
|
if (!match(&R, m_ExtractLastLaneOfLastPart(m_VPValue(Op))))
|
|
continue;
|
|
|
|
// Compute the index of the last active lane.
|
|
VPValue *LastActiveLane = Builder.createLastActiveLane(HeaderMask);
|
|
auto *Ext =
|
|
Builder.createNaryOp(VPInstruction::ExtractLane, {LastActiveLane, Op});
|
|
R.getVPSingleValue()->replaceAllUsesWith(Ext);
|
|
}
|
|
|
|
// VectorTripCount now equals TripCount so simplify the MiddleVPBB branch.
|
|
assert(match(Plan.getMiddleBlock()->getTerminator(),
|
|
m_BranchOnCond(m_SpecificICmp(
|
|
CmpInst::ICMP_EQ, m_Specific(Plan.getTripCount()),
|
|
m_Specific(&Plan.getVectorTripCount())))) &&
|
|
"Unexpected MiddleVPBB branch");
|
|
Plan.getMiddleBlock()->getTerminator()->setOperand(0, Plan.getTrue());
|
|
}
|
|
|
|
/// Insert \p CheckBlockVPBB on the edge leading to the vector preheader,
|
|
/// connecting it to both vector and scalar preheaders. Updates scalar
|
|
/// preheader phis to account for the new predecessor.
|
|
static void insertCheckBlockBeforeVectorLoop(VPlan &Plan,
|
|
VPBasicBlock *CheckBlockVPBB) {
|
|
VPBlockBase *VectorPH = Plan.getVectorPreheader();
|
|
auto *ScalarPH = cast<VPBasicBlock>(Plan.getScalarPreheader());
|
|
VPBlockBase *PreVectorPH = VectorPH->getSinglePredecessor();
|
|
VPBlockUtils::insertOnEdge(PreVectorPH, VectorPH, CheckBlockVPBB);
|
|
VPBlockUtils::connectBlocks(CheckBlockVPBB, ScalarPH);
|
|
CheckBlockVPBB->swapSuccessors();
|
|
unsigned NumPreds = ScalarPH->getNumPredecessors();
|
|
for (VPRecipeBase &R : ScalarPH->phis()) {
|
|
auto *Phi = cast<VPPhi>(&R);
|
|
assert(Phi->getNumIncoming() == NumPreds - 1 &&
|
|
"must have incoming values for all predecessors");
|
|
Phi->addIncoming(Phi->getOperand(NumPreds - 2));
|
|
}
|
|
}
|
|
|
|
// Likelyhood of bypassing the vectorized loop due to a runtime check block,
|
|
// including memory overlap checks block and wrapping/unit-stride checks block.
|
|
static constexpr uint32_t CheckBypassWeights[] = {1, 127};
|
|
|
|
/// Create a BranchOnCond terminator in \p CheckBlockVPBB. Optionally adds
|
|
/// branch weights.
|
|
static void addBypassBranch(VPlan &Plan, VPBasicBlock *CheckBlockVPBB,
|
|
VPValue *Cond, bool AddBranchWeights) {
|
|
DebugLoc DL = Plan.getVectorLoopRegion()->getCanonicalIV()->getDebugLoc();
|
|
auto *Term = VPBuilder(CheckBlockVPBB)
|
|
.createNaryOp(VPInstruction::BranchOnCond, {Cond}, DL);
|
|
if (AddBranchWeights) {
|
|
MDBuilder MDB(Plan.getContext());
|
|
MDNode *BranchWeights =
|
|
MDB.createBranchWeights(CheckBypassWeights, /*IsExpected=*/false);
|
|
Term->setMetadata(LLVMContext::MD_prof, BranchWeights);
|
|
}
|
|
}
|
|
|
|
void VPlanTransforms::attachVPCheckBlock(VPlan &Plan, VPValue *Cond,
|
|
VPBasicBlock *CheckBlock,
|
|
bool AddBranchWeights) {
|
|
insertCheckBlockBeforeVectorLoop(Plan, CheckBlock);
|
|
addBypassBranch(Plan, CheckBlock, Cond, AddBranchWeights);
|
|
}
|
|
|
|
void VPlanTransforms::attachCheckBlock(VPlan &Plan, Value *Cond,
|
|
BasicBlock *CheckBlock,
|
|
bool AddBranchWeights) {
|
|
VPValue *CondVPV = Plan.getOrAddLiveIn(Cond);
|
|
VPBasicBlock *CheckBlockVPBB = Plan.createVPIRBasicBlock(CheckBlock);
|
|
attachVPCheckBlock(Plan, CondVPV, CheckBlockVPBB, AddBranchWeights);
|
|
}
|
|
|
|
void VPlanTransforms::addMinimumIterationCheck(
|
|
VPlan &Plan, ElementCount VF, unsigned UF,
|
|
ElementCount MinProfitableTripCount, bool RequiresScalarEpilogue,
|
|
bool TailFolded, Loop *OrigLoop, const uint32_t *MinItersBypassWeights,
|
|
DebugLoc DL, PredicatedScalarEvolution &PSE, VPBasicBlock *CheckBlock) {
|
|
// Generate code to check if the loop's trip count is less than VF * UF, or
|
|
// equal to it in case a scalar epilogue is required; this implies that the
|
|
// vector trip count is zero. This check also covers the case where adding one
|
|
// to the backedge-taken count overflowed leading to an incorrect trip count
|
|
// of zero. In this case we will also jump to the scalar loop.
|
|
CmpInst::Predicate CmpPred =
|
|
RequiresScalarEpilogue ? ICmpInst::ICMP_ULE : ICmpInst::ICMP_ULT;
|
|
// If tail is to be folded, vector loop takes care of all iterations.
|
|
VPValue *TripCountVPV = Plan.getTripCount();
|
|
const SCEV *TripCount = vputils::getSCEVExprForVPValue(TripCountVPV, PSE);
|
|
Type *TripCountTy = TripCount->getType();
|
|
ScalarEvolution &SE = *PSE.getSE();
|
|
auto GetMinTripCount = [&]() -> const SCEV * {
|
|
// Compute max(MinProfitableTripCount, UF * VF) and return it.
|
|
const SCEV *VFxUF =
|
|
SE.getElementCount(TripCountTy, (VF * UF), SCEV::FlagNUW);
|
|
if (UF * VF.getKnownMinValue() >=
|
|
MinProfitableTripCount.getKnownMinValue()) {
|
|
// TODO: SCEV should be able to simplify test.
|
|
return VFxUF;
|
|
}
|
|
const SCEV *MinProfitableTripCountSCEV =
|
|
SE.getElementCount(TripCountTy, MinProfitableTripCount, SCEV::FlagNUW);
|
|
return SE.getUMaxExpr(MinProfitableTripCountSCEV, VFxUF);
|
|
};
|
|
|
|
VPBuilder Builder(CheckBlock);
|
|
VPValue *TripCountCheck = Plan.getFalse();
|
|
const SCEV *Step = GetMinTripCount();
|
|
// TripCountCheck = false, folding tail implies positive vector trip
|
|
// count.
|
|
if (!TailFolded) {
|
|
// TODO: Emit unconditional branch to vector preheader instead of
|
|
// conditional branch with known condition.
|
|
TripCount = SE.applyLoopGuards(TripCount, OrigLoop);
|
|
// Check if the trip count is < the step.
|
|
if (SE.isKnownPredicate(CmpPred, TripCount, Step)) {
|
|
// TODO: Ensure step is at most the trip count when determining max VF and
|
|
// UF, w/o tail folding.
|
|
TripCountCheck = Plan.getTrue();
|
|
} else if (!SE.isKnownPredicate(CmpInst::getInversePredicate(CmpPred),
|
|
TripCount, Step)) {
|
|
// Generate the minimum iteration check only if we cannot prove the
|
|
// check is known to be true, or known to be false.
|
|
// Try to expand Step into VPInstructions in CheckBlock; otherwise fall
|
|
// back to a VPExpandSCEV recipe in the plan's entry block.
|
|
VPValue *MinTripCountVPV =
|
|
VPSCEVExpander(Builder, *PSE.getSE(), DL).tryToExpand(Step);
|
|
if (!MinTripCountVPV)
|
|
MinTripCountVPV = VPBuilder(Plan.getEntry()).createExpandSCEV(Step);
|
|
TripCountCheck = Builder.createICmp(
|
|
CmpPred, TripCountVPV, MinTripCountVPV, DL, "min.iters.check");
|
|
} // else step known to be < trip count, use TripCountCheck preset to false.
|
|
}
|
|
VPInstruction *Term =
|
|
Builder.createNaryOp(VPInstruction::BranchOnCond, {TripCountCheck}, DL);
|
|
if (MinItersBypassWeights) {
|
|
MDBuilder MDB(Plan.getContext());
|
|
MDNode *BranchWeights = MDB.createBranchWeights(
|
|
ArrayRef(MinItersBypassWeights, 2), /*IsExpected=*/false);
|
|
Term->setMetadata(LLVMContext::MD_prof, BranchWeights);
|
|
}
|
|
}
|
|
|
|
void VPlanTransforms::addIterationCountCheckBlock(
|
|
VPlan &Plan, ElementCount VF, unsigned UF, bool RequiresScalarEpilogue,
|
|
Loop *OrigLoop, const uint32_t *MinItersBypassWeights, DebugLoc DL,
|
|
PredicatedScalarEvolution &PSE) {
|
|
auto *CheckBlock = Plan.createVPBasicBlock("vector.main.loop.iter.check");
|
|
insertCheckBlockBeforeVectorLoop(Plan, CheckBlock);
|
|
addMinimumIterationCheck(Plan, VF, UF, ElementCount::getFixed(0),
|
|
RequiresScalarEpilogue, /*TailFolded=*/false,
|
|
OrigLoop, MinItersBypassWeights, DL, PSE,
|
|
CheckBlock);
|
|
}
|
|
|
|
void VPlanTransforms::addMinimumVectorEpilogueIterationCheck(
|
|
VPlan &Plan, Value *VectorTripCount, bool RequiresScalarEpilogue,
|
|
ElementCount EpilogueVF, unsigned EpilogueUF, unsigned MainLoopStep,
|
|
unsigned EpilogueLoopStep, ScalarEvolution &SE) {
|
|
// Add the minimum iteration check for the epilogue vector loop.
|
|
VPValue *TC = Plan.getTripCount();
|
|
Value *TripCount = TC->getLiveInIRValue();
|
|
VPBuilder Builder(cast<VPBasicBlock>(Plan.getEntry()));
|
|
VPValue *VFxUF = Builder.createExpandSCEV(SE.getElementCount(
|
|
TripCount->getType(), (EpilogueVF * EpilogueUF), SCEV::FlagNUW));
|
|
VPValue *Count = Builder.createSub(TC, Plan.getOrAddLiveIn(VectorTripCount),
|
|
DebugLoc::getUnknown(), "n.vec.remaining");
|
|
|
|
// Generate code to check if the loop's trip count is less than VF * UF of
|
|
// the vector epilogue loop.
|
|
auto P = RequiresScalarEpilogue ? ICmpInst::ICMP_ULE : ICmpInst::ICMP_ULT;
|
|
auto *CheckMinIters = Builder.createICmp(
|
|
P, Count, VFxUF, DebugLoc::getUnknown(), "min.epilog.iters.check");
|
|
VPInstruction *Branch =
|
|
Builder.createNaryOp(VPInstruction::BranchOnCond, CheckMinIters);
|
|
|
|
// We assume the remaining `Count` is equally distributed in
|
|
// [0, MainLoopStep)
|
|
// So the probability for `Count < EpilogueLoopStep` should be
|
|
// min(MainLoopStep, EpilogueLoopStep) / MainLoopStep
|
|
// TODO: Improve the estimate by taking the estimated trip count into
|
|
// consideration.
|
|
unsigned EstimatedSkipCount = std::min(MainLoopStep, EpilogueLoopStep);
|
|
const uint32_t Weights[] = {EstimatedSkipCount,
|
|
MainLoopStep - EstimatedSkipCount};
|
|
MDBuilder MDB(Plan.getContext());
|
|
MDNode *BranchWeights =
|
|
MDB.createBranchWeights(Weights, /*IsExpected=*/false);
|
|
Branch->setMetadata(LLVMContext::MD_prof, BranchWeights);
|
|
}
|
|
|
|
/// Find and return the final select instruction of the FindIV result pattern
|
|
/// for the given \p BackedgeVal:
|
|
/// select(icmp ne ComputeReductionResult(ReducedIV), Sentinel),
|
|
/// ComputeReductionResult(ReducedIV), Start.
|
|
static VPInstruction *findFindIVSelect(VPValue *BackedgeVal) {
|
|
return cast<VPInstruction>(
|
|
vputils::findRecipe(BackedgeVal, [BackedgeVal](VPRecipeBase *R) {
|
|
auto *VPI = dyn_cast<VPInstruction>(R);
|
|
return VPI &&
|
|
matchFindIVResult(VPI, m_Specific(BackedgeVal), m_VPValue());
|
|
}));
|
|
}
|
|
|
|
bool VPlanTransforms::handleMaxMinNumReductions(VPlan &Plan) {
|
|
auto GetMinOrMaxCompareValue =
|
|
[](VPReductionPHIRecipe *RedPhiR) -> VPValue * {
|
|
auto *MinOrMaxR =
|
|
dyn_cast_or_null<VPRecipeWithIRFlags>(RedPhiR->getBackedgeValue());
|
|
if (!MinOrMaxR)
|
|
return nullptr;
|
|
|
|
// Check that MinOrMaxR is a VPWidenIntrinsicRecipe or VPReplicateRecipe
|
|
// with an intrinsic that matches the reduction kind.
|
|
Intrinsic::ID ExpectedIntrinsicID =
|
|
getMinMaxReductionIntrinsicOp(RedPhiR->getRecurrenceKind());
|
|
if (!match(MinOrMaxR, m_Intrinsic(ExpectedIntrinsicID)))
|
|
return nullptr;
|
|
|
|
if (MinOrMaxR->getOperand(0) == RedPhiR)
|
|
return MinOrMaxR->getOperand(1);
|
|
|
|
assert(MinOrMaxR->getOperand(1) == RedPhiR &&
|
|
"Reduction phi operand expected");
|
|
return MinOrMaxR->getOperand(0);
|
|
};
|
|
|
|
VPRegionBlock *LoopRegion = Plan.getVectorLoopRegion();
|
|
SmallVector<std::pair<VPReductionPHIRecipe *, VPValue *>>
|
|
MinOrMaxNumReductionsToHandle;
|
|
bool HasUnsupportedPhi = false;
|
|
for (auto &R : LoopRegion->getEntryBasicBlock()->phis()) {
|
|
if (isa<VPWidenIntOrFpInductionRecipe>(&R))
|
|
continue;
|
|
auto *Cur = dyn_cast<VPReductionPHIRecipe>(&R);
|
|
if (!Cur) {
|
|
// TODO: Also support fixed-order recurrence phis.
|
|
HasUnsupportedPhi = true;
|
|
continue;
|
|
}
|
|
if (!RecurrenceDescriptor::isFPMinMaxNumRecurrenceKind(
|
|
Cur->getRecurrenceKind())) {
|
|
HasUnsupportedPhi = true;
|
|
continue;
|
|
}
|
|
|
|
VPValue *MinOrMaxOp = GetMinOrMaxCompareValue(Cur);
|
|
if (!MinOrMaxOp)
|
|
return false;
|
|
|
|
MinOrMaxNumReductionsToHandle.emplace_back(Cur, MinOrMaxOp);
|
|
}
|
|
|
|
if (MinOrMaxNumReductionsToHandle.empty())
|
|
return true;
|
|
|
|
// We won't be able to resume execution in the scalar tail, if there are
|
|
// unsupported header phis or there is no scalar tail at all, due to
|
|
// tail-folding.
|
|
if (HasUnsupportedPhi || !Plan.hasScalarTail())
|
|
return false;
|
|
|
|
/// Check if the vector loop of \p Plan can early exit and restart
|
|
/// execution of last vector iteration in the scalar loop. This requires all
|
|
/// recipes up to early exit point be side-effect free as they are
|
|
/// re-executed. Currently we check that the loop is free of any recipe that
|
|
/// may write to memory. Expected to operate on an early VPlan w/o nested
|
|
/// regions.
|
|
for (VPBlockBase *VPB : vp_depth_first_shallow(
|
|
Plan.getVectorLoopRegion()->getEntryBasicBlock())) {
|
|
auto *VPBB = cast<VPBasicBlock>(VPB);
|
|
for (auto &R : *VPBB) {
|
|
if (R.mayWriteToMemory() && !match(&R, m_BranchOnCount()))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
VPBasicBlock *LatchVPBB = LoopRegion->getExitingBasicBlock();
|
|
VPBuilder LatchBuilder(LatchVPBB->getTerminator());
|
|
VPValue *AllNaNLanes = nullptr;
|
|
SmallPtrSet<VPValue *, 2> RdxResults;
|
|
for (const auto &[_, MinOrMaxOp] : MinOrMaxNumReductionsToHandle) {
|
|
VPValue *RedNaNLanes =
|
|
LatchBuilder.createFCmp(CmpInst::FCMP_UNO, MinOrMaxOp, MinOrMaxOp);
|
|
AllNaNLanes = AllNaNLanes ? LatchBuilder.createOr(AllNaNLanes, RedNaNLanes)
|
|
: RedNaNLanes;
|
|
}
|
|
|
|
VPValue *AnyNaNLane =
|
|
LatchBuilder.createNaryOp(VPInstruction::AnyOf, {AllNaNLanes});
|
|
VPBasicBlock *MiddleVPBB = Plan.getMiddleBlock();
|
|
VPBuilder MiddleBuilder(MiddleVPBB, MiddleVPBB->begin());
|
|
for (const auto &[RedPhiR, _] : MinOrMaxNumReductionsToHandle) {
|
|
assert(RecurrenceDescriptor::isFPMinMaxNumRecurrenceKind(
|
|
RedPhiR->getRecurrenceKind()) &&
|
|
"unsupported reduction");
|
|
|
|
// If we exit early due to NaNs, compute the final reduction result based on
|
|
// the reduction phi at the beginning of the last vector iteration.
|
|
auto *RdxResult = vputils::findComputeReductionResult(RedPhiR);
|
|
assert(RdxResult && "must find a ComputeReductionResult");
|
|
|
|
auto *NewSel = MiddleBuilder.createSelect(AnyNaNLane, RedPhiR,
|
|
RdxResult->getOperand(0));
|
|
RdxResult->setOperand(0, NewSel);
|
|
assert(!RdxResults.contains(RdxResult) && "RdxResult already used");
|
|
RdxResults.insert(RdxResult);
|
|
}
|
|
|
|
auto *LatchExitingBranch = LatchVPBB->getTerminator();
|
|
assert(match(LatchExitingBranch, m_BranchOnCount(m_VPValue(), m_VPValue())) &&
|
|
"Unexpected terminator");
|
|
auto *IsLatchExitTaken = LatchBuilder.createICmp(
|
|
CmpInst::ICMP_EQ, LatchExitingBranch->getOperand(0),
|
|
LatchExitingBranch->getOperand(1));
|
|
auto *AnyExitTaken = LatchBuilder.createOr(AnyNaNLane, IsLatchExitTaken);
|
|
LatchBuilder.createNaryOp(VPInstruction::BranchOnCond, AnyExitTaken);
|
|
LatchExitingBranch->eraseFromParent();
|
|
|
|
// Update resume phis for inductions in the scalar preheader. If AnyNaNLane is
|
|
// true, the resume from the start of the last vector iteration via the
|
|
// canonical IV, otherwise from the original value.
|
|
auto IsTC = [&Plan](VPValue *V) {
|
|
return V == &Plan.getVectorTripCount() || V == Plan.getTripCount();
|
|
};
|
|
for (auto &R : Plan.getScalarPreheader()->phis()) {
|
|
auto *ResumeR = cast<VPPhi>(&R);
|
|
VPValue *VecV = ResumeR->getOperand(0);
|
|
if (RdxResults.contains(VecV))
|
|
continue;
|
|
if (auto *DerivedIV = dyn_cast<VPDerivedIVRecipe>(VecV)) {
|
|
VPValue *DIVTC = DerivedIV->getOperand(1);
|
|
if (DerivedIV->hasOneUse() && IsTC(DIVTC)) {
|
|
auto *NewSel = MiddleBuilder.createSelect(
|
|
AnyNaNLane, LoopRegion->getCanonicalIV(), DIVTC);
|
|
DerivedIV->moveAfter(&*MiddleBuilder.getInsertPoint());
|
|
DerivedIV->setOperand(1, NewSel);
|
|
continue;
|
|
}
|
|
}
|
|
// Bail out and abandon the current, partially modified, VPlan if we
|
|
// encounter resume phi that cannot be updated yet.
|
|
if (!IsTC(VecV)) {
|
|
LLVM_DEBUG(dbgs() << "Found resume phi we cannot update for VPlan with "
|
|
"FMaxNum/FMinNum reduction.\n");
|
|
return false;
|
|
}
|
|
auto *NewSel = MiddleBuilder.createSelect(
|
|
AnyNaNLane, LoopRegion->getCanonicalIV(), VecV);
|
|
ResumeR->setOperand(0, NewSel);
|
|
}
|
|
|
|
auto *MiddleTerm = MiddleVPBB->getTerminator();
|
|
MiddleBuilder.setInsertPoint(MiddleTerm);
|
|
VPValue *MiddleCond = MiddleTerm->getOperand(0);
|
|
VPValue *NewCond =
|
|
MiddleBuilder.createAnd(MiddleCond, MiddleBuilder.createNot(AnyNaNLane));
|
|
MiddleTerm->setOperand(0, NewCond);
|
|
return true;
|
|
}
|
|
|
|
bool VPlanTransforms::handleFindLastReductions(VPlan &Plan) {
|
|
if (Plan.hasScalarVFOnly())
|
|
return false;
|
|
|
|
// We want to create the following nodes:
|
|
// vector.body:
|
|
// ...new WidenPHI recipe introduced to keep the mask value for the latest
|
|
// iteration where any lane was active.
|
|
// mask.phi = phi [ ir<false>, vector.ph ], [ vp<new.mask>, vector.body ]
|
|
// ...data.phi (a VPReductionPHIRecipe for a FindLast reduction) already
|
|
// exists, but needs updating to use 'new.data' for the backedge value.
|
|
// data.phi = phi ir<default.val>, vp<new.data>
|
|
//
|
|
// ...'data' and 'compare' created by existing nodes...
|
|
//
|
|
// ...new recipes introduced to determine whether to update the reduction
|
|
// values or keep the current one.
|
|
// any.active = i1 any-of ir<compare>
|
|
// new.mask = select vp<any.active>, ir<compare>, vp<mask.phi>
|
|
// new.data = select vp<any.active>, ir<data>, ir<data.phi>
|
|
//
|
|
// middle.block:
|
|
// ...extract-last-active replaces compute-reduction-result.
|
|
// result = extract-last-active vp<new.data>, vp<new.mask>, ir<default.val>
|
|
|
|
SmallVector<VPReductionPHIRecipe *, 4> Phis;
|
|
for (VPRecipeBase &Phi :
|
|
Plan.getVectorLoopRegion()->getEntryBasicBlock()->phis()) {
|
|
auto *PhiR = dyn_cast<VPReductionPHIRecipe>(&Phi);
|
|
if (PhiR && RecurrenceDescriptor::isFindLastRecurrenceKind(
|
|
PhiR->getRecurrenceKind()))
|
|
Phis.push_back(PhiR);
|
|
}
|
|
|
|
if (Phis.empty())
|
|
return true;
|
|
|
|
VPValue *HeaderMask = Plan.getVectorLoopRegion()->getHeaderMask();
|
|
for (VPReductionPHIRecipe *PhiR : Phis) {
|
|
// Find the condition for the select/blend.
|
|
VPValue *BackedgeSelect = PhiR->getBackedgeValue();
|
|
VPValue *CondSelect = BackedgeSelect;
|
|
|
|
// If there's a header mask, the backedge select will not be the find-last
|
|
// select.
|
|
if (HeaderMask &&
|
|
!match(BackedgeSelect,
|
|
m_SelectLike(m_Specific(HeaderMask), m_VPValue(CondSelect),
|
|
m_Specific(PhiR))))
|
|
return false;
|
|
|
|
VPValue *Cond = nullptr, *Op1 = nullptr, *Op2 = nullptr;
|
|
|
|
// If we're matching a blend rather than a select, there should be one
|
|
// incoming value which is the data, then all other incoming values should
|
|
// be the phi.
|
|
auto MatchBlend = [&](VPRecipeBase *R) {
|
|
auto *Blend = dyn_cast<VPBlendRecipe>(R);
|
|
if (!Blend)
|
|
return false;
|
|
assert(!Blend->isNormalized() && "must run before blend normalizaion");
|
|
unsigned NumIncomingDataValues = 0;
|
|
for (unsigned I = 0; I < Blend->getNumIncomingValues(); ++I) {
|
|
VPValue *Incoming = Blend->getIncomingValue(I);
|
|
if (Incoming != PhiR) {
|
|
++NumIncomingDataValues;
|
|
Cond = Blend->getMask(I);
|
|
Op1 = Incoming;
|
|
Op2 = PhiR;
|
|
}
|
|
}
|
|
return NumIncomingDataValues == 1;
|
|
};
|
|
|
|
VPSingleDefRecipe *SelectR =
|
|
cast<VPSingleDefRecipe>(CondSelect->getDefiningRecipe());
|
|
if (!match(SelectR,
|
|
m_Select(m_VPValue(Cond), m_VPValue(Op1), m_VPValue(Op2))) &&
|
|
!MatchBlend(SelectR))
|
|
return false;
|
|
|
|
assert(Cond != HeaderMask && "Cond must not be HeaderMask");
|
|
|
|
// Find final reduction computation and replace it with an
|
|
// extract.last.active intrinsic.
|
|
auto *RdxResult =
|
|
findUserOf<VPInstruction::ComputeReductionResult>(BackedgeSelect);
|
|
assert(RdxResult && "Could not find reduction result");
|
|
|
|
// Add mask phi.
|
|
VPBuilder Builder = VPBuilder::getToInsertAfter(PhiR);
|
|
auto *MaskPHI = Builder.createWidenPhi(Plan.getFalse());
|
|
|
|
// Add select for mask.
|
|
Builder.setInsertPoint(SelectR);
|
|
|
|
if (Op1 == PhiR) {
|
|
// Normalize to selecting the data operand when the condition is true by
|
|
// swapping operands and negating the condition.
|
|
std::swap(Op1, Op2);
|
|
Cond = Builder.createNot(Cond);
|
|
}
|
|
assert(Op2 == PhiR && "data value must be selected if Cond is true");
|
|
|
|
if (HeaderMask)
|
|
Cond = Builder.createLogicalAnd(HeaderMask, Cond);
|
|
|
|
VPValue *AnyOf = Builder.createNaryOp(VPInstruction::AnyOf, {Cond});
|
|
VPValue *MaskSelect = Builder.createSelect(AnyOf, Cond, MaskPHI);
|
|
MaskPHI->addIncoming(MaskSelect);
|
|
|
|
// Replace select for data.
|
|
VPValue *DataSelect =
|
|
Builder.createSelect(AnyOf, Op1, Op2, SelectR->getDebugLoc());
|
|
SelectR->replaceAllUsesWith(DataSelect);
|
|
PhiR->setBackedgeValue(DataSelect);
|
|
SelectR->eraseFromParent();
|
|
|
|
Builder.setInsertPoint(RdxResult);
|
|
auto *ExtractLastActive =
|
|
Builder.createNaryOp(VPInstruction::ExtractLastActive,
|
|
{PhiR->getStartValue(), DataSelect, MaskSelect},
|
|
RdxResult->getDebugLoc());
|
|
RdxResult->replaceAllUsesWith(ExtractLastActive);
|
|
RdxResult->eraseFromParent();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// Given a first argmin/argmax pattern with strict predicate consisting of
|
|
/// 1) a MinOrMax reduction \p MinOrMaxPhiR producing \p MinOrMaxResult,
|
|
/// 2) a wide induction \p WideIV,
|
|
/// 3) a FindLastIV reduction \p FindLastIVPhiR using \p WideIV,
|
|
/// return the smallest index of the FindLastIV reduction result using UMin,
|
|
/// unless \p MinOrMaxResult equals the start value of its MinOrMax reduction.
|
|
/// In that case, return the start value of the FindLastIV reduction instead.
|
|
/// If \p WideIV is not canonical, a new canonical wide IV is added, and the
|
|
/// final result is scaled back to the non-canonical \p WideIV.
|
|
/// The final value of the FindLastIV reduction is originally computed using
|
|
/// \p FindIVSelect, \p FindIVCmp, and \p FindIVRdxResult, which are replaced
|
|
/// and removed.
|
|
/// Returns true if the pattern was handled successfully, false otherwise.
|
|
static bool handleFirstArgMinOrMax(
|
|
VPlan &Plan, VPReductionPHIRecipe *MinOrMaxPhiR,
|
|
VPReductionPHIRecipe *FindLastIVPhiR, VPWidenIntOrFpInductionRecipe *WideIV,
|
|
VPInstruction *MinOrMaxResult, VPInstruction *FindIVSelect,
|
|
VPRecipeBase *FindIVCmp, VPInstruction *FindIVRdxResult) {
|
|
assert(!FindLastIVPhiR->isInLoop() && !FindLastIVPhiR->isOrdered() &&
|
|
"inloop and ordered reductions not supported");
|
|
assert(FindLastIVPhiR->getVFScaleFactor() == 1 &&
|
|
"FindIV reduction must not be scaled");
|
|
|
|
Type *Ty = Plan.getVectorLoopRegion()->getCanonicalIVType();
|
|
// TODO: Support non (i.e., narrower than) canonical IV types.
|
|
// TODO: Emit remarks for failed transformations.
|
|
if (Ty != WideIV->getScalarType())
|
|
return false;
|
|
|
|
auto *FindIVSelectR = cast<VPSingleDefRecipe>(
|
|
FindLastIVPhiR->getBackedgeValue()->getDefiningRecipe());
|
|
assert(
|
|
match(FindIVSelectR, m_Select(m_VPValue(), m_VPValue(), m_VPValue())) &&
|
|
"backedge value must be a select");
|
|
if (FindIVSelectR->getOperand(1) != WideIV &&
|
|
FindIVSelectR->getOperand(2) != WideIV)
|
|
return false;
|
|
|
|
// If the original wide IV is not canonical, create a new one. The canonical
|
|
// wide IV is guaranteed to not wrap for all lanes that are active in the
|
|
// vector loop.
|
|
if (!WideIV->isCanonical()) {
|
|
VPIRValue *Zero = Plan.getConstantInt(Ty, 0);
|
|
VPIRValue *One = Plan.getConstantInt(Ty, 1);
|
|
auto *WidenCanIV = new VPWidenIntOrFpInductionRecipe(
|
|
nullptr, Zero, One, WideIV->getVFValue(),
|
|
WideIV->getInductionDescriptor(),
|
|
VPIRFlags::WrapFlagsTy(/*HasNUW=*/true, /*HasNSW=*/false),
|
|
WideIV->getDebugLoc());
|
|
WidenCanIV->insertBefore(WideIV);
|
|
|
|
// Update the select to use the wide canonical IV.
|
|
FindIVSelectR->setOperand(FindIVSelectR->getOperand(1) == WideIV ? 1 : 2,
|
|
WidenCanIV);
|
|
}
|
|
FindLastIVPhiR->setOperand(0, Plan.getPoison(Ty));
|
|
|
|
// The reduction using MinOrMaxPhiR needs adjusting to compute the correct
|
|
// result:
|
|
// 1. Find the first canonical indices corresponding to partial min/max
|
|
// values, using loop reductions.
|
|
// 2. Find which of the partial min/max values are equal to the overall
|
|
// min/max value.
|
|
// 3. Select among the canonical indices those corresponding to the overall
|
|
// min/max value.
|
|
// 4. Find the first canonical index of overall min/max and scale it back to
|
|
// the original IV using VPDerivedIVRecipe.
|
|
// 5. If the overall min/max equals the starting min/max, the condition in
|
|
// the loop was always false, due to being strict; return the start value
|
|
// of FindLastIVPhiR in that case.
|
|
//
|
|
// For example, we transforms two independent reduction result computations
|
|
// for
|
|
//
|
|
// <x1> vector loop: {
|
|
// vector.body:
|
|
// ...
|
|
// ir<%iv> = WIDEN-INDUCTION nuw nsw ir<10>, ir<1>, vp<%0>
|
|
// WIDEN-REDUCTION-PHI ir<%min.idx> = phi ir<sentinel.min.start>,
|
|
// ir<%min.idx.next>
|
|
// WIDEN-REDUCTION-PHI ir<%min.val> = phi ir<100>, ir<%min.val.next>
|
|
// ....
|
|
// WIDEN-INTRINSIC ir<%min.val.next> = call llvm.umin(ir<%min.val>, ir<%l>)
|
|
// WIDEN ir<%min.idx.next> = select ir<%cmp>, ir<%iv>, ir<%min.idx>
|
|
// ...
|
|
// }
|
|
// Successor(s): middle.block
|
|
//
|
|
// middle.block:
|
|
// vp<%iv.rdx> = compute-reduction-result (smax) vp<%min.idx.next>
|
|
// vp<%min.result> = compute-reduction-result (umin) ir<%min.val.next>
|
|
// vp<%cmp> = icmp ne vp<%iv.rdx>, ir<sentinel.min.start>
|
|
// vp<%find.iv.result> = select vp<%cmp>, vp<%iv.rdx>, ir<10>
|
|
//
|
|
//
|
|
// Into:
|
|
//
|
|
// vp<%reduced.min> = compute-reduction-result (umin) ir<%min.val.next>
|
|
// vp<%reduced.mins.mask> = icmp eq ir<%min.val.next>, vp<%reduced.min>
|
|
// vp<%idxs2reduce> = select vp<%reduced.mins.mask>, ir<%min.idx.next>,
|
|
// ir<MaxUInt>
|
|
// vp<%reduced.idx> = compute-reduction-result (umin) vp<%idxs2reduce>
|
|
// vp<%scaled.idx> = DERIVED-IV ir<20> + vp<%reduced.idx> * ir<1>
|
|
// vp<%always.false> = icmp eq vp<%reduced.min>, ir<100>
|
|
// vp<%final.idx> = select vp<%always.false>, ir<10>,
|
|
// vp<%scaled.idx>
|
|
|
|
VPBuilder Builder(FindIVRdxResult);
|
|
VPValue *MinOrMaxExiting = MinOrMaxResult->getOperand(0);
|
|
auto *FinalMinOrMaxCmp =
|
|
Builder.createICmp(CmpInst::ICMP_EQ, MinOrMaxExiting, MinOrMaxResult);
|
|
VPValue *LastIVExiting = FindIVRdxResult->getOperand(0);
|
|
VPValue *MaxIV =
|
|
Plan.getConstantInt(APInt::getMaxValue(Ty->getIntegerBitWidth()));
|
|
auto *FinalIVSelect =
|
|
Builder.createSelect(FinalMinOrMaxCmp, LastIVExiting, MaxIV);
|
|
VPIRFlags RdxFlags(RecurKind::UMin, false, false, FastMathFlags());
|
|
VPSingleDefRecipe *FinalCanIV = Builder.createNaryOp(
|
|
VPInstruction::ComputeReductionResult, {FinalIVSelect}, RdxFlags,
|
|
FindIVRdxResult->getDebugLoc());
|
|
|
|
// If we used a new wide canonical IV convert the reduction result back to the
|
|
// original IV scale before the final select.
|
|
if (!WideIV->isCanonical()) {
|
|
auto *DerivedIVRecipe = new VPDerivedIVRecipe(
|
|
InductionDescriptor::IK_IntInduction,
|
|
nullptr, // No FPBinOp for integer induction
|
|
WideIV->getStartValue(), FinalCanIV, WideIV->getStepValue());
|
|
DerivedIVRecipe->insertBefore(&*Builder.getInsertPoint());
|
|
FinalCanIV = DerivedIVRecipe;
|
|
}
|
|
|
|
// If the final min/max value matches its start value, the condition in the
|
|
// loop was always false, i.e. no induction value has been selected. If that's
|
|
// the case, set the result of the IV reduction to its start value.
|
|
VPValue *AlwaysFalse = Builder.createICmp(CmpInst::ICMP_EQ, MinOrMaxResult,
|
|
MinOrMaxPhiR->getStartValue());
|
|
VPValue *FinalIV = Builder.createSelect(
|
|
AlwaysFalse, FindIVSelect->getOperand(2), FinalCanIV);
|
|
FindIVSelect->replaceAllUsesWith(FinalIV);
|
|
|
|
// Erase the old FindIV result pattern which is now dead.
|
|
FindIVSelect->eraseFromParent();
|
|
FindIVCmp->eraseFromParent();
|
|
FindIVRdxResult->eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
bool VPlanTransforms::handleMultiUseReductions(VPlan &Plan,
|
|
OptimizationRemarkEmitter *ORE,
|
|
Loop *TheLoop) {
|
|
for (auto &PhiR : make_early_inc_range(
|
|
Plan.getVectorLoopRegion()->getEntryBasicBlock()->phis())) {
|
|
auto *MinOrMaxPhiR = dyn_cast<VPReductionPHIRecipe>(&PhiR);
|
|
// TODO: check for multi-uses in VPlan directly.
|
|
if (!MinOrMaxPhiR || !MinOrMaxPhiR->hasUsesOutsideReductionChain())
|
|
continue;
|
|
|
|
// MinOrMaxPhiR has users outside the reduction cycle in the loop. Check if
|
|
// the only other user is a FindLastIV reduction. MinOrMaxPhiR must have
|
|
// exactly 2 users:
|
|
// 1) the min/max operation of the reduction cycle, and
|
|
// 2) the compare of a FindLastIV reduction cycle. This compare must match
|
|
// the min/max operation - comparing MinOrMaxPhiR with the operand of the
|
|
// min/max operation, and be used only by the select of the FindLastIV
|
|
// reduction cycle.
|
|
RecurKind RdxKind = MinOrMaxPhiR->getRecurrenceKind();
|
|
assert(
|
|
RecurrenceDescriptor::isIntMinMaxRecurrenceKind(RdxKind) &&
|
|
"only min/max recurrences support users outside the reduction chain");
|
|
|
|
auto *MinOrMaxOp =
|
|
dyn_cast<VPRecipeWithIRFlags>(MinOrMaxPhiR->getBackedgeValue());
|
|
if (!MinOrMaxOp)
|
|
return false;
|
|
|
|
// Check that MinOrMaxOp is a VPWidenIntrinsicRecipe or VPReplicateRecipe
|
|
// with an intrinsic that matches the reduction kind.
|
|
Intrinsic::ID ExpectedIntrinsicID = getMinMaxReductionIntrinsicOp(RdxKind);
|
|
if (!match(MinOrMaxOp, m_Intrinsic(ExpectedIntrinsicID)))
|
|
return false;
|
|
|
|
// MinOrMaxOp must have 2 users: 1) MinOrMaxPhiR and 2)
|
|
// ComputeReductionResult.
|
|
assert(MinOrMaxOp->getNumUsers() == 2 &&
|
|
"MinOrMaxOp must have exactly 2 users");
|
|
VPValue *MinOrMaxOpValue = MinOrMaxOp->getOperand(0);
|
|
if (MinOrMaxOpValue == MinOrMaxPhiR)
|
|
MinOrMaxOpValue = MinOrMaxOp->getOperand(1);
|
|
|
|
VPValue *CmpOpA;
|
|
VPValue *CmpOpB;
|
|
CmpPredicate Pred;
|
|
auto *Cmp = dyn_cast_or_null<VPRecipeWithIRFlags>(findUserOf(
|
|
MinOrMaxPhiR, m_Cmp(Pred, m_VPValue(CmpOpA), m_VPValue(CmpOpB))));
|
|
if (!Cmp || Cmp->getNumUsers() != 1 ||
|
|
(CmpOpA != MinOrMaxOpValue && CmpOpB != MinOrMaxOpValue))
|
|
return false;
|
|
|
|
if (MinOrMaxOpValue != CmpOpB)
|
|
Pred = CmpInst::getSwappedPredicate(Pred);
|
|
|
|
// MinOrMaxPhiR must have exactly 2 users:
|
|
// * MinOrMaxOp,
|
|
// * Cmp (that's part of a FindLastIV chain).
|
|
if (MinOrMaxPhiR->getNumUsers() != 2)
|
|
return false;
|
|
|
|
VPInstruction *MinOrMaxResult =
|
|
findUserOf<VPInstruction::ComputeReductionResult>(MinOrMaxOp);
|
|
assert(is_contained(MinOrMaxPhiR->users(), MinOrMaxOp) &&
|
|
"one user must be MinOrMaxOp");
|
|
assert(MinOrMaxResult && "MinOrMaxResult must be a user of MinOrMaxOp");
|
|
|
|
// Cmp must be used by the select of a FindLastIV chain.
|
|
VPValue *Sel = dyn_cast<VPSingleDefRecipe>(Cmp->getSingleUser());
|
|
VPValue *IVOp, *FindIV;
|
|
if (!Sel || Sel->getNumUsers() != 2 ||
|
|
!match(Sel,
|
|
m_Select(m_Specific(Cmp), m_VPValue(IVOp), m_VPValue(FindIV))))
|
|
return false;
|
|
|
|
if (!isa<VPReductionPHIRecipe>(FindIV)) {
|
|
std::swap(FindIV, IVOp);
|
|
Pred = CmpInst::getInversePredicate(Pred);
|
|
}
|
|
|
|
auto *FindIVPhiR = dyn_cast<VPReductionPHIRecipe>(FindIV);
|
|
if (!FindIVPhiR || !RecurrenceDescriptor::isFindIVRecurrenceKind(
|
|
FindIVPhiR->getRecurrenceKind()))
|
|
return false;
|
|
|
|
assert(!FindIVPhiR->isInLoop() && !FindIVPhiR->isOrdered() &&
|
|
"cannot handle inloop/ordered reductions yet");
|
|
|
|
// Check if FindIVPhiR is a FindLast pattern by checking the MinMaxKind
|
|
// on its ComputeReductionResult. SMax/UMax indicates FindLast.
|
|
VPInstruction *FindIVResult =
|
|
findUserOf<VPInstruction::ComputeReductionResult>(
|
|
FindIVPhiR->getBackedgeValue());
|
|
assert(FindIVResult &&
|
|
"must be able to retrieve the FindIVResult VPInstruction");
|
|
RecurKind FindIVMinMaxKind = FindIVResult->getRecurKind();
|
|
if (FindIVMinMaxKind != RecurKind::SMax &&
|
|
FindIVMinMaxKind != RecurKind::UMax)
|
|
return false;
|
|
|
|
// TODO: Support cases where IVOp is the IV increment.
|
|
if (!match(IVOp, m_TruncOrSelf(m_VPValue(IVOp))) ||
|
|
!isa<VPWidenIntOrFpInductionRecipe>(IVOp))
|
|
return false;
|
|
|
|
// Check if the predicate is compatible with the reduction kind.
|
|
bool IsValidKindPred = [RdxKind, Pred]() {
|
|
switch (RdxKind) {
|
|
case RecurKind::UMin:
|
|
return Pred == CmpInst::ICMP_UGE || Pred == CmpInst::ICMP_UGT;
|
|
case RecurKind::UMax:
|
|
return Pred == CmpInst::ICMP_ULE || Pred == CmpInst::ICMP_ULT;
|
|
case RecurKind::SMax:
|
|
return Pred == CmpInst::ICMP_SLE || Pred == CmpInst::ICMP_SLT;
|
|
case RecurKind::SMin:
|
|
return Pred == CmpInst::ICMP_SGE || Pred == CmpInst::ICMP_SGT;
|
|
default:
|
|
llvm_unreachable("unhandled recurrence kind");
|
|
}
|
|
}();
|
|
if (!IsValidKindPred) {
|
|
ORE->emit([&]() {
|
|
return OptimizationRemarkMissed(
|
|
DEBUG_TYPE, "VectorizationMultiUseReductionPredicate",
|
|
TheLoop->getStartLoc(), TheLoop->getHeader())
|
|
<< "Multi-use reduction with predicate "
|
|
<< CmpInst::getPredicateName(Pred)
|
|
<< " incompatible with reduction kind";
|
|
});
|
|
return false;
|
|
}
|
|
|
|
auto *FindIVSelect = findFindIVSelect(FindIVPhiR->getBackedgeValue());
|
|
auto *FindIVCmp = FindIVSelect->getOperand(0)->getDefiningRecipe();
|
|
auto *FindIVRdxResult = cast<VPInstruction>(FindIVCmp->getOperand(0));
|
|
assert(FindIVSelect->getParent() == MinOrMaxResult->getParent() &&
|
|
"both results must be computed in the same block");
|
|
// Reducing to a scalar min or max value is placed right before reducing to
|
|
// its scalar iteration, in order to generate instructions that use both
|
|
// their operands.
|
|
MinOrMaxResult->moveBefore(*FindIVRdxResult->getParent(),
|
|
FindIVRdxResult->getIterator());
|
|
|
|
bool IsStrictPredicate = ICmpInst::isLT(Pred) || ICmpInst::isGT(Pred);
|
|
if (IsStrictPredicate) {
|
|
if (!handleFirstArgMinOrMax(Plan, MinOrMaxPhiR, FindIVPhiR,
|
|
cast<VPWidenIntOrFpInductionRecipe>(IVOp),
|
|
MinOrMaxResult, FindIVSelect, FindIVCmp,
|
|
FindIVRdxResult))
|
|
return false;
|
|
continue;
|
|
}
|
|
|
|
// The reduction using MinOrMaxPhiR needs adjusting to compute the correct
|
|
// result:
|
|
// 1. We need to find the last IV for which the condition based on the
|
|
// min/max recurrence is true,
|
|
// 2. Compare the partial min/max reduction result to its final value and,
|
|
// 3. Select the lanes of the partial FindLastIV reductions which
|
|
// correspond to the lanes matching the min/max reduction result.
|
|
//
|
|
// For example, this transforms
|
|
// vp<%min.result> = compute-reduction-result ir<%min.val.next>
|
|
// vp<%iv.rdx> = compute-reduction-result (smax) vp<%min.idx.next>
|
|
// vp<%cmp> = icmp ne vp<%iv.rdx>, SENTINEL
|
|
// vp<%find.iv.result> = select vp<%cmp>, vp<%iv.rdx>, ir<0>
|
|
//
|
|
// into:
|
|
//
|
|
// vp<min.result> = compute-reduction-result ir<%min.val.next>
|
|
// vp<%final.min.cmp> = icmp eq ir<%min.val.next>, vp<min.result>
|
|
// vp<%final.iv> = select vp<%final.min.cmp>, vp<%min.idx.next>, SENTINEL
|
|
// vp<%iv.rdx> = compute-reduction-result (smax) vp<%final.iv>
|
|
// vp<%cmp> = icmp ne vp<%iv.rdx>, SENTINEL
|
|
// vp<%find.iv.result> = select vp<%cmp>, vp<%iv.rdx>, ir<0>
|
|
//
|
|
VPBuilder B(FindIVRdxResult);
|
|
VPValue *MinOrMaxExiting = MinOrMaxResult->getOperand(0);
|
|
auto *FinalMinOrMaxCmp =
|
|
B.createICmp(CmpInst::ICMP_EQ, MinOrMaxExiting, MinOrMaxResult);
|
|
VPValue *Sentinel = FindIVCmp->getOperand(1);
|
|
VPValue *LastIVExiting = FindIVRdxResult->getOperand(0);
|
|
auto *FinalIVSelect =
|
|
B.createSelect(FinalMinOrMaxCmp, LastIVExiting, Sentinel);
|
|
FindIVRdxResult->setOperand(0, FinalIVSelect);
|
|
}
|
|
return true;
|
|
}
|