//===-- VPlanPredicator.cpp -------------------------------------*- C++ -*-===// // // 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 the VPlanPredicator class which contains the public /// interfaces to predicate and linearize the VPlan region. /// //===----------------------------------------------------------------------===// #include "VPlanPredicator.h" #include "VPlan.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/GraphTraits.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #define DEBUG_TYPE "VPlanPredicator" using namespace llvm; // Generate VPInstructions at the beginning of CurrBB that calculate the // predicate being propagated from PredBB to CurrBB depending on the edge type // between them. For example if: // i. PredBB is controlled by predicate %BP, and // ii. The edge PredBB->CurrBB is the false edge, controlled by the condition // bit value %CBV then this function will generate the following two // VPInstructions at the start of CurrBB: // %IntermediateVal = not %CBV // %FinalVal = and %BP %IntermediateVal // It returns %FinalVal. VPValue *VPlanPredicator::getOrCreateNotPredicate(VPBasicBlock *PredBB, VPBasicBlock *CurrBB) { VPValue *CBV = PredBB->getCondBit(); // Set the intermediate value - this is either 'CBV', or 'not CBV' // depending on the edge type. EdgeType ET = getEdgeTypeBetween(PredBB, CurrBB); VPValue *IntermediateVal = nullptr; switch (ET) { case EdgeType::TRUE_EDGE: // CurrBB is the true successor of PredBB - nothing to do here. IntermediateVal = CBV; break; case EdgeType::FALSE_EDGE: // CurrBB is the False successor of PredBB - compute not of CBV. IntermediateVal = Builder.createNot(CBV); break; } // Now AND intermediate value with PredBB's block predicate if it has one. VPValue *BP = PredBB->getPredicate(); if (BP) return Builder.createAnd(BP, IntermediateVal); else return IntermediateVal; } // Generate a tree of ORs for all IncomingPredicates in WorkList. // Note: This function destroys the original Worklist. // // P1 P2 P3 P4 P5 // \ / \ / / // OR1 OR2 / // \ | / // \ +/-+ // \ / | // OR3 | // \ | // OR4 <- Returns this // | // // The algorithm uses a worklist of predicates as its main data structure. // We pop a pair of values from the front (e.g. P1 and P2), generate an OR // (in this example OR1), and push it back. In this example the worklist // contains {P3, P4, P5, OR1}. // The process iterates until we have only one element in the Worklist (OR4). // The last element is the root predicate which is returned. VPValue *VPlanPredicator::genPredicateTree(std::list &Worklist) { if (Worklist.empty()) return nullptr; // The worklist initially contains all the leaf nodes. Initialize the tree // using them. while (Worklist.size() >= 2) { // Pop a pair of values from the front. VPValue *LHS = Worklist.front(); Worklist.pop_front(); VPValue *RHS = Worklist.front(); Worklist.pop_front(); // Create an OR of these values. VPValue *Or = Builder.createOr(LHS, RHS); // Push OR to the back of the worklist. Worklist.push_back(Or); } assert(Worklist.size() == 1 && "Expected 1 item in worklist"); // The root is the last node in the worklist. VPValue *Root = Worklist.front(); // This root needs to replace the existing block predicate. This is done in // the caller function. return Root; } // Return whether the edge FromBlock -> ToBlock is a TRUE_EDGE or FALSE_EDGE VPlanPredicator::EdgeType VPlanPredicator::getEdgeTypeBetween(VPBlockBase *FromBlock, VPBlockBase *ToBlock) { unsigned Count = 0; for (VPBlockBase *SuccBlock : FromBlock->getSuccessors()) { if (SuccBlock == ToBlock) { assert(Count < 2 && "Switch not supported currently"); return (Count == 0) ? EdgeType::TRUE_EDGE : EdgeType::FALSE_EDGE; } Count++; } llvm_unreachable("Broken getEdgeTypeBetween"); } // Generate all predicates needed for CurrBlock by going through its immediate // predecessor blocks. void VPlanPredicator::createOrPropagatePredicates(VPBlockBase *CurrBlock, VPRegionBlock *Region) { // Blocks that dominate region exit inherit the predicate from the region. // Return after setting the predicate. if (VPDomTree.dominates(CurrBlock, Region->getExit())) { VPValue *RegionBP = Region->getPredicate(); CurrBlock->setPredicate(RegionBP); return; } // Collect all incoming predicates in a worklist. std::list IncomingPredicates; // Set the builder's insertion point to the top of the current BB VPBasicBlock *CurrBB = cast(CurrBlock->getEntryBasicBlock()); Builder.setInsertPoint(CurrBB, CurrBB->begin()); // For each predecessor, generate the VPInstructions required for // computing 'BP AND (not) CBV" at the top of CurrBB. // Collect the outcome of this calculation for all predecessors // into IncomingPredicates. for (VPBlockBase *PredBlock : CurrBlock->getPredecessors()) { // Skip back-edges if (VPBlockUtils::isBackEdge(PredBlock, CurrBlock, VPLI)) continue; VPValue *IncomingPredicate = nullptr; unsigned NumPredSuccsNoBE = VPBlockUtils::countSuccessorsNoBE(PredBlock, VPLI); // If there is an unconditional branch to the currBB, then we don't create // edge predicates. We use the predecessor's block predicate instead. if (NumPredSuccsNoBE == 1) IncomingPredicate = PredBlock->getPredicate(); else if (NumPredSuccsNoBE == 2) { // Emit recipes into CurrBlock if required assert(isa(PredBlock) && "Only BBs have multiple exits"); IncomingPredicate = getOrCreateNotPredicate(cast(PredBlock), CurrBB); } else llvm_unreachable("FIXME: switch statement ?"); if (IncomingPredicate) IncomingPredicates.push_back(IncomingPredicate); } // Logically OR all incoming predicates by building the Predicate Tree. VPValue *Predicate = genPredicateTree(IncomingPredicates); // Now update the block's predicate with the new one. CurrBlock->setPredicate(Predicate); } // Generate all predicates needed for Region. void VPlanPredicator::predicateRegionRec(VPRegionBlock *Region) { VPBasicBlock *EntryBlock = cast(Region->getEntry()); ReversePostOrderTraversal RPOT(EntryBlock); // Generate edge predicates and append them to the block predicate. RPO is // necessary since the predecessor blocks' block predicate needs to be set // before the current block's block predicate can be computed. for (VPBlockBase *Block : RPOT) { // TODO: Handle nested regions once we start generating the same. assert(!isa(Block) && "Nested region not expected"); createOrPropagatePredicates(Block, Region); } } // Linearize the CFG within Region. // TODO: Predication and linearization need RPOT for every region. // This traversal is expensive. Since predication is not adding new // blocks, we should be able to compute RPOT once in predication and // reuse it here. This becomes even more important once we have nested // regions. void VPlanPredicator::linearizeRegionRec(VPRegionBlock *Region) { ReversePostOrderTraversal RPOT(Region->getEntry()); VPBlockBase *PrevBlock = nullptr; for (VPBlockBase *CurrBlock : RPOT) { // TODO: Handle nested regions once we start generating the same. assert(!isa(CurrBlock) && "Nested region not expected"); // Linearize control flow by adding an unconditional edge between PrevBlock // and CurrBlock skipping loop headers and latches to keep intact loop // header predecessors and loop latch successors. if (PrevBlock && !VPLI->isLoopHeader(CurrBlock) && !VPBlockUtils::blockIsLoopLatch(PrevBlock, VPLI)) { LLVM_DEBUG(dbgs() << "Linearizing: " << PrevBlock->getName() << "->" << CurrBlock->getName() << "\n"); PrevBlock->clearSuccessors(); CurrBlock->clearPredecessors(); VPBlockUtils::connectBlocks(PrevBlock, CurrBlock); } PrevBlock = CurrBlock; } } // Entry point. The driver function for the predicator. void VPlanPredicator::predicate(void) { // Predicate the blocks within Region. predicateRegionRec(cast(Plan.getEntry())); // Linearlize the blocks with Region. linearizeRegionRec(cast(Plan.getEntry())); } VPlanPredicator::VPlanPredicator(VPlan &Plan) : Plan(Plan), VPLI(&(Plan.getVPLoopInfo())) { // FIXME: Predicator is currently computing the dominator information for the // top region. Once we start storing dominator information in a VPRegionBlock, // we can avoid this recalculation. VPDomTree.recalculate(*(cast(Plan.getEntry()))); }