<div dir="ltr">Note that the newly added files in this commit used the old header. I've fixed it, but please check any other outstanding patches you have.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Jan 23, 2019 at 2:43 PM Hideki Saito via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Author: hsaito<br>
Date: Wed Jan 23 14:43:12 2019<br>
New Revision: 351990<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=351990&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=351990&view=rev</a><br>
Log:<br>
<br>
[LV][VPlan] Change to implement VPlan based predication for<br>
VPlan-native path<br>
<br>
Context: Patch Series #2 for outer loop vectorization support in LV<br>
using VPlan. (RFC:<br>
<a href="http://lists.llvm.org/pipermail/llvm-dev/2017-December/119523.html" rel="noreferrer" target="_blank">http://lists.llvm.org/pipermail/llvm-dev/2017-December/119523.html</a>).<br>
<br>
Patch series #2 checks that inner loops are still trivially lock-step<br>
among all vector elements. Non-loop branches are blindly assumed as<br>
divergent.<br>
<br>
Changes here implement VPlan based predication algorithm to compute<br>
predicates for blocks that need predication. Predicates are computed<br>
for the VPLoop region in reverse post order. A block's predicate is<br>
computed as OR of the masks of all incoming edges. The mask for an<br>
incoming edge is computed as AND of predecessor block's predicate and<br>
either predecessor's Condition bit or NOT(Condition bit) depending on<br>
whether the edge from predecessor block to the current block is true<br>
or false edge.<br>
<br>
Reviewers: fhahn, rengolin, hsaito, dcaballe<br>
<br>
Reviewed By: fhahn<br>
<br>
Patch by Satish Guggilla, thanks!<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D53349" rel="noreferrer" target="_blank">https://reviews.llvm.org/D53349</a><br>
<br>
<br>
Added:<br>
llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.cpp<br>
llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.h<br>
llvm/trunk/unittests/Transforms/Vectorize/VPlanPredicatorTest.cpp<br>
Modified:<br>
llvm/trunk/lib/Transforms/Vectorize/CMakeLists.txt<br>
llvm/trunk/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp<br>
llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp<br>
llvm/trunk/lib/Transforms/Vectorize/VPlan.cpp<br>
llvm/trunk/lib/Transforms/Vectorize/VPlan.h<br>
llvm/trunk/unittests/Transforms/Vectorize/CMakeLists.txt<br>
<br>
Modified: llvm/trunk/lib/Transforms/Vectorize/CMakeLists.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/CMakeLists.txt?rev=351990&r1=351989&r2=351990&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/CMakeLists.txt?rev=351990&r1=351989&r2=351990&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Vectorize/CMakeLists.txt (original)<br>
+++ llvm/trunk/lib/Transforms/Vectorize/CMakeLists.txt Wed Jan 23 14:43:12 2019<br>
@@ -7,6 +7,7 @@ add_llvm_library(LLVMVectorize<br>
VPlan.cpp<br>
VPlanHCFGBuilder.cpp<br>
VPlanHCFGTransforms.cpp<br>
+ VPlanPredicator.cpp<br>
VPlanSLP.cpp<br>
VPlanVerifier.cpp<br>
<br>
<br>
Modified: llvm/trunk/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp?rev=351990&r1=351989&r2=351990&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp?rev=351990&r1=351989&r2=351990&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp (original)<br>
+++ llvm/trunk/lib/Transforms/Vectorize/LoopVectorizationLegality.cpp Wed Jan 23 14:43:12 2019<br>
@@ -22,6 +22,8 @@ using namespace llvm;<br>
#define LV_NAME "loop-vectorize"<br>
#define DEBUG_TYPE LV_NAME<br>
<br>
+extern cl::opt<bool> EnableVPlanPredication;<br>
+<br>
static cl::opt<bool><br>
EnableIfConversion("enable-if-conversion", cl::init(true), cl::Hidden,<br>
cl::desc("Enable if-conversion during vectorization."));<br>
@@ -487,7 +489,10 @@ bool LoopVectorizationLegality::canVecto<br>
// Check whether the BranchInst is a supported one. Only unconditional<br>
// branches, conditional branches with an outer loop invariant condition or<br>
// backedges are supported.<br>
- if (Br && Br->isConditional() &&<br>
+ // FIXME: We skip these checks when VPlan predication is enabled as we<br>
+ // want to allow divergent branches. This whole check will be removed<br>
+ // once VPlan predication is on by default.<br>
+ if (!EnableVPlanPredication && Br && Br->isConditional() &&<br>
!TheLoop->isLoopInvariant(Br->getCondition()) &&<br>
!LI->isLoopHeader(Br->getSuccessor(0)) &&<br>
!LI->isLoopHeader(Br->getSuccessor(1))) {<br>
<br>
Modified: llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp?rev=351990&r1=351989&r2=351990&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp?rev=351990&r1=351989&r2=351990&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp (original)<br>
+++ llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp Wed Jan 23 14:43:12 2019<br>
@@ -58,6 +58,7 @@<br>
#include "VPRecipeBuilder.h"<br>
#include "VPlanHCFGBuilder.h"<br>
#include "VPlanHCFGTransforms.h"<br>
+#include "VPlanPredicator.h"<br>
#include "llvm/ADT/APInt.h"<br>
#include "llvm/ADT/ArrayRef.h"<br>
#include "llvm/ADT/DenseMap.h"<br>
@@ -255,6 +256,13 @@ cl::opt<bool> EnableVPlanNativePath(<br>
cl::desc("Enable VPlan-native vectorization path with "<br>
"support for outer loop vectorization."));<br>
<br>
+// FIXME: Remove this switch once we have divergence analysis. Currently we<br>
+// assume divergent non-backedge branches when this switch is true.<br>
+cl::opt<bool> EnableVPlanPredication(<br>
+ "enable-vplan-predication", cl::init(false), cl::Hidden,<br>
+ cl::desc("Enable VPlan-native vectorization path predicator with "<br>
+ "support for outer loop vectorization."));<br>
+<br>
// This flag enables the stress testing of the VPlan H-CFG construction in the<br>
// VPlan-native vectorization path. It must be used in conjuction with<br>
// -enable-vplan-native-path. -vplan-verify-hcfg can also be used to enable the<br>
@@ -6896,13 +6904,22 @@ LoopVectorizationPlanner::buildVPlan(VFR<br>
VPlanHCFGBuilder HCFGBuilder(OrigLoop, LI, *Plan);<br>
HCFGBuilder.buildHierarchicalCFG();<br>
<br>
+ for (unsigned VF = Range.Start; VF < Range.End; VF *= 2)<br>
+ Plan->addVF(VF);<br>
+<br>
+ if (EnableVPlanPredication) {<br>
+ VPlanPredicator VPP(*Plan);<br>
+ VPP.predicate();<br>
+<br>
+ // Avoid running transformation to recipes until masked code generation in<br>
+ // VPlan-native path is in place.<br>
+ return Plan;<br>
+ }<br>
+<br>
SmallPtrSet<Instruction *, 1> DeadInstructions;<br>
VPlanHCFGTransforms::VPInstructionsToVPRecipes(<br>
Plan, Legal->getInductionVars(), DeadInstructions);<br>
<br>
- for (unsigned VF = Range.Start; VF < Range.End; VF *= 2)<br>
- Plan->addVF(VF);<br>
-<br>
return Plan;<br>
}<br>
<br>
@@ -7119,8 +7136,8 @@ static bool processLoopInVPlanNativePath<br>
VectorizationFactor VF = LVP.planInVPlanNativePath(OptForSize, UserVF);<br>
<br>
// If we are stress testing VPlan builds, do not attempt to generate vector<br>
- // code.<br>
- if (VPlanBuildStressTest)<br>
+ // code. Masked vector code generation support will follow soon.<br>
+ if (VPlanBuildStressTest || EnableVPlanPredication)<br>
return false;<br>
<br>
LVP.setBestPlan(VF.Width, 1);<br>
<br>
Modified: llvm/trunk/lib/Transforms/Vectorize/VPlan.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/VPlan.cpp?rev=351990&r1=351989&r2=351990&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/VPlan.cpp?rev=351990&r1=351989&r2=351990&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Vectorize/VPlan.cpp (original)<br>
+++ llvm/trunk/lib/Transforms/Vectorize/VPlan.cpp Wed Jan 23 14:43:12 2019<br>
@@ -560,6 +560,19 @@ void VPlanPrinter::dumpBasicBlock(const<br>
bumpIndent(1);<br>
OS << Indent << "\"" << DOT::EscapeString(BasicBlock->getName()) << ":\\n\"";<br>
bumpIndent(1);<br>
+<br>
+ // Dump the block predicate.<br>
+ const VPValue *Pred = BasicBlock->getPredicate();<br>
+ if (Pred) {<br>
+ OS << " +\n" << Indent << " \"BlockPredicate: ";<br>
+ if (const VPInstruction *PredI = dyn_cast<VPInstruction>(Pred)) {<br>
+ PredI->printAsOperand(OS);<br>
+ OS << " (" << DOT::EscapeString(PredI->getParent()->getName())<br>
+ << ")\\l\"";<br>
+ } else<br>
+ Pred->printAsOperand(OS);<br>
+ }<br>
+<br>
for (const VPRecipeBase &Recipe : *BasicBlock)<br>
Recipe.print(OS, Indent);<br>
<br>
<br>
Modified: llvm/trunk/lib/Transforms/Vectorize/VPlan.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/VPlan.h?rev=351990&r1=351989&r2=351990&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/VPlan.h?rev=351990&r1=351989&r2=351990&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Vectorize/VPlan.h (original)<br>
+++ llvm/trunk/lib/Transforms/Vectorize/VPlan.h Wed Jan 23 14:43:12 2019<br>
@@ -352,6 +352,9 @@ private:<br>
/// Successor selector, null for zero or single successor blocks.<br>
VPValue *CondBit = nullptr;<br>
<br>
+ /// Current block predicate - null if the block does not need a predicate.<br>
+ VPValue *Predicate = nullptr;<br>
+<br>
/// Add \p Successor as the last successor to this block.<br>
void appendSuccessor(VPBlockBase *Successor) {<br>
assert(Successor && "Cannot add nullptr successor!");<br>
@@ -490,6 +493,12 @@ public:<br>
<br>
void setCondBit(VPValue *CV) { CondBit = CV; }<br>
<br>
+ VPValue *getPredicate() { return Predicate; }<br>
+<br>
+ const VPValue *getPredicate() const { return Predicate; }<br>
+<br>
+ void setPredicate(VPValue *Pred) { Predicate = Pred; }<br>
+<br>
/// Set a given VPBlockBase \p Successor as the single successor of this<br>
/// VPBlockBase. This VPBlockBase is not added as predecessor of \p Successor.<br>
/// This VPBlockBase must have no successors.<br>
@@ -520,6 +529,15 @@ public:<br>
appendPredecessor(Pred);<br>
}<br>
<br>
+ /// Remove all the predecessor of this block.<br>
+ void clearPredecessors() { Predecessors.clear(); }<br>
+<br>
+ /// Remove all the successors of this block and set to null its condition bit<br>
+ void clearSuccessors() {<br>
+ Successors.clear();<br>
+ CondBit = nullptr;<br>
+ }<br>
+<br>
/// The method which generates the output IR that correspond to this<br>
/// VPBlockBase, thereby "executing" the VPlan.<br>
virtual void execute(struct VPTransformState *State) = 0;<br>
@@ -1490,6 +1508,41 @@ public:<br>
From->removeSuccessor(To);<br>
To->removePredecessor(From);<br>
}<br>
+<br>
+ /// Returns true if the edge \p FromBlock -> \p ToBlock is a back-edge.<br>
+ static bool isBackEdge(const VPBlockBase *FromBlock,<br>
+ const VPBlockBase *ToBlock, const VPLoopInfo *VPLI) {<br>
+ assert(FromBlock->getParent() == ToBlock->getParent() &&<br>
+ FromBlock->getParent() && "Must be in same region");<br>
+ const VPLoop *FromLoop = VPLI->getLoopFor(FromBlock);<br>
+ const VPLoop *ToLoop = VPLI->getLoopFor(ToBlock);<br>
+ if (!FromLoop || !ToLoop || FromLoop != ToLoop)<br>
+ return false;<br>
+<br>
+ // A back-edge is a branch from the loop latch to its header.<br>
+ return ToLoop->isLoopLatch(FromBlock) && ToBlock == ToLoop->getHeader();<br>
+ }<br>
+<br>
+ /// Returns true if \p Block is a loop latch<br>
+ static bool blockIsLoopLatch(const VPBlockBase *Block,<br>
+ const VPLoopInfo *VPLInfo) {<br>
+ if (const VPLoop *ParentVPL = VPLInfo->getLoopFor(Block))<br>
+ return ParentVPL->isLoopLatch(Block);<br>
+<br>
+ return false;<br>
+ }<br>
+<br>
+ /// Count and return the number of succesors of \p PredBlock excluding any<br>
+ /// backedges.<br>
+ static unsigned countSuccessorsNoBE(VPBlockBase *PredBlock,<br>
+ VPLoopInfo *VPLI) {<br>
+ unsigned Count = 0;<br>
+ for (VPBlockBase *SuccBlock : PredBlock->getSuccessors()) {<br>
+ if (!VPBlockUtils::isBackEdge(PredBlock, SuccBlock, VPLI))<br>
+ Count++;<br>
+ }<br>
+ return Count;<br>
+ }<br>
};<br>
<br>
class VPInterleavedAccessInfo {<br>
<br>
Added: llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.cpp?rev=351990&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.cpp?rev=351990&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.cpp (added)<br>
+++ llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.cpp Wed Jan 23 14:43:12 2019<br>
@@ -0,0 +1,249 @@<br>
+//===-- VPlanPredicator.cpp -------------------------------------*- C++ -*-===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+///<br>
+/// \file<br>
+/// This file implements the VPlanPredicator class which contains the public<br>
+/// interfaces to predicate and linearize the VPlan region.<br>
+///<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "VPlanPredicator.h"<br>
+#include "VPlan.h"<br>
+#include "llvm/ADT/DepthFirstIterator.h"<br>
+#include "llvm/ADT/GraphTraits.h"<br>
+#include "llvm/ADT/PostOrderIterator.h"<br>
+#include "llvm/Support/Debug.h"<br>
+#include "llvm/Support/raw_ostream.h"<br>
+<br>
+#define DEBUG_TYPE "VPlanPredicator"<br>
+<br>
+using namespace llvm;<br>
+<br>
+// Generate VPInstructions at the beginning of CurrBB that calculate the<br>
+// predicate being propagated from PredBB to CurrBB depending on the edge type<br>
+// between them. For example if:<br>
+// i. PredBB is controlled by predicate %BP, and<br>
+// ii. The edge PredBB->CurrBB is the false edge, controlled by the condition<br>
+// bit value %CBV then this function will generate the following two<br>
+// VPInstructions at the start of CurrBB:<br>
+// %IntermediateVal = not %CBV<br>
+// %FinalVal = and %BP %IntermediateVal<br>
+// It returns %FinalVal.<br>
+VPValue *VPlanPredicator::getOrCreateNotPredicate(VPBasicBlock *PredBB,<br>
+ VPBasicBlock *CurrBB) {<br>
+ VPValue *CBV = PredBB->getCondBit();<br>
+<br>
+ // Set the intermediate value - this is either 'CBV', or 'not CBV'<br>
+ // depending on the edge type.<br>
+ EdgeType ET = getEdgeTypeBetween(PredBB, CurrBB);<br>
+ VPValue *IntermediateVal = nullptr;<br>
+ switch (ET) {<br>
+ case EdgeType::TRUE_EDGE:<br>
+ // CurrBB is the true successor of PredBB - nothing to do here.<br>
+ IntermediateVal = CBV;<br>
+ break;<br>
+<br>
+ case EdgeType::FALSE_EDGE:<br>
+ // CurrBB is the False successor of PredBB - compute not of CBV.<br>
+ IntermediateVal = Builder.createNot(CBV);<br>
+ break;<br>
+ }<br>
+<br>
+ // Now AND intermediate value with PredBB's block predicate if it has one.<br>
+ VPValue *BP = PredBB->getPredicate();<br>
+ if (BP)<br>
+ return Builder.createAnd(BP, IntermediateVal);<br>
+ else<br>
+ return IntermediateVal;<br>
+}<br>
+<br>
+// Generate a tree of ORs for all IncomingPredicates in WorkList.<br>
+// Note: This function destroys the original Worklist.<br>
+//<br>
+// P1 P2 P3 P4 P5<br>
+// \ / \ / /<br>
+// OR1 OR2 /<br>
+// \ | /<br>
+// \ +/-+<br>
+// \ / |<br>
+// OR3 |<br>
+// \ |<br>
+// OR4 <- Returns this<br>
+// |<br>
+//<br>
+// The algorithm uses a worklist of predicates as its main data structure.<br>
+// We pop a pair of values from the front (e.g. P1 and P2), generate an OR<br>
+// (in this example OR1), and push it back. In this example the worklist<br>
+// contains {P3, P4, P5, OR1}.<br>
+// The process iterates until we have only one element in the Worklist (OR4).<br>
+// The last element is the root predicate which is returned.<br>
+VPValue *VPlanPredicator::genPredicateTree(std::list<VPValue *> &Worklist) {<br>
+ if (Worklist.empty())<br>
+ return nullptr;<br>
+<br>
+ // The worklist initially contains all the leaf nodes. Initialize the tree<br>
+ // using them.<br>
+ while (Worklist.size() >= 2) {<br>
+ // Pop a pair of values from the front.<br>
+ VPValue *LHS = Worklist.front();<br>
+ Worklist.pop_front();<br>
+ VPValue *RHS = Worklist.front();<br>
+ Worklist.pop_front();<br>
+<br>
+ // Create an OR of these values.<br>
+ VPValue *Or = Builder.createOr(LHS, RHS);<br>
+<br>
+ // Push OR to the back of the worklist.<br>
+ Worklist.push_back(Or);<br>
+ }<br>
+<br>
+ assert(Worklist.size() == 1 && "Expected 1 item in worklist");<br>
+<br>
+ // The root is the last node in the worklist.<br>
+ VPValue *Root = Worklist.front();<br>
+<br>
+ // This root needs to replace the existing block predicate. This is done in<br>
+ // the caller function.<br>
+ return Root;<br>
+}<br>
+<br>
+// Return whether the edge FromBlock -> ToBlock is a TRUE_EDGE or FALSE_EDGE<br>
+VPlanPredicator::EdgeType<br>
+VPlanPredicator::getEdgeTypeBetween(VPBlockBase *FromBlock,<br>
+ VPBlockBase *ToBlock) {<br>
+ unsigned Count = 0;<br>
+ for (VPBlockBase *SuccBlock : FromBlock->getSuccessors()) {<br>
+ if (SuccBlock == ToBlock) {<br>
+ assert(Count < 2 && "Switch not supported currently");<br>
+ return (Count == 0) ? EdgeType::TRUE_EDGE : EdgeType::FALSE_EDGE;<br>
+ }<br>
+ Count++;<br>
+ }<br>
+<br>
+ llvm_unreachable("Broken getEdgeTypeBetween");<br>
+}<br>
+<br>
+// Generate all predicates needed for CurrBlock by going through its immediate<br>
+// predecessor blocks.<br>
+void VPlanPredicator::createOrPropagatePredicates(VPBlockBase *CurrBlock,<br>
+ VPRegionBlock *Region) {<br>
+ // Blocks that dominate region exit inherit the predicate from the region.<br>
+ // Return after setting the predicate.<br>
+ if (VPDomTree.dominates(CurrBlock, Region->getExit())) {<br>
+ VPValue *RegionBP = Region->getPredicate();<br>
+ CurrBlock->setPredicate(RegionBP);<br>
+ return;<br>
+ }<br>
+<br>
+ // Collect all incoming predicates in a worklist.<br>
+ std::list<VPValue *> IncomingPredicates;<br>
+<br>
+ // Set the builder's insertion point to the top of the current BB<br>
+ VPBasicBlock *CurrBB = cast<VPBasicBlock>(CurrBlock->getEntryBasicBlock());<br>
+ Builder.setInsertPoint(CurrBB, CurrBB->begin());<br>
+<br>
+ // For each predecessor, generate the VPInstructions required for<br>
+ // computing 'BP AND (not) CBV" at the top of CurrBB.<br>
+ // Collect the outcome of this calculation for all predecessors<br>
+ // into IncomingPredicates.<br>
+ for (VPBlockBase *PredBlock : CurrBlock->getPredecessors()) {<br>
+ // Skip back-edges<br>
+ if (VPBlockUtils::isBackEdge(PredBlock, CurrBlock, VPLI))<br>
+ continue;<br>
+<br>
+ VPValue *IncomingPredicate = nullptr;<br>
+ unsigned NumPredSuccsNoBE =<br>
+ VPBlockUtils::countSuccessorsNoBE(PredBlock, VPLI);<br>
+<br>
+ // If there is an unconditional branch to the currBB, then we don't create<br>
+ // edge predicates. We use the predecessor's block predicate instead.<br>
+ if (NumPredSuccsNoBE == 1)<br>
+ IncomingPredicate = PredBlock->getPredicate();<br>
+ else if (NumPredSuccsNoBE == 2) {<br>
+ // Emit recipes into CurrBlock if required<br>
+ assert(isa<VPBasicBlock>(PredBlock) && "Only BBs have multiple exits");<br>
+ IncomingPredicate =<br>
+ getOrCreateNotPredicate(cast<VPBasicBlock>(PredBlock), CurrBB);<br>
+ } else<br>
+ llvm_unreachable("FIXME: switch statement ?");<br>
+<br>
+ if (IncomingPredicate)<br>
+ IncomingPredicates.push_back(IncomingPredicate);<br>
+ }<br>
+<br>
+ // Logically OR all incoming predicates by building the Predicate Tree.<br>
+ VPValue *Predicate = genPredicateTree(IncomingPredicates);<br>
+<br>
+ // Now update the block's predicate with the new one.<br>
+ CurrBlock->setPredicate(Predicate);<br>
+}<br>
+<br>
+// Generate all predicates needed for Region.<br>
+void VPlanPredicator::predicateRegionRec(VPRegionBlock *Region) {<br>
+ VPBasicBlock *EntryBlock = cast<VPBasicBlock>(Region->getEntry());<br>
+ ReversePostOrderTraversal<VPBlockBase *> RPOT(EntryBlock);<br>
+<br>
+ // Generate edge predicates and append them to the block predicate. RPO is<br>
+ // necessary since the predecessor blocks' block predicate needs to be set<br>
+ // before the current block's block predicate can be computed.<br>
+ for (VPBlockBase *Block : make_range(RPOT.begin(), RPOT.end())) {<br>
+ // TODO: Handle nested regions once we start generating the same.<br>
+ assert(!isa<VPRegionBlock>(Block) && "Nested region not expected");<br>
+ createOrPropagatePredicates(Block, Region);<br>
+ }<br>
+}<br>
+<br>
+// Linearize the CFG within Region.<br>
+// TODO: Predication and linearization need RPOT for every region.<br>
+// This traversal is expensive. Since predication is not adding new<br>
+// blocks, we should be able to compute RPOT once in predication and<br>
+// reuse it here. This becomes even more important once we have nested<br>
+// regions.<br>
+void VPlanPredicator::linearizeRegionRec(VPRegionBlock *Region) {<br>
+ ReversePostOrderTraversal<VPBlockBase *> RPOT(Region->getEntry());<br>
+ VPBlockBase *PrevBlock = nullptr;<br>
+<br>
+ for (VPBlockBase *CurrBlock : make_range(RPOT.begin(), RPOT.end())) {<br>
+ // TODO: Handle nested regions once we start generating the same.<br>
+ assert(!isa<VPRegionBlock>(CurrBlock) && "Nested region not expected");<br>
+<br>
+ // Linearize control flow by adding an unconditional edge between PrevBlock<br>
+ // and CurrBlock skipping loop headers and latches to keep intact loop<br>
+ // header predecessors and loop latch successors.<br>
+ if (PrevBlock && !VPLI->isLoopHeader(CurrBlock) &&<br>
+ !VPBlockUtils::blockIsLoopLatch(PrevBlock, VPLI)) {<br>
+<br>
+ LLVM_DEBUG(dbgs() << "Linearizing: " << PrevBlock->getName() << "->"<br>
+ << CurrBlock->getName() << "\n");<br>
+<br>
+ PrevBlock->clearSuccessors();<br>
+ CurrBlock->clearPredecessors();<br>
+ VPBlockUtils::connectBlocks(PrevBlock, CurrBlock);<br>
+ }<br>
+<br>
+ PrevBlock = CurrBlock;<br>
+ }<br>
+}<br>
+<br>
+// Entry point. The driver function for the predicator.<br>
+void VPlanPredicator::predicate(void) {<br>
+ // Predicate the blocks within Region.<br>
+ predicateRegionRec(cast<VPRegionBlock>(Plan.getEntry()));<br>
+<br>
+ // Linearlize the blocks with Region.<br>
+ linearizeRegionRec(cast<VPRegionBlock>(Plan.getEntry()));<br>
+}<br>
+<br>
+VPlanPredicator::VPlanPredicator(VPlan &Plan)<br>
+ : Plan(Plan), VPLI(&(Plan.getVPLoopInfo())) {<br>
+ // FIXME: Predicator is currently computing the dominator information for the<br>
+ // top region. Once we start storing dominator information in a VPRegionBlock,<br>
+ // we can avoid this recalculation.<br>
+ VPDomTree.recalculate(*(cast<VPRegionBlock>(Plan.getEntry())));<br>
+}<br>
<br>
Added: llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.h?rev=351990&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.h?rev=351990&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.h (added)<br>
+++ llvm/trunk/lib/Transforms/Vectorize/VPlanPredicator.h Wed Jan 23 14:43:12 2019<br>
@@ -0,0 +1,75 @@<br>
+//===-- VPlanPredicator.h ---------------------------------------*- C++ -*-===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+///<br>
+/// \file<br>
+/// This file defines the VPlanPredicator class which contains the public<br>
+/// interfaces to predicate and linearize the VPlan region.<br>
+///<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef LLVM_TRANSFORMS_VECTORIZE_VPLAN_PREDICATOR_H<br>
+#define LLVM_TRANSFORMS_VECTORIZE_VPLAN_PREDICATOR_H<br>
+<br>
+#include "LoopVectorizationPlanner.h"<br>
+#include "VPlan.h"<br>
+#include "VPlanDominatorTree.h"<br>
+<br>
+namespace llvm {<br>
+<br>
+class VPlanPredicator {<br>
+private:<br>
+ enum class EdgeType {<br>
+ TRUE_EDGE,<br>
+ FALSE_EDGE,<br>
+ };<br>
+<br>
+ // VPlan being predicated.<br>
+ VPlan &Plan;<br>
+<br>
+ // VPLoopInfo for Plan's HCFG.<br>
+ VPLoopInfo *VPLI;<br>
+<br>
+ // Dominator tree for Plan's HCFG.<br>
+ VPDominatorTree VPDomTree;<br>
+<br>
+ // VPlan builder used to generate VPInstructions for block predicates.<br>
+ VPBuilder Builder;<br>
+<br>
+ /// Get the type of edge from \p FromBlock to \p ToBlock. Returns TRUE_EDGE if<br>
+ /// \p ToBlock is either the unconditional successor or the conditional true<br>
+ /// successor of \p FromBlock and FALSE_EDGE otherwise.<br>
+ EdgeType getEdgeTypeBetween(VPBlockBase *FromBlock, VPBlockBase *ToBlock);<br>
+<br>
+ /// Create and return VPValue corresponding to the predicate for the edge from<br>
+ /// \p PredBB to \p CurrentBlock.<br>
+ VPValue *getOrCreateNotPredicate(VPBasicBlock *PredBB, VPBasicBlock *CurrBB);<br>
+<br>
+ /// Generate and return the result of ORing all the predicate VPValues in \p<br>
+ /// Worklist.<br>
+ VPValue *genPredicateTree(std::list<VPValue *> &Worklist);<br>
+<br>
+ /// Create or propagate predicate for \p CurrBlock in region \p Region using<br>
+ /// predicate(s) of its predecessor(s)<br>
+ void createOrPropagatePredicates(VPBlockBase *CurrBlock,<br>
+ VPRegionBlock *Region);<br>
+<br>
+ /// Predicate the CFG within \p Region.<br>
+ void predicateRegionRec(VPRegionBlock *Region);<br>
+<br>
+ /// Linearize the CFG within \p Region.<br>
+ void linearizeRegionRec(VPRegionBlock *Region);<br>
+<br>
+public:<br>
+ VPlanPredicator(VPlan &Plan);<br>
+<br>
+ /// Predicate Plan's HCFG.<br>
+ void predicate(void);<br>
+};<br>
+} // end namespace llvm<br>
+#endif // LLVM_TRANSFORMS_VECTORIZE_VPLAN_PREDICATOR_H<br>
<br>
Modified: llvm/trunk/unittests/Transforms/Vectorize/CMakeLists.txt<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Transforms/Vectorize/CMakeLists.txt?rev=351990&r1=351989&r2=351990&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Transforms/Vectorize/CMakeLists.txt?rev=351990&r1=351989&r2=351990&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/unittests/Transforms/Vectorize/CMakeLists.txt (original)<br>
+++ llvm/trunk/unittests/Transforms/Vectorize/CMakeLists.txt Wed Jan 23 14:43:12 2019<br>
@@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS<br>
add_llvm_unittest(VectorizeTests<br>
VPlanDominatorTreeTest.cpp<br>
VPlanLoopInfoTest.cpp<br>
+ VPlanPredicatorTest.cpp<br>
VPlanTest.cpp<br>
VPlanHCFGTest.cpp<br>
VPlanSlpTest.cpp<br>
<br>
Added: llvm/trunk/unittests/Transforms/Vectorize/VPlanPredicatorTest.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Transforms/Vectorize/VPlanPredicatorTest.cpp?rev=351990&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Transforms/Vectorize/VPlanPredicatorTest.cpp?rev=351990&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/unittests/Transforms/Vectorize/VPlanPredicatorTest.cpp (added)<br>
+++ llvm/trunk/unittests/Transforms/Vectorize/VPlanPredicatorTest.cpp Wed Jan 23 14:43:12 2019<br>
@@ -0,0 +1,230 @@<br>
+//===- llvm/unittests/Transforms/Vectorize/VPlanPredicatorTest.cpp -----===//<br>
+//<br>
+// The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "../lib/Transforms/Vectorize/VPlanPredicator.h"<br>
+#include "VPlanTestBase.h"<br>
+#include "gtest/gtest.h"<br>
+<br>
+namespace llvm {<br>
+namespace {<br>
+<br>
+class VPlanPredicatorTest : public VPlanTestBase {};<br>
+<br>
+TEST_F(VPlanPredicatorTest, BasicPredicatorTest) {<br>
+ const char *ModuleString =<br>
+ "@arr = common global [8 x [8 x i64]] "<br>
+ "zeroinitializer, align 16\n"<br>
+ "@arr2 = common global [8 x [8 x i64]] "<br>
+ "zeroinitializer, align 16\n"<br>
+ "@arr3 = common global [8 x [8 x i64]] "<br>
+ "zeroinitializer, align 16\n"<br>
+ "define void @f(i64 %n1) {\n"<br>
+ "entry:\n"<br>
+ " br label %for.cond1.preheader\n"<br>
+ "for.cond1.preheader: \n"<br>
+ " %i1.029 = phi i64 [ 0, %entry ], [ %inc14, %for.inc13 ]\n"<br>
+ " br label %for.body3\n"<br>
+ "for.body3: \n"<br>
+ " %i2.028 = phi i64 [ 0, %for.cond1.preheader ], [ %inc, %for.inc ]\n"<br>
+ " %arrayidx4 = getelementptr inbounds [8 x [8 x i64]], [8 x [8 x i64]]* "<br>
+ "@arr, i64 0, i64 %i2.028, i64 %i1.029\n"<br>
+ " %0 = load i64, i64* %arrayidx4, align 8\n"<br>
+ " %cmp5 = icmp ugt i64 %0, 10\n"<br>
+ " br i1 %cmp5, label %if.then, label %for.inc\n"<br>
+ "if.then: \n"<br>
+ " %arrayidx7 = getelementptr inbounds [8 x [8 x i64]], [8 x [8 x i64]]* "<br>
+ "@arr2, i64 0, i64 %i2.028, i64 %i1.029\n"<br>
+ " %1 = load i64, i64* %arrayidx7, align 8\n"<br>
+ " %cmp8 = icmp ugt i64 %1, 100\n"<br>
+ " br i1 %cmp8, label %if.then9, label %for.inc\n"<br>
+ "if.then9: \n"<br>
+ " %add = add nuw nsw i64 %i2.028, %i1.029\n"<br>
+ " %arrayidx11 = getelementptr inbounds [8 x [8 x i64]], [8 x [8 x "<br>
+ "i64]]* @arr3, i64 0, i64 %i2.028, i64 %i1.029\n"<br>
+ " store i64 %add, i64* %arrayidx11, align 8\n"<br>
+ " br label %for.inc\n"<br>
+ "for.inc: \n"<br>
+ " %inc = add nuw nsw i64 %i2.028, 1\n"<br>
+ " %exitcond = icmp eq i64 %inc, 8\n"<br>
+ " br i1 %exitcond, label %for.inc13, label %for.body3\n"<br>
+ "for.inc13: \n"<br>
+ " %inc14 = add nuw nsw i64 %i1.029, 1\n"<br>
+ " %exitcond30 = icmp eq i64 %inc14, 8\n"<br>
+ " br i1 %exitcond30, label %for.end15, label %for.cond1.preheader\n"<br>
+ "for.end15: \n"<br>
+ " ret void\n"<br>
+ "}\n";<br>
+<br>
+ Module &M = parseModule(ModuleString);<br>
+<br>
+ Function *F = M.getFunction("f");<br>
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();<br>
+ auto Plan = buildHCFG(LoopHeader);<br>
+<br>
+ VPRegionBlock *TopRegion = cast<VPRegionBlock>(Plan->getEntry());<br>
+ VPBlockBase *PH = TopRegion->getEntry();<br>
+ VPBlockBase *H = PH->getSingleSuccessor();<br>
+ VPBlockBase *InnerLoopH = H->getSingleSuccessor();<br>
+ VPBlockBase *OuterIf = InnerLoopH->getSuccessors()[0];<br>
+ VPBlockBase *InnerLoopLatch = InnerLoopH->getSuccessors()[1];<br>
+ VPBlockBase *InnerIf = OuterIf->getSuccessors()[0];<br>
+ VPValue *CBV1 = InnerLoopH->getCondBit();<br>
+ VPValue *CBV2 = OuterIf->getCondBit();<br>
+<br>
+ // Apply predication.<br>
+ VPlanPredicator VPP(*Plan);<br>
+ VPP.predicate();<br>
+<br>
+ VPBlockBase *InnerLoopLinSucc = InnerLoopH->getSingleSuccessor();<br>
+ VPBlockBase *OuterIfLinSucc = OuterIf->getSingleSuccessor();<br>
+ VPBlockBase *InnerIfLinSucc = InnerIf->getSingleSuccessor();<br>
+ VPValue *OuterIfPred = OuterIf->getPredicate();<br>
+ VPInstruction *InnerAnd =<br>
+ cast<VPInstruction>(InnerIf->getEntryBasicBlock()->begin());<br>
+ VPValue *InnerIfPred = InnerIf->getPredicate();<br>
+<br>
+ // Test block predicates<br>
+ EXPECT_NE(nullptr, CBV1);<br>
+ EXPECT_NE(nullptr, CBV2);<br>
+ EXPECT_NE(nullptr, InnerAnd);<br>
+ EXPECT_EQ(CBV1, OuterIfPred);<br>
+ EXPECT_EQ(InnerAnd->getOpcode(), Instruction::And);<br>
+ EXPECT_EQ(InnerAnd->getOperand(0), CBV1);<br>
+ EXPECT_EQ(InnerAnd->getOperand(1), CBV2);<br>
+ EXPECT_EQ(InnerIfPred, InnerAnd);<br>
+<br>
+ // Test Linearization<br>
+ EXPECT_EQ(InnerLoopLinSucc, OuterIf);<br>
+ EXPECT_EQ(OuterIfLinSucc, InnerIf);<br>
+ EXPECT_EQ(InnerIfLinSucc, InnerLoopLatch);<br>
+}<br>
+<br>
+// Test generation of Not and Or during predication.<br>
+TEST_F(VPlanPredicatorTest, PredicatorNegOrTest) {<br>
+ const char *ModuleString =<br>
+ "@arr = common global [100 x [100 x i32]] zeroinitializer, align 16\n"<br>
+ "@arr2 = common global [100 x [100 x i32]] zeroinitializer, align 16\n"<br>
+ "@arr3 = common global [100 x [100 x i32]] zeroinitializer, align 16\n"<br>
+ "define void @foo() {\n"<br>
+ "entry:\n"<br>
+ " br label %for.cond1.preheader\n"<br>
+ "for.cond1.preheader: \n"<br>
+ " %indvars.iv42 = phi i64 [ 0, %entry ], [ %indvars.iv.next43, "<br>
+ "%for.inc22 ]\n"<br>
+ " br label %for.body3\n"<br>
+ "for.body3: \n"<br>
+ " %indvars.iv = phi i64 [ 0, %for.cond1.preheader ], [ "<br>
+ "%indvars.iv.next, %if.end21 ]\n"<br>
+ " %arrayidx5 = getelementptr inbounds [100 x [100 x i32]], [100 x [100 "<br>
+ "x i32]]* @arr, i64 0, i64 %indvars.iv, i64 %indvars.iv42\n"<br>
+ " %0 = load i32, i32* %arrayidx5, align 4\n"<br>
+ " %cmp6 = icmp slt i32 %0, 100\n"<br>
+ " br i1 %cmp6, label %if.then, label %if.end21\n"<br>
+ "if.then: \n"<br>
+ " %cmp7 = icmp sgt i32 %0, 10\n"<br>
+ " br i1 %cmp7, label %if.then8, label %if.else\n"<br>
+ "if.then8: \n"<br>
+ " %add = add nsw i32 %0, 10\n"<br>
+ " %arrayidx12 = getelementptr inbounds [100 x [100 x i32]], [100 x [100 "<br>
+ "x i32]]* @arr2, i64 0, i64 %indvars.iv, i64 %indvars.iv42\n"<br>
+ " store i32 %add, i32* %arrayidx12, align 4\n"<br>
+ " br label %if.end\n"<br>
+ "if.else: \n"<br>
+ " %sub = add nsw i32 %0, -10\n"<br>
+ " %arrayidx16 = getelementptr inbounds [100 x [100 x i32]], [100 x [100 "<br>
+ "x i32]]* @arr3, i64 0, i64 %indvars.iv, i64 %indvars.iv42\n"<br>
+ " store i32 %sub, i32* %arrayidx16, align 4\n"<br>
+ " br label %if.end\n"<br>
+ "if.end: \n"<br>
+ " store i32 222, i32* %arrayidx5, align 4\n"<br>
+ " br label %if.end21\n"<br>
+ "if.end21: \n"<br>
+ " %indvars.iv.next = add nuw nsw i64 %indvars.iv, 1\n"<br>
+ " %exitcond = icmp eq i64 %indvars.iv.next, 100\n"<br>
+ " br i1 %exitcond, label %for.inc22, label %for.body3\n"<br>
+ "for.inc22: \n"<br>
+ " %indvars.iv.next43 = add nuw nsw i64 %indvars.iv42, 1\n"<br>
+ " %exitcond44 = icmp eq i64 %indvars.iv.next43, 100\n"<br>
+ " br i1 %exitcond44, label %for.end24, label %for.cond1.preheader\n"<br>
+ "for.end24: \n"<br>
+ " ret void\n"<br>
+ "}\n";<br>
+<br>
+ Module &M = parseModule(ModuleString);<br>
+ Function *F = M.getFunction("foo");<br>
+ BasicBlock *LoopHeader = F->getEntryBlock().getSingleSuccessor();<br>
+ auto Plan = buildHCFG(LoopHeader);<br>
+<br>
+ VPRegionBlock *TopRegion = cast<VPRegionBlock>(Plan->getEntry());<br>
+ VPBlockBase *PH = TopRegion->getEntry();<br>
+ VPBlockBase *H = PH->getSingleSuccessor();<br>
+ VPBlockBase *OuterIfCmpBlk = H->getSingleSuccessor();<br>
+ VPBlockBase *InnerIfCmpBlk = OuterIfCmpBlk->getSuccessors()[0];<br>
+ VPBlockBase *InnerIfTSucc = InnerIfCmpBlk->getSuccessors()[0];<br>
+ VPBlockBase *InnerIfFSucc = InnerIfCmpBlk->getSuccessors()[1];<br>
+ VPBlockBase *TSuccSucc = InnerIfTSucc->getSingleSuccessor();<br>
+ VPBlockBase *FSuccSucc = InnerIfFSucc->getSingleSuccessor();<br>
+<br>
+ VPValue *OuterCBV = OuterIfCmpBlk->getCondBit();<br>
+ VPValue *InnerCBV = InnerIfCmpBlk->getCondBit();<br>
+<br>
+ // Apply predication.<br>
+ VPlanPredicator VPP(*Plan);<br>
+ VPP.predicate();<br>
+<br>
+ VPInstruction *And =<br>
+ cast<VPInstruction>(InnerIfTSucc->getEntryBasicBlock()->begin());<br>
+ VPInstruction *Not =<br>
+ cast<VPInstruction>(InnerIfFSucc->getEntryBasicBlock()->begin());<br>
+ VPInstruction *NotAnd = cast<VPInstruction>(<br>
+ &*std::next(InnerIfFSucc->getEntryBasicBlock()->begin(), 1));<br>
+ VPInstruction *Or =<br>
+ cast<VPInstruction>(TSuccSucc->getEntryBasicBlock()->begin());<br>
+<br>
+ // Test block predicates<br>
+ EXPECT_NE(nullptr, OuterCBV);<br>
+ EXPECT_NE(nullptr, InnerCBV);<br>
+ EXPECT_NE(nullptr, And);<br>
+ EXPECT_NE(nullptr, Not);<br>
+ EXPECT_NE(nullptr, NotAnd);<br>
+<br>
+ EXPECT_EQ(And->getOpcode(), Instruction::And);<br>
+ EXPECT_EQ(NotAnd->getOpcode(), Instruction::And);<br>
+ EXPECT_EQ(Not->getOpcode(), VPInstruction::Not);<br>
+<br>
+ EXPECT_EQ(And->getOperand(0), OuterCBV);<br>
+ EXPECT_EQ(And->getOperand(1), InnerCBV);<br>
+<br>
+ EXPECT_EQ(Not->getOperand(0), InnerCBV);<br>
+<br>
+ EXPECT_EQ(NotAnd->getOperand(0), OuterCBV);<br>
+ EXPECT_EQ(NotAnd->getOperand(1), Not);<br>
+<br>
+ EXPECT_EQ(InnerIfTSucc->getPredicate(), And);<br>
+ EXPECT_EQ(InnerIfFSucc->getPredicate(), NotAnd);<br>
+<br>
+ EXPECT_EQ(TSuccSucc, FSuccSucc);<br>
+ EXPECT_EQ(Or->getOpcode(), Instruction::Or);<br>
+ EXPECT_EQ(TSuccSucc->getPredicate(), Or);<br>
+<br>
+ // Test operands of the Or - account for differences in predecessor block<br>
+ // ordering.<br>
+ VPInstruction *OrOp0Inst = cast<VPInstruction>(Or->getOperand(0));<br>
+ VPInstruction *OrOp1Inst = cast<VPInstruction>(Or->getOperand(1));<br>
+<br>
+ bool ValidOrOperands = false;<br>
+ if (((OrOp0Inst == And) && (OrOp1Inst == NotAnd)) ||<br>
+ ((OrOp0Inst == NotAnd) && (OrOp1Inst == And)))<br>
+ ValidOrOperands = true;<br>
+<br>
+ EXPECT_TRUE(ValidOrOperands);<br>
+}<br>
+<br>
+} // namespace<br>
+} // namespace llvm<br>
<br>
<br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div>