<HTML><HEAD><TITLE>Samsung Enterprise Portal mySingle</TITLE>
<META content=IE=5 http-equiv=X-UA-Compatible>
<META content="text/html; charset=windows-1252" http-equiv=Content-Type>
<STYLE id=mysingle_style type=text/css>P {
MARGIN-BOTTOM: 5px; FONT-SIZE: 9pt; FONT-FAMILY: Arial, arial; MARGIN-TOP: 5px
}
TD {
MARGIN-BOTTOM: 5px; FONT-SIZE: 9pt; FONT-FAMILY: Arial, arial; MARGIN-TOP: 5px
}
LI {
MARGIN-BOTTOM: 5px; FONT-SIZE: 9pt; FONT-FAMILY: Arial, arial; MARGIN-TOP: 5px
}
BODY {
FONT-SIZE: 9pt; FONT-FAMILY: Arial, arial; MARGIN: 10px; LINE-HEIGHT: 1.4
}
</STYLE>
<META name=GENERATOR content=ActiveSquare></HEAD>
<BODY>
<P>Hi Richard,</P>
<P>Thanks for the heads-up and sorry for the noise. </P>
<P>Somehow was not getting this warning when configured with cmake. Will fix this shortly.</P>
<P>Thanks</P>
<P>Karthik Bhat</P>
<P>------- <B>Original Message</B> -------</P>
<P><B>Sender</B> : Richard Smith<richard@metafoo.co.uk></P>
<P><B>Date</B> : Apr 20, 2015 13:48 (GMT+09:00)</P>
<P><B>Title</B> : Re: [llvm] r235284 - [NFC] Refactor identification of reductions as common utility function.</P>
<P> </P>
<DIV dir=ltr>Looks like this caused:
<DIV><BR></DIV>
<DIV><PRE style="COLOR: rgb(0,0,0)"><FONT face="monospace, monospace">include/llvm/Transforms/Utils/LoopUtils.h:75:16: warning: private field 'PatternLastInst' is not used [-Wunused-private-field]</FONT></PRE></DIV></DIV>
<DIV class=gmail_extra><BR>
<DIV class=gmail_quote>On Sun, Apr 19, 2015 at 9:38 PM, Karthik Bhat <SPAN dir=ltr><<A href="mailto:kv.bhat@samsung.com" target=_blank>kv.bhat@samsung.com</A>></SPAN> wrote:<BR>
<BLOCKQUOTE class=gmail_quote style="PADDING-LEFT: 1ex; MARGIN: 0px 0px 0px 0.8ex; BORDER-LEFT: #ccc 1px solid">Author: karthik<BR>Date: Sun Apr 19 23:38:33 2015<BR>New Revision: 235284<BR><BR>URL: <A href="http://llvm.org/viewvc/llvm-project?rev=235284&view=rev" target=_blank>http://llvm.org/viewvc/llvm-project?rev=235284&view=rev</A><BR>Log:<BR>[NFC] Refactor identification of reductions as common utility function.<BR>This patch refactors reduction identification code out of LoopVectorizer and<BR>exposes them as common utilities.<BR>No functional change.<BR>Review: <A href="http://reviews.llvm.org/D9046" target=_blank>http://reviews.llvm.org/D9046</A><BR><BR><BR>Added:<BR> llvm/trunk/lib/Transforms/Utils/LoopUtils.cpp<BR>Modified:<BR> llvm/trunk/include/llvm/Transforms/Utils/LoopUtils.h<BR> llvm/trunk/lib/Transforms/Utils/CMakeLists.txt<BR> llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp<BR><BR>Modified: llvm/trunk/include/llvm/Transforms/Utils/LoopUtils.h<BR>URL: <A href="http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/LoopUtils.h?rev=235284&r1=235283&r2=235284&view=diff" target=_blank>http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Utils/LoopUtils.h?rev=235284&r1=235283&r2=235284&view=diff</A><BR>==============================================================================<BR>--- llvm/trunk/include/llvm/Transforms/Utils/LoopUtils.h (original)<BR>+++ llvm/trunk/include/llvm/Transforms/Utils/LoopUtils.h Sun Apr 19 23:38:33 2015<BR>@@ -16,6 +16,7 @@<BR><BR> #include "llvm/ADT/SmallVector.h"<BR> #include "llvm/IR/Dominators.h"<BR>+#include "llvm/IR/IRBuilder.h"<BR><BR> namespace llvm {<BR> class AliasAnalysis;<BR>@@ -42,6 +43,132 @@ struct LICMSafetyInfo {<BR> {}<BR> };<BR><BR>+/// This POD struct holds information about a potential reduction operation.<BR>+class ReductionInstDesc {<BR>+<BR>+public:<BR>+ // This enum represents the kind of minmax reduction.<BR>+ enum MinMaxReductionKind {<BR>+ MRK_Invalid,<BR>+ MRK_UIntMin,<BR>+ MRK_UIntMax,<BR>+ MRK_SIntMin,<BR>+ MRK_SIntMax,<BR>+ MRK_FloatMin,<BR>+ MRK_FloatMax<BR>+ };<BR>+ ReductionInstDesc(bool IsRedux, Instruction *I)<BR>+ : IsReduction(IsRedux), PatternLastInst(I), MinMaxKind(MRK_Invalid) {}<BR>+<BR>+ ReductionInstDesc(Instruction *I, MinMaxReductionKind K)<BR>+ : IsReduction(true), PatternLastInst(I), MinMaxKind(K) {}<BR>+<BR>+ bool isReduction() { return IsReduction; }<BR>+<BR>+ MinMaxReductionKind getMinMaxKind() { return MinMaxKind; }<BR>+<BR>+private:<BR>+ // Is this instruction a reduction candidate.<BR>+ bool IsReduction;<BR>+ // The last instruction in a min/max pattern (select of the select(icmp())<BR>+ // pattern), or the current reduction instruction otherwise.<BR>+ Instruction *PatternLastInst;<BR>+ // If this is a min/max pattern the comparison predicate.<BR>+ MinMaxReductionKind MinMaxKind;<BR>+};<BR>+<BR>+/// This struct holds information about reduction variables.<BR>+class ReductionDescriptor {<BR>+<BR>+public:<BR>+ /// This enum represents the kinds of reductions that we support.<BR>+ enum ReductionKind {<BR>+ RK_NoReduction, ///< Not a reduction.<BR>+ RK_IntegerAdd, ///< Sum of integers.<BR>+ RK_IntegerMult, ///< Product of integers.<BR>+ RK_IntegerOr, ///< Bitwise or logical OR of numbers.<BR>+ RK_IntegerAnd, ///< Bitwise or logical AND of numbers.<BR>+ RK_IntegerXor, ///< Bitwise or logical XOR of numbers.<BR>+ RK_IntegerMinMax, ///< Min/max implemented in terms of select(cmp()).<BR>+ RK_FloatAdd, ///< Sum of floats.<BR>+ RK_FloatMult, ///< Product of floats.<BR>+ RK_FloatMinMax ///< Min/max implemented in terms of select(cmp()).<BR>+ };<BR>+<BR>+ ReductionDescriptor()<BR>+ : StartValue(nullptr), LoopExitInstr(nullptr), Kind(RK_NoReduction),<BR>+ MinMaxKind(ReductionInstDesc::MRK_Invalid) {}<BR>+<BR>+ ReductionDescriptor(Value *Start, Instruction *Exit, ReductionKind K,<BR>+ ReductionInstDesc::MinMaxReductionKind MK)<BR>+ : StartValue(Start), LoopExitInstr(Exit), Kind(K), MinMaxKind(MK) {}<BR>+<BR>+ /// Returns a struct describing if the instruction 'I' can be a reduction<BR>+ /// variable of type 'Kind'. If the reduction is a min/max pattern of<BR>+ /// select(icmp()) this function advances the instruction pointer 'I' from the<BR>+ /// compare instruction to the select instruction and stores this pointer in<BR>+ /// 'PatternLastInst' member of the returned struct.<BR>+ static ReductionInstDesc isReductionInstr(Instruction *I, ReductionKind Kind,<BR>+ ReductionInstDesc &Prev,<BR>+ bool HasFunNoNaNAttr);<BR>+<BR>+ /// Returns true if instuction I has multiple uses in Insts<BR>+ static bool hasMultipleUsesOf(Instruction *I,<BR>+ SmallPtrSetImpl<Instruction *> &Insts);<BR>+<BR>+ /// Returns true if all uses of the instruction I is within the Set.<BR>+ static bool areAllUsesIn(Instruction *I, SmallPtrSetImpl<Instruction *> &Set);<BR>+<BR>+ /// Returns a struct describing if the instruction if the instruction is a<BR>+ /// Select(ICmp(X, Y), X, Y) instruction pattern corresponding to a min(X, Y)<BR>+ /// or max(X, Y).<BR>+ static ReductionInstDesc isMinMaxSelectCmpPattern(Instruction *I,<BR>+ ReductionInstDesc &Prev);<BR>+<BR>+ /// Returns identity corresponding to the ReductionKind.<BR>+ static Constant *getReductionIdentity(ReductionKind K, Type *Tp);<BR>+<BR>+ /// Returns the opcode of binary operation corresponding to the ReductionKind.<BR>+ static unsigned getReductionBinOp(ReductionKind Kind);<BR>+<BR>+ /// Returns a Min/Max operation corresponding to MinMaxReductionKind.<BR>+ static Value *createMinMaxOp(IRBuilder<> &Builder,<BR>+ ReductionInstDesc::MinMaxReductionKind RK,<BR>+ Value *Left, Value *Right);<BR>+<BR>+ /// Returns true if Phi is a reduction of type Kind and adds it to the<BR>+ /// ReductionDescriptor.<BR>+ static bool AddReductionVar(PHINode *Phi, ReductionKind Kind, Loop *TheLoop,<BR>+ bool HasFunNoNaNAttr,<BR>+ ReductionDescriptor &RedDes);<BR>+<BR>+ /// Returns true if Phi is a reduction in TheLoop. The ReductionDescriptor is<BR>+ /// returned in RedDes.<BR>+ static bool isReductionPHI(PHINode *Phi, Loop *TheLoop,<BR>+ ReductionDescriptor &RedDes);<BR>+<BR>+ ReductionKind getReductionKind() { return Kind; }<BR>+<BR>+ ReductionInstDesc::MinMaxReductionKind getMinMaxReductionKind() {<BR>+ return MinMaxKind;<BR>+ }<BR>+<BR>+ TrackingVH<Value> getReductionStartValue() { return StartValue; }<BR>+<BR>+ Instruction *getLoopExitInstr() { return LoopExitInstr; }<BR>+<BR>+private:<BR>+ // The starting value of the reduction.<BR>+ // It does not have to be zero!<BR>+ TrackingVH<Value> StartValue;<BR>+ // The instruction who's value is used outside the loop.<BR>+ Instruction *LoopExitInstr;<BR>+ // The kind of the reduction.<BR>+ ReductionKind Kind;<BR>+ // If this a min/max reduction the kind of reduction.<BR>+ ReductionInstDesc::MinMaxReductionKind MinMaxKind;<BR>+};<BR>+<BR> BasicBlock *InsertPreheaderForLoop(Loop *L, Pass *P);<BR><BR> /// \brief Simplify each loop in a loop nest recursively.<BR><BR>Modified: llvm/trunk/lib/Transforms/Utils/CMakeLists.txt<BR>URL: <A href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/CMakeLists.txt?rev=235284&r1=235283&r2=235284&view=diff" target=_blank>http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/CMakeLists.txt?rev=235284&r1=235283&r2=235284&view=diff</A><BR>==============================================================================<BR>--- llvm/trunk/lib/Transforms/Utils/CMakeLists.txt (original)<BR>+++ llvm/trunk/lib/Transforms/Utils/CMakeLists.txt Sun Apr 19 23:38:33 2015<BR>@@ -21,6 +21,7 @@ add_llvm_library(LLVMTransformUtils<BR> LoopSimplify.cpp<BR> LoopUnroll.cpp<BR> LoopUnrollRuntime.cpp<BR>+ LoopUtils.cpp<BR> LowerInvoke.cpp<BR> LowerSwitch.cpp<BR> Mem2Reg.cpp<BR><BR>Added: llvm/trunk/lib/Transforms/Utils/LoopUtils.cpp<BR>URL: <A href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/LoopUtils.cpp?rev=235284&view=auto" target=_blank>http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Utils/LoopUtils.cpp?rev=235284&view=auto</A><BR>==============================================================================<BR>--- llvm/trunk/lib/Transforms/Utils/LoopUtils.cpp (added)<BR>+++ llvm/trunk/lib/Transforms/Utils/LoopUtils.cpp Sun Apr 19 23:38:33 2015<BR>@@ -0,0 +1,453 @@<BR>+//===-- LoopUtils.cpp - Loop Utility functions -------------------------===//<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>+// This file defines common loop utility functions.<BR>+//<BR>+//===----------------------------------------------------------------------===//<BR>+<BR>+#include "llvm/Analysis/LoopInfo.h"<BR>+#include "llvm/IR/Instructions.h"<BR>+#include "llvm/IR/PatternMatch.h"<BR>+#include "llvm/IR/ValueHandle.h"<BR>+#include "llvm/Support/Debug.h"<BR>+#include "llvm/Transforms/Utils/LoopUtils.h"<BR>+<BR>+using namespace llvm;<BR>+using namespace llvm::PatternMatch;<BR>+<BR>+#define DEBUG_TYPE "loop-utils"<BR>+<BR>+bool ReductionDescriptor::areAllUsesIn(Instruction *I,<BR>+ SmallPtrSetImpl<Instruction *> &Set) {<BR>+ for (User::op_iterator Use = I->op_begin(), E = I->op_end(); Use != E; ++Use)<BR>+ if (!Set.count(dyn_cast<Instruction>(*Use)))<BR>+ return false;<BR>+ return true;<BR>+}<BR>+<BR>+bool ReductionDescriptor::AddReductionVar(PHINode *Phi, ReductionKind Kind,<BR>+ Loop *TheLoop, bool HasFunNoNaNAttr,<BR>+ ReductionDescriptor &RedDes) {<BR>+ if (Phi->getNumIncomingValues() != 2)<BR>+ return false;<BR>+<BR>+ // Reduction variables are only found in the loop header block.<BR>+ if (Phi->getParent() != TheLoop->getHeader())<BR>+ return false;<BR>+<BR>+ // Obtain the reduction start value from the value that comes from the loop<BR>+ // preheader.<BR>+ Value *RdxStart = Phi->getIncomingValueForBlock(TheLoop->getLoopPreheader());<BR>+<BR>+ // ExitInstruction is the single value which is used outside the loop.<BR>+ // We only allow for a single reduction value to be used outside the loop.<BR>+ // This includes users of the reduction, variables (which form a cycle<BR>+ // which ends in the phi node).<BR>+ Instruction *ExitInstruction = nullptr;<BR>+ // Indicates that we found a reduction operation in our scan.<BR>+ bool FoundReduxOp = false;<BR>+<BR>+ // We start with the PHI node and scan for all of the users of this<BR>+ // instruction. All users must be instructions that can be used as reduction<BR>+ // variables (such as ADD). We must have a single out-of-block user. The cycle<BR>+ // must include the original PHI.<BR>+ bool FoundStartPHI = false;<BR>+<BR>+ // To recognize min/max patterns formed by a icmp select sequence, we store<BR>+ // the number of instruction we saw from the recognized min/max pattern,<BR>+ // to make sure we only see exactly the two instructions.<BR>+ unsigned NumCmpSelectPatternInst = 0;<BR>+ ReductionInstDesc ReduxDesc(false, nullptr);<BR>+<BR>+ SmallPtrSet<Instruction *, 8> VisitedInsts;<BR>+ SmallVector<Instruction *, 8> Worklist;<BR>+ Worklist.push_back(Phi);<BR>+ VisitedInsts.insert(Phi);<BR>+<BR>+ // A value in the reduction can be used:<BR>+ // - By the reduction:<BR>+ // - Reduction operation:<BR>+ // - One use of reduction value (safe).<BR>+ // - Multiple use of reduction value (not safe).<BR>+ // - PHI:<BR>+ // - All uses of the PHI must be the reduction (safe).<BR>+ // - Otherwise, not safe.<BR>+ // - By one instruction outside of the loop (safe).<BR>+ // - By further instructions outside of the loop (not safe).<BR>+ // - By an instruction that is not part of the reduction (not safe).<BR>+ // This is either:<BR>+ // * An instruction type other than PHI or the reduction operation.<BR>+ // * A PHI in the header other than the initial PHI.<BR>+ while (!Worklist.empty()) {<BR>+ Instruction *Cur = Worklist.back();<BR>+ Worklist.pop_back();<BR>+<BR>+ // No Users.<BR>+ // If the instruction has no users then this is a broken chain and can't be<BR>+ // a reduction variable.<BR>+ if (Cur->use_empty())<BR>+ return false;<BR>+<BR>+ bool IsAPhi = isa<PHINode>(Cur);<BR>+<BR>+ // A header PHI use other than the original PHI.<BR>+ if (Cur != Phi && IsAPhi && Cur->getParent() == Phi->getParent())<BR>+ return false;<BR>+<BR>+ // Reductions of instructions such as Div, and Sub is only possible if the<BR>+ // LHS is the reduction variable.<BR>+ if (!Cur->isCommutative() && !IsAPhi && !isa<SelectInst>(Cur) &&<BR>+ !isa<ICmpInst>(Cur) && !isa<FCmpInst>(Cur) &&<BR>+ !VisitedInsts.count(dyn_cast<Instruction>(Cur->getOperand(0))))<BR>+ return false;<BR>+<BR>+ // Any reduction instruction must be of one of the allowed kinds.<BR>+ ReduxDesc = isReductionInstr(Cur, Kind, ReduxDesc, HasFunNoNaNAttr);<BR>+ if (!ReduxDesc.isReduction())<BR>+ return false;<BR>+<BR>+ // A reduction operation must only have one use of the reduction value.<BR>+ if (!IsAPhi && Kind != RK_IntegerMinMax && Kind != RK_FloatMinMax &&<BR>+ hasMultipleUsesOf(Cur, VisitedInsts))<BR>+ return false;<BR>+<BR>+ // All inputs to a PHI node must be a reduction value.<BR>+ if (IsAPhi && Cur != Phi && !areAllUsesIn(Cur, VisitedInsts))<BR>+ return false;<BR>+<BR>+ if (Kind == RK_IntegerMinMax &&<BR>+ (isa<ICmpInst>(Cur) || isa<SelectInst>(Cur)))<BR>+ ++NumCmpSelectPatternInst;<BR>+ if (Kind == RK_FloatMinMax && (isa<FCmpInst>(Cur) || isa<SelectInst>(Cur)))<BR>+ ++NumCmpSelectPatternInst;<BR>+<BR>+ // Check whether we found a reduction operator.<BR>+ FoundReduxOp |= !IsAPhi;<BR>+<BR>+ // Process users of current instruction. Push non-PHI nodes after PHI nodes<BR>+ // onto the stack. This way we are going to have seen all inputs to PHI<BR>+ // nodes once we get to them.<BR>+ SmallVector<Instruction *, 8> NonPHIs;<BR>+ SmallVector<Instruction *, 8> PHIs;<BR>+ for (User *U : Cur->users()) {<BR>+ Instruction *UI = cast<Instruction>(U);<BR>+<BR>+ // Check if we found the exit user.<BR>+ BasicBlock *Parent = UI->getParent();<BR>+ if (!TheLoop->contains(Parent)) {<BR>+ // Exit if you find multiple outside users or if the header phi node is<BR>+ // being used. In this case the user uses the value of the previous<BR>+ // iteration, in which case we would loose "VF-1" iterations of the<BR>+ // reduction operation if we vectorize.<BR>+ if (ExitInstruction != nullptr || Cur == Phi)<BR>+ return false;<BR>+<BR>+ // The instruction used by an outside user must be the last instruction<BR>+ // before we feed back to the reduction phi. Otherwise, we loose VF-1<BR>+ // operations on the value.<BR>+ if (std::find(Phi->op_begin(), Phi->op_end(), Cur) == Phi->op_end())<BR>+ return false;<BR>+<BR>+ ExitInstruction = Cur;<BR>+ continue;<BR>+ }<BR>+<BR>+ // Process instructions only once (termination). Each reduction cycle<BR>+ // value must only be used once, except by phi nodes and min/max<BR>+ // reductions which are represented as a cmp followed by a select.<BR>+ ReductionInstDesc IgnoredVal(false, nullptr);<BR>+ if (VisitedInsts.insert(UI).second) {<BR>+ if (isa<PHINode>(UI))<BR>+ PHIs.push_back(UI);<BR>+ else<BR>+ NonPHIs.push_back(UI);<BR>+ } else if (!isa<PHINode>(UI) &&<BR>+ ((!isa<FCmpInst>(UI) && !isa<ICmpInst>(UI) &&<BR>+ !isa<SelectInst>(UI)) ||<BR>+ !isMinMaxSelectCmpPattern(UI, IgnoredVal).isReduction()))<BR>+ return false;<BR>+<BR>+ // Remember that we completed the cycle.<BR>+ if (UI == Phi)<BR>+ FoundStartPHI = true;<BR>+ }<BR>+ Worklist.append(PHIs.begin(), PHIs.end());<BR>+ Worklist.append(NonPHIs.begin(), NonPHIs.end());<BR>+ }<BR>+<BR>+ // This means we have seen one but not the other instruction of the<BR>+ // pattern or more than just a select and cmp.<BR>+ if ((Kind == RK_IntegerMinMax || Kind == RK_FloatMinMax) &&<BR>+ NumCmpSelectPatternInst != 2)<BR>+ return false;<BR>+<BR>+ if (!FoundStartPHI || !FoundReduxOp || !ExitInstruction)<BR>+ return false;<BR>+<BR>+ // We found a reduction var if we have reached the original phi node and we<BR>+ // only have a single instruction with out-of-loop users.<BR>+<BR>+ // The ExitInstruction(Instruction which is allowed to have out-of-loop users)<BR>+ // is saved as part of the ReductionDescriptor.<BR>+<BR>+ // Save the description of this reduction variable.<BR>+ ReductionDescriptor RD(RdxStart, ExitInstruction, Kind,<BR>+ ReduxDesc.getMinMaxKind());<BR>+<BR>+ RedDes = RD;<BR>+<BR>+ return true;<BR>+}<BR>+<BR>+/// Returns true if the instruction is a Select(ICmp(X, Y), X, Y) instruction<BR>+/// pattern corresponding to a min(X, Y) or max(X, Y).<BR>+ReductionInstDesc<BR>+ReductionDescriptor::isMinMaxSelectCmpPattern(Instruction *I,<BR>+ ReductionInstDesc &Prev) {<BR>+<BR>+ assert((isa<ICmpInst>(I) || isa<FCmpInst>(I) || isa<SelectInst>(I)) &&<BR>+ "Expect a select instruction");<BR>+ Instruction *Cmp = nullptr;<BR>+ SelectInst *Select = nullptr;<BR>+<BR>+ // We must handle the select(cmp()) as a single instruction. Advance to the<BR>+ // select.<BR>+ if ((Cmp = dyn_cast<ICmpInst>(I)) || (Cmp = dyn_cast<FCmpInst>(I))) {<BR>+ if (!Cmp->hasOneUse() || !(Select = dyn_cast<SelectInst>(*I->user_begin())))<BR>+ return ReductionInstDesc(false, I);<BR>+ return ReductionInstDesc(Select, Prev.getMinMaxKind());<BR>+ }<BR>+<BR>+ // Only handle single use cases for now.<BR>+ if (!(Select = dyn_cast<SelectInst>(I)))<BR>+ return ReductionInstDesc(false, I);<BR>+ if (!(Cmp = dyn_cast<ICmpInst>(I->getOperand(0))) &&<BR>+ !(Cmp = dyn_cast<FCmpInst>(I->getOperand(0))))<BR>+ return ReductionInstDesc(false, I);<BR>+ if (!Cmp->hasOneUse())<BR>+ return ReductionInstDesc(false, I);<BR>+<BR>+ Value *CmpLeft;<BR>+ Value *CmpRight;<BR>+<BR>+ // Look for a min/max pattern.<BR>+ if (m_UMin(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>+ return ReductionInstDesc(Select, ReductionInstDesc::MRK_UIntMin);<BR>+ else if (m_UMax(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>+ return ReductionInstDesc(Select, ReductionInstDesc::MRK_UIntMax);<BR>+ else if (m_SMax(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>+ return ReductionInstDesc(Select, ReductionInstDesc::MRK_SIntMax);<BR>+ else if (m_SMin(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>+ return ReductionInstDesc(Select, ReductionInstDesc::MRK_SIntMin);<BR>+ else if (m_OrdFMin(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>+ return ReductionInstDesc(Select, ReductionInstDesc::MRK_FloatMin);<BR>+ else if (m_OrdFMax(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>+ return ReductionInstDesc(Select, ReductionInstDesc::MRK_FloatMax);<BR>+ else if (m_UnordFMin(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>+ return ReductionInstDesc(Select, ReductionInstDesc::MRK_FloatMin);<BR>+ else if (m_UnordFMax(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>+ return ReductionInstDesc(Select, ReductionInstDesc::MRK_FloatMax);<BR>+<BR>+ return ReductionInstDesc(false, I);<BR>+}<BR>+<BR>+ReductionInstDesc ReductionDescriptor::isReductionInstr(Instruction *I,<BR>+ ReductionKind Kind,<BR>+ ReductionInstDesc &Prev,<BR>+ bool HasFunNoNaNAttr) {<BR>+ bool FP = I->getType()->isFloatingPointTy();<BR>+ bool FastMath = FP && I->hasUnsafeAlgebra();<BR>+ switch (I->getOpcode()) {<BR>+ default:<BR>+ return ReductionInstDesc(false, I);<BR>+ case Instruction::PHI:<BR>+ if (FP &&<BR>+ (Kind != RK_FloatMult && Kind != RK_FloatAdd && Kind != RK_FloatMinMax))<BR>+ return ReductionInstDesc(false, I);<BR>+ return ReductionInstDesc(I, Prev.getMinMaxKind());<BR>+ case Instruction::Sub:<BR>+ case Instruction::Add:<BR>+ return ReductionInstDesc(Kind == RK_IntegerAdd, I);<BR>+ case Instruction::Mul:<BR>+ return ReductionInstDesc(Kind == RK_IntegerMult, I);<BR>+ case Instruction::And:<BR>+ return ReductionInstDesc(Kind == RK_IntegerAnd, I);<BR>+ case Instruction::Or:<BR>+ return ReductionInstDesc(Kind == RK_IntegerOr, I);<BR>+ case Instruction::Xor:<BR>+ return ReductionInstDesc(Kind == RK_IntegerXor, I);<BR>+ case Instruction::FMul:<BR>+ return ReductionInstDesc(Kind == RK_FloatMult && FastMath, I);<BR>+ case Instruction::FSub:<BR>+ case Instruction::FAdd:<BR>+ return ReductionInstDesc(Kind == RK_FloatAdd && FastMath, I);<BR>+ case Instruction::FCmp:<BR>+ case Instruction::ICmp:<BR>+ case Instruction::Select:<BR>+ if (Kind != RK_IntegerMinMax &&<BR>+ (!HasFunNoNaNAttr || Kind != RK_FloatMinMax))<BR>+ return ReductionInstDesc(false, I);<BR>+ return isMinMaxSelectCmpPattern(I, Prev);<BR>+ }<BR>+}<BR>+<BR>+bool ReductionDescriptor::hasMultipleUsesOf(<BR>+ Instruction *I, SmallPtrSetImpl<Instruction *> &Insts) {<BR>+ unsigned NumUses = 0;<BR>+ for (User::op_iterator Use = I->op_begin(), E = I->op_end(); Use != E;<BR>+ ++Use) {<BR>+ if (Insts.count(dyn_cast<Instruction>(*Use)))<BR>+ ++NumUses;<BR>+ if (NumUses > 1)<BR>+ return true;<BR>+ }<BR>+<BR>+ return false;<BR>+}<BR>+bool ReductionDescriptor::isReductionPHI(PHINode *Phi, Loop *TheLoop,<BR>+ ReductionDescriptor &RedDes) {<BR>+<BR>+ bool HasFunNoNaNAttr = false;<BR>+ BasicBlock *Header = TheLoop->getHeader();<BR>+ Function &F = *Header->getParent();<BR>+ if (F.hasFnAttribute("no-nans-fp-math"))<BR>+ HasFunNoNaNAttr =<BR>+ F.getFnAttribute("no-nans-fp-math").getValueAsString() == "true";<BR>+<BR>+ if (AddReductionVar(Phi, RK_IntegerAdd, TheLoop, HasFunNoNaNAttr, RedDes)) {<BR>+ DEBUG(dbgs() << "Found an ADD reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ if (AddReductionVar(Phi, RK_IntegerMult, TheLoop, HasFunNoNaNAttr, RedDes)) {<BR>+ DEBUG(dbgs() << "Found a MUL reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ if (AddReductionVar(Phi, RK_IntegerOr, TheLoop, HasFunNoNaNAttr, RedDes)) {<BR>+ DEBUG(dbgs() << "Found an OR reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ if (AddReductionVar(Phi, RK_IntegerAnd, TheLoop, HasFunNoNaNAttr, RedDes)) {<BR>+ DEBUG(dbgs() << "Found an AND reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ if (AddReductionVar(Phi, RK_IntegerXor, TheLoop, HasFunNoNaNAttr, RedDes)) {<BR>+ DEBUG(dbgs() << "Found a XOR reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ if (AddReductionVar(Phi, RK_IntegerMinMax, TheLoop, HasFunNoNaNAttr,<BR>+ RedDes)) {<BR>+ DEBUG(dbgs() << "Found a MINMAX reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ if (AddReductionVar(Phi, RK_FloatMult, TheLoop, HasFunNoNaNAttr, RedDes)) {<BR>+ DEBUG(dbgs() << "Found an FMult reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ if (AddReductionVar(Phi, RK_FloatAdd, TheLoop, HasFunNoNaNAttr, RedDes)) {<BR>+ DEBUG(dbgs() << "Found an FAdd reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ if (AddReductionVar(Phi, RK_FloatMinMax, TheLoop, HasFunNoNaNAttr, RedDes)) {<BR>+ DEBUG(dbgs() << "Found an float MINMAX reduction PHI." << *Phi << "\n");<BR>+ return true;<BR>+ }<BR>+ // Not a reduction of known type.<BR>+ return false;<BR>+}<BR>+<BR>+/// This function returns the identity element (or neutral element) for<BR>+/// the operation K.<BR>+Constant *ReductionDescriptor::getReductionIdentity(ReductionKind K, Type *Tp) {<BR>+ switch (K) {<BR>+ case RK_IntegerXor:<BR>+ case RK_IntegerAdd:<BR>+ case RK_IntegerOr:<BR>+ // Adding, Xoring, Oring zero to a number does not change it.<BR>+ return ConstantInt::get(Tp, 0);<BR>+ case RK_IntegerMult:<BR>+ // Multiplying a number by 1 does not change it.<BR>+ return ConstantInt::get(Tp, 1);<BR>+ case RK_IntegerAnd:<BR>+ // AND-ing a number with an all-1 value does not change it.<BR>+ return ConstantInt::get(Tp, -1, true);<BR>+ case RK_FloatMult:<BR>+ // Multiplying a number by 1 does not change it.<BR>+ return ConstantFP::get(Tp, 1.0L);<BR>+ case RK_FloatAdd:<BR>+ // Adding zero to a number does not change it.<BR>+ return ConstantFP::get(Tp, 0.0L);<BR>+ default:<BR>+ llvm_unreachable("Unknown reduction kind");<BR>+ }<BR>+}<BR>+<BR>+/// This function translates the reduction kind to an LLVM binary operator.<BR>+unsigned ReductionDescriptor::getReductionBinOp(ReductionKind Kind) {<BR>+ switch (Kind) {<BR>+ case RK_IntegerAdd:<BR>+ return Instruction::Add;<BR>+ case RK_IntegerMult:<BR>+ return Instruction::Mul;<BR>+ case RK_IntegerOr:<BR>+ return Instruction::Or;<BR>+ case RK_IntegerAnd:<BR>+ return Instruction::And;<BR>+ case RK_IntegerXor:<BR>+ return Instruction::Xor;<BR>+ case RK_FloatMult:<BR>+ return Instruction::FMul;<BR>+ case RK_FloatAdd:<BR>+ return Instruction::FAdd;<BR>+ case RK_IntegerMinMax:<BR>+ return Instruction::ICmp;<BR>+ case RK_FloatMinMax:<BR>+ return Instruction::FCmp;<BR>+ default:<BR>+ llvm_unreachable("Unknown reduction operation");<BR>+ }<BR>+}<BR>+<BR>+Value *<BR>+ReductionDescriptor::createMinMaxOp(IRBuilder<> &Builder,<BR>+ ReductionInstDesc::MinMaxReductionKind RK,<BR>+ Value *Left, Value *Right) {<BR>+ CmpInst::Predicate P = CmpInst::ICMP_NE;<BR>+ switch (RK) {<BR>+ default:<BR>+ llvm_unreachable("Unknown min/max reduction kind");<BR>+ case ReductionInstDesc::MRK_UIntMin:<BR>+ P = CmpInst::ICMP_ULT;<BR>+ break;<BR>+ case ReductionInstDesc::MRK_UIntMax:<BR>+ P = CmpInst::ICMP_UGT;<BR>+ break;<BR>+ case ReductionInstDesc::MRK_SIntMin:<BR>+ P = CmpInst::ICMP_SLT;<BR>+ break;<BR>+ case ReductionInstDesc::MRK_SIntMax:<BR>+ P = CmpInst::ICMP_SGT;<BR>+ break;<BR>+ case ReductionInstDesc::MRK_FloatMin:<BR>+ P = CmpInst::FCMP_OLT;<BR>+ break;<BR>+ case ReductionInstDesc::MRK_FloatMax:<BR>+ P = CmpInst::FCMP_OGT;<BR>+ break;<BR>+ }<BR>+<BR>+ Value *Cmp;<BR>+ if (RK == ReductionInstDesc::MRK_FloatMin ||<BR>+ RK == ReductionInstDesc::MRK_FloatMax)<BR>+ Cmp = Builder.CreateFCmp(P, Left, Right, "rdx.minmax.cmp");<BR>+ else<BR>+ Cmp = Builder.CreateICmp(P, Left, Right, "rdx.minmax.cmp");<BR>+<BR>+ Value *Select = Builder.CreateSelect(Cmp, Left, Right, "rdx.minmax.select");<BR>+ return Select;<BR>+}<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=235284&r1=235283&r2=235284&view=diff" target=_blank>http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp?rev=235284&r1=235283&r2=235284&view=diff</A><BR>==============================================================================<BR>--- llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp (original)<BR>+++ llvm/trunk/lib/Transforms/Vectorize/LoopVectorize.cpp Sun Apr 19 23:38:33 2015<BR>@@ -568,20 +568,6 @@ public:<BR> TTI(TTI), DT(DT), LAA(LAA), LAI(nullptr), Induction(nullptr),<BR> WidestIndTy(nullptr), HasFunNoNaNAttr(false) {}<BR><BR>- /// This enum represents the kinds of reductions that we support.<BR>- enum ReductionKind {<BR>- RK_NoReduction, ///< Not a reduction.<BR>- RK_IntegerAdd, ///< Sum of integers.<BR>- RK_IntegerMult, ///< Product of integers.<BR>- RK_IntegerOr, ///< Bitwise or logical OR of numbers.<BR>- RK_IntegerAnd, ///< Bitwise or logical AND of numbers.<BR>- RK_IntegerXor, ///< Bitwise or logical XOR of numbers.<BR>- RK_IntegerMinMax, ///< Min/max implemented in terms of select(cmp()).<BR>- RK_FloatAdd, ///< Sum of floats.<BR>- RK_FloatMult, ///< Product of floats.<BR>- RK_FloatMinMax ///< Min/max implemented in terms of select(cmp()).<BR>- };<BR>-<BR> /// This enum represents the kinds of inductions that we support.<BR> enum InductionKind {<BR> IK_NoInduction, ///< Not an induction variable.<BR>@@ -589,54 +575,6 @@ public:<BR> IK_PtrInduction ///< Pointer induction var. Step = C / sizeof(elem).<BR> };<BR><BR>- // This enum represents the kind of minmax reduction.<BR>- enum MinMaxReductionKind {<BR>- MRK_Invalid,<BR>- MRK_UIntMin,<BR>- MRK_UIntMax,<BR>- MRK_SIntMin,<BR>- MRK_SIntMax,<BR>- MRK_FloatMin,<BR>- MRK_FloatMax<BR>- };<BR>-<BR>- /// This struct holds information about reduction variables.<BR>- struct ReductionDescriptor {<BR>- ReductionDescriptor() : StartValue(nullptr), LoopExitInstr(nullptr),<BR>- Kind(RK_NoReduction), MinMaxKind(MRK_Invalid) {}<BR>-<BR>- ReductionDescriptor(Value *Start, Instruction *Exit, ReductionKind K,<BR>- MinMaxReductionKind MK)<BR>- : StartValue(Start), LoopExitInstr(Exit), Kind(K), MinMaxKind(MK) {}<BR>-<BR>- // The starting value of the reduction.<BR>- // It does not have to be zero!<BR>- TrackingVH<Value> StartValue;<BR>- // The instruction who's value is used outside the loop.<BR>- Instruction *LoopExitInstr;<BR>- // The kind of the reduction.<BR>- ReductionKind Kind;<BR>- // If this a min/max reduction the kind of reduction.<BR>- MinMaxReductionKind MinMaxKind;<BR>- };<BR>-<BR>- /// This POD struct holds information about a potential reduction operation.<BR>- struct ReductionInstDesc {<BR>- ReductionInstDesc(bool IsRedux, Instruction *I) :<BR>- IsReduction(IsRedux), PatternLastInst(I), MinMaxKind(MRK_Invalid) {}<BR>-<BR>- ReductionInstDesc(Instruction *I, MinMaxReductionKind K) :<BR>- IsReduction(true), PatternLastInst(I), MinMaxKind(K) {}<BR>-<BR>- // Is this instruction a reduction candidate.<BR>- bool IsReduction;<BR>- // The last instruction in a min/max pattern (select of the select(icmp())<BR>- // pattern), or the current reduction instruction otherwise.<BR>- Instruction *PatternLastInst;<BR>- // If this is a min/max pattern the comparison predicate.<BR>- MinMaxReductionKind MinMaxKind;<BR>- };<BR>-<BR> /// A struct for saving information about induction variables.<BR> struct InductionInfo {<BR> InductionInfo(Value *Start, InductionKind K, ConstantInt *Step)<BR>@@ -759,10 +697,6 @@ public:<BR> return LAI;<BR> }<BR><BR>- /// This function returns the identity element (or neutral element) for<BR>- /// the operation K.<BR>- static Constant *getReductionIdentity(ReductionKind K, Type *Tp);<BR>-<BR> unsigned getMaxSafeDepDistBytes() { return LAI->getMaxSafeDepDistBytes(); }<BR><BR> bool hasStride(Value *V) { return StrideSet.count(V); }<BR>@@ -820,20 +754,6 @@ private:<BR> /// and we know that we can read from them without segfault.<BR> bool blockCanBePredicated(BasicBlock *BB, SmallPtrSetImpl<Value *> &SafePtrs);<BR><BR>- /// Returns True, if 'Phi' is the kind of reduction variable for type<BR>- /// 'Kind'. If this is a reduction variable, it adds it to ReductionList.<BR>- bool AddReductionVar(PHINode *Phi, ReductionKind Kind);<BR>- /// Returns a struct describing if the instruction 'I' can be a reduction<BR>- /// variable of type 'Kind'. If the reduction is a min/max pattern of<BR>- /// select(icmp()) this function advances the instruction pointer 'I' from the<BR>- /// compare instruction to the select instruction and stores this pointer in<BR>- /// 'PatternLastInst' member of the returned struct.<BR>- ReductionInstDesc isReductionInstr(Instruction *I, ReductionKind Kind,<BR>- ReductionInstDesc &Desc);<BR>- /// Returns true if the instruction is a Select(ICmp(X, Y), X, Y) instruction<BR>- /// pattern corresponding to a min(X, Y) or max(X, Y).<BR>- static ReductionInstDesc isMinMaxSelectCmpPattern(Instruction *I,<BR>- ReductionInstDesc &Prev);<BR> /// Returns the induction kind of Phi and record the step. This function may<BR> /// return NoInduction if the PHI is not an induction variable.<BR> InductionKind isInductionVariable(PHINode *Phi, ConstantInt *&StepValue);<BR>@@ -2469,98 +2389,6 @@ void InnerLoopVectorizer::createEmptyLoo<BR> Hints.setAlreadyVectorized();<BR> }<BR><BR>-/// This function returns the identity element (or neutral element) for<BR>-/// the operation K.<BR>-Constant*<BR>-LoopVectorizationLegality::getReductionIdentity(ReductionKind K, Type *Tp) {<BR>- switch (K) {<BR>- case RK_IntegerXor:<BR>- case RK_IntegerAdd:<BR>- case RK_IntegerOr:<BR>- // Adding, Xoring, Oring zero to a number does not change it.<BR>- return ConstantInt::get(Tp, 0);<BR>- case RK_IntegerMult:<BR>- // Multiplying a number by 1 does not change it.<BR>- return ConstantInt::get(Tp, 1);<BR>- case RK_IntegerAnd:<BR>- // AND-ing a number with an all-1 value does not change it.<BR>- return ConstantInt::get(Tp, -1, true);<BR>- case RK_FloatMult:<BR>- // Multiplying a number by 1 does not change it.<BR>- return ConstantFP::get(Tp, 1.0L);<BR>- case RK_FloatAdd:<BR>- // Adding zero to a number does not change it.<BR>- return ConstantFP::get(Tp, 0.0L);<BR>- default:<BR>- llvm_unreachable("Unknown reduction kind");<BR>- }<BR>-}<BR>-<BR>-/// This function translates the reduction kind to an LLVM binary operator.<BR>-static unsigned<BR>-getReductionBinOp(LoopVectorizationLegality::ReductionKind Kind) {<BR>- switch (Kind) {<BR>- case LoopVectorizationLegality::RK_IntegerAdd:<BR>- return Instruction::Add;<BR>- case LoopVectorizationLegality::RK_IntegerMult:<BR>- return Instruction::Mul;<BR>- case LoopVectorizationLegality::RK_IntegerOr:<BR>- return Instruction::Or;<BR>- case LoopVectorizationLegality::RK_IntegerAnd:<BR>- return Instruction::And;<BR>- case LoopVectorizationLegality::RK_IntegerXor:<BR>- return Instruction::Xor;<BR>- case LoopVectorizationLegality::RK_FloatMult:<BR>- return Instruction::FMul;<BR>- case LoopVectorizationLegality::RK_FloatAdd:<BR>- return Instruction::FAdd;<BR>- case LoopVectorizationLegality::RK_IntegerMinMax:<BR>- return Instruction::ICmp;<BR>- case LoopVectorizationLegality::RK_FloatMinMax:<BR>- return Instruction::FCmp;<BR>- default:<BR>- llvm_unreachable("Unknown reduction operation");<BR>- }<BR>-}<BR>-<BR>-static Value *createMinMaxOp(IRBuilder<> &Builder,<BR>- LoopVectorizationLegality::MinMaxReductionKind RK,<BR>- Value *Left, Value *Right) {<BR>- CmpInst::Predicate P = CmpInst::ICMP_NE;<BR>- switch (RK) {<BR>- default:<BR>- llvm_unreachable("Unknown min/max reduction kind");<BR>- case LoopVectorizationLegality::MRK_UIntMin:<BR>- P = CmpInst::ICMP_ULT;<BR>- break;<BR>- case LoopVectorizationLegality::MRK_UIntMax:<BR>- P = CmpInst::ICMP_UGT;<BR>- break;<BR>- case LoopVectorizationLegality::MRK_SIntMin:<BR>- P = CmpInst::ICMP_SLT;<BR>- break;<BR>- case LoopVectorizationLegality::MRK_SIntMax:<BR>- P = CmpInst::ICMP_SGT;<BR>- break;<BR>- case LoopVectorizationLegality::MRK_FloatMin:<BR>- P = CmpInst::FCMP_OLT;<BR>- break;<BR>- case LoopVectorizationLegality::MRK_FloatMax:<BR>- P = CmpInst::FCMP_OGT;<BR>- break;<BR>- }<BR>-<BR>- Value *Cmp;<BR>- if (RK == LoopVectorizationLegality::MRK_FloatMin ||<BR>- RK == LoopVectorizationLegality::MRK_FloatMax)<BR>- Cmp = Builder.CreateFCmp(P, Left, Right, "rdx.minmax.cmp");<BR>- else<BR>- Cmp = Builder.CreateICmp(P, Left, Right, "rdx.minmax.cmp");<BR>-<BR>- Value *Select = Builder.CreateSelect(Cmp, Left, Right, "rdx.minmax.select");<BR>- return Select;<BR>-}<BR>-<BR> namespace {<BR> struct CSEDenseMapInfo {<BR> static bool canHandle(Instruction *I) {<BR>@@ -2772,10 +2600,14 @@ void InnerLoopVectorizer::vectorizeLoop(<BR> // Find the reduction variable descriptor.<BR> assert(Legal->getReductionVars()->count(RdxPhi) &&<BR> "Unable to find the reduction variable");<BR>- LoopVectorizationLegality::ReductionDescriptor RdxDesc =<BR>- (*Legal->getReductionVars())[RdxPhi];<BR>+ ReductionDescriptor RdxDesc = (*Legal->getReductionVars())[RdxPhi];<BR><BR>- setDebugLocFromInst(Builder, RdxDesc.StartValue);<BR>+ ReductionDescriptor::ReductionKind RK = RdxDesc.getReductionKind();<BR>+ TrackingVH<Value> ReductionStartValue = RdxDesc.getReductionStartValue();<BR>+ Instruction *LoopExitInst = RdxDesc.getLoopExitInstr();<BR>+ ReductionInstDesc::MinMaxReductionKind MinMaxKind =<BR>+ RdxDesc.getMinMaxReductionKind();<BR>+ setDebugLocFromInst(Builder, ReductionStartValue);<BR><BR> // We need to generate a reduction vector from the incoming scalar.<BR> // To do so, we need to generate the 'identity' vector and override<BR>@@ -2784,40 +2616,38 @@ void InnerLoopVectorizer::vectorizeLoop(<BR> Builder.SetInsertPoint(LoopBypassBlocks[1]->getTerminator());<BR><BR> // This is the vector-clone of the value that leaves the loop.<BR>- VectorParts &VectorExit = getVectorValue(RdxDesc.LoopExitInstr);<BR>+ VectorParts &VectorExit = getVectorValue(LoopExitInst);<BR> Type *VecTy = VectorExit[0]->getType();<BR><BR> // Find the reduction identity variable. Zero for addition, or, xor,<BR> // one for multiplication, -1 for And.<BR> Value *Identity;<BR> Value *VectorStart;<BR>- if (RdxDesc.Kind == LoopVectorizationLegality::RK_IntegerMinMax ||<BR>- RdxDesc.Kind == LoopVectorizationLegality::RK_FloatMinMax) {<BR>+ if (RK == ReductionDescriptor::RK_IntegerMinMax ||<BR>+ RK == ReductionDescriptor::RK_FloatMinMax) {<BR> // MinMax reduction have the start value as their identify.<BR> if (VF == 1) {<BR>- VectorStart = Identity = RdxDesc.StartValue;<BR>+ VectorStart = Identity = ReductionStartValue;<BR> } else {<BR>- VectorStart = Identity = Builder.CreateVectorSplat(VF,<BR>- RdxDesc.StartValue,<BR>- "minmax.ident");<BR>+ VectorStart = Identity =<BR>+ Builder.CreateVectorSplat(VF, ReductionStartValue, "minmax.ident");<BR> }<BR> } else {<BR> // Handle other reduction kinds:<BR> Constant *Iden =<BR>- LoopVectorizationLegality::getReductionIdentity(RdxDesc.Kind,<BR>- VecTy->getScalarType());<BR>+ ReductionDescriptor::getReductionIdentity(RK, VecTy->getScalarType());<BR> if (VF == 1) {<BR> Identity = Iden;<BR> // This vector is the Identity vector where the first element is the<BR> // incoming scalar reduction.<BR>- VectorStart = RdxDesc.StartValue;<BR>+ VectorStart = ReductionStartValue;<BR> } else {<BR> Identity = ConstantVector::getSplat(VF, Iden);<BR><BR> // This vector is the Identity vector where the first element is the<BR> // incoming scalar reduction.<BR>- VectorStart = Builder.CreateInsertElement(Identity,<BR>- RdxDesc.StartValue, Zero);<BR>+ VectorStart =<BR>+ Builder.CreateInsertElement(Identity, ReductionStartValue, Zero);<BR> }<BR> }<BR><BR>@@ -2846,11 +2676,11 @@ void InnerLoopVectorizer::vectorizeLoop(<BR> Builder.SetInsertPoint(LoopMiddleBlock->getFirstInsertionPt());<BR><BR> VectorParts RdxParts;<BR>- setDebugLocFromInst(Builder, RdxDesc.LoopExitInstr);<BR>+ setDebugLocFromInst(Builder, LoopExitInst);<BR> for (unsigned part = 0; part < UF; ++part) {<BR> // This PHINode contains the vectorized reduction variable, or<BR> // the initial value vector, if we bypass the vector loop.<BR>- VectorParts &RdxExitVal = getVectorValue(RdxDesc.LoopExitInstr);<BR>+ VectorParts &RdxExitVal = getVectorValue(LoopExitInst);<BR> PHINode *NewPhi = Builder.CreatePHI(VecTy, 2, "rdx.vec.exit.phi");<BR> Value *StartVal = (part == 0) ? VectorStart : Identity;<BR> for (unsigned I = 1, E = LoopBypassBlocks.size(); I != E; ++I)<BR>@@ -2862,7 +2692,7 @@ void InnerLoopVectorizer::vectorizeLoop(<BR><BR> // Reduce all of the unrolled parts into a single vector.<BR> Value *ReducedPartRdx = RdxParts[0];<BR>- unsigned Op = getReductionBinOp(RdxDesc.Kind);<BR>+ unsigned Op = ReductionDescriptor::getReductionBinOp(RK);<BR> setDebugLocFromInst(Builder, ReducedPartRdx);<BR> for (unsigned part = 1; part < UF; ++part) {<BR> if (Op != Instruction::ICmp && Op != Instruction::FCmp)<BR>@@ -2871,8 +2701,8 @@ void InnerLoopVectorizer::vectorizeLoop(<BR> Builder.CreateBinOp((Instruction::BinaryOps)Op, RdxParts[part],<BR> ReducedPartRdx, "bin.rdx"));<BR> else<BR>- ReducedPartRdx = createMinMaxOp(Builder, RdxDesc.MinMaxKind,<BR>- ReducedPartRdx, RdxParts[part]);<BR>+ ReducedPartRdx = ReductionDescriptor::createMinMaxOp(<BR>+ Builder, MinMaxKind, ReducedPartRdx, RdxParts[part]);<BR> }<BR><BR> if (VF > 1) {<BR>@@ -2903,7 +2733,8 @@ void InnerLoopVectorizer::vectorizeLoop(<BR> TmpVec = addFastMathFlag(Builder.CreateBinOp(<BR> (Instruction::BinaryOps)Op, TmpVec, Shuf, "bin.rdx"));<BR> else<BR>- TmpVec = createMinMaxOp(Builder, RdxDesc.MinMaxKind, TmpVec, Shuf);<BR>+ TmpVec = ReductionDescriptor::createMinMaxOp(Builder, MinMaxKind,<BR>+ TmpVec, Shuf);<BR> }<BR><BR> // The result is in the first element of the vector.<BR>@@ -2915,7 +2746,7 @@ void InnerLoopVectorizer::vectorizeLoop(<BR> // block and the middle block.<BR> PHINode *BCBlockPhi = PHINode::Create(RdxPhi->getType(), 2, "bc.merge.rdx",<BR> LoopScalarPreHeader->getTerminator());<BR>- BCBlockPhi->addIncoming(RdxDesc.StartValue, LoopBypassBlocks[0]);<BR>+ BCBlockPhi->addIncoming(ReductionStartValue, LoopBypassBlocks[0]);<BR> BCBlockPhi->addIncoming(ReducedPartRdx, LoopMiddleBlock);<BR><BR> // Now, we need to fix the users of the reduction variable<BR>@@ -2933,7 +2764,7 @@ void InnerLoopVectorizer::vectorizeLoop(<BR><BR> // We found our reduction value exit-PHI. Update it with the<BR> // incoming bypass edge.<BR>- if (LCSSAPhi->getIncomingValue(0) == RdxDesc.LoopExitInstr) {<BR>+ if (LCSSAPhi->getIncomingValue(0) == LoopExitInst) {<BR> // Add an edge coming from the bypass.<BR> LCSSAPhi->addIncoming(ReducedPartRdx, LoopMiddleBlock);<BR> break;<BR>@@ -2948,7 +2779,7 @@ void InnerLoopVectorizer::vectorizeLoop(<BR> // Pick the other block.<BR> int SelfEdgeBlockIdx = (IncomingEdgeBlockIdx ? 0 : 1);<BR> (RdxPhi)->setIncomingValue(SelfEdgeBlockIdx, BCBlockPhi);<BR>- (RdxPhi)->setIncomingValue(IncomingEdgeBlockIdx, RdxDesc.LoopExitInstr);<BR>+ (RdxPhi)->setIncomingValue(IncomingEdgeBlockIdx, LoopExitInst);<BR> }// end of for each redux variable.<BR><BR> fixLCSSAPHIs();<BR>@@ -3712,41 +3543,9 @@ bool LoopVectorizationLegality::canVecto<BR> continue;<BR> }<BR><BR>- if (AddReductionVar(Phi, RK_IntegerAdd)) {<BR>- DEBUG(dbgs() << "LV: Found an ADD reduction PHI."<< *Phi <<"\n");<BR>- continue;<BR>- }<BR>- if (AddReductionVar(Phi, RK_IntegerMult)) {<BR>- DEBUG(dbgs() << "LV: Found a MUL reduction PHI."<< *Phi <<"\n");<BR>- continue;<BR>- }<BR>- if (AddReductionVar(Phi, RK_IntegerOr)) {<BR>- DEBUG(dbgs() << "LV: Found an OR reduction PHI."<< *Phi <<"\n");<BR>- continue;<BR>- }<BR>- if (AddReductionVar(Phi, RK_IntegerAnd)) {<BR>- DEBUG(dbgs() << "LV: Found an AND reduction PHI."<< *Phi <<"\n");<BR>- continue;<BR>- }<BR>- if (AddReductionVar(Phi, RK_IntegerXor)) {<BR>- DEBUG(dbgs() << "LV: Found a XOR reduction PHI."<< *Phi <<"\n");<BR>- continue;<BR>- }<BR>- if (AddReductionVar(Phi, RK_IntegerMinMax)) {<BR>- DEBUG(dbgs() << "LV: Found a MINMAX reduction PHI."<< *Phi <<"\n");<BR>- continue;<BR>- }<BR>- if (AddReductionVar(Phi, RK_FloatMult)) {<BR>- DEBUG(dbgs() << "LV: Found an FMult reduction PHI."<< *Phi <<"\n");<BR>- continue;<BR>- }<BR>- if (AddReductionVar(Phi, RK_FloatAdd)) {<BR>- DEBUG(dbgs() << "LV: Found an FAdd reduction PHI."<< *Phi <<"\n");<BR>- continue;<BR>- }<BR>- if (AddReductionVar(Phi, RK_FloatMinMax)) {<BR>- DEBUG(dbgs() << "LV: Found an float MINMAX reduction PHI."<< *Phi <<<BR>- "\n");<BR>+ if (ReductionDescriptor::isReductionPHI(Phi, TheLoop,<BR>+ Reductions[Phi])) {<BR>+ AllowedExit.insert(Reductions[Phi].getLoopExitInstr());<BR> continue;<BR> }<BR><BR>@@ -4029,294 +3828,6 @@ bool LoopVectorizationLegality::canVecto<BR> return true;<BR> }<BR><BR>-static bool hasMultipleUsesOf(Instruction *I,<BR>- SmallPtrSetImpl<Instruction *> &Insts) {<BR>- unsigned NumUses = 0;<BR>- for(User::op_iterator Use = I->op_begin(), E = I->op_end(); Use != E; ++Use) {<BR>- if (Insts.count(dyn_cast<Instruction>(*Use)))<BR>- ++NumUses;<BR>- if (NumUses > 1)<BR>- return true;<BR>- }<BR>-<BR>- return false;<BR>-}<BR>-<BR>-static bool areAllUsesIn(Instruction *I, SmallPtrSetImpl<Instruction *> &Set) {<BR>- for(User::op_iterator Use = I->op_begin(), E = I->op_end(); Use != E; ++Use)<BR>- if (!Set.count(dyn_cast<Instruction>(*Use)))<BR>- return false;<BR>- return true;<BR>-}<BR>-<BR>-bool LoopVectorizationLegality::AddReductionVar(PHINode *Phi,<BR>- ReductionKind Kind) {<BR>- if (Phi->getNumIncomingValues() != 2)<BR>- return false;<BR>-<BR>- // Reduction variables are only found in the loop header block.<BR>- if (Phi->getParent() != TheLoop->getHeader())<BR>- return false;<BR>-<BR>- // Obtain the reduction start value from the value that comes from the loop<BR>- // preheader.<BR>- Value *RdxStart = Phi->getIncomingValueForBlock(TheLoop->getLoopPreheader());<BR>-<BR>- // ExitInstruction is the single value which is used outside the loop.<BR>- // We only allow for a single reduction value to be used outside the loop.<BR>- // This includes users of the reduction, variables (which form a cycle<BR>- // which ends in the phi node).<BR>- Instruction *ExitInstruction = nullptr;<BR>- // Indicates that we found a reduction operation in our scan.<BR>- bool FoundReduxOp = false;<BR>-<BR>- // We start with the PHI node and scan for all of the users of this<BR>- // instruction. All users must be instructions that can be used as reduction<BR>- // variables (such as ADD). We must have a single out-of-block user. The cycle<BR>- // must include the original PHI.<BR>- bool FoundStartPHI = false;<BR>-<BR>- // To recognize min/max patterns formed by a icmp select sequence, we store<BR>- // the number of instruction we saw from the recognized min/max pattern,<BR>- // to make sure we only see exactly the two instructions.<BR>- unsigned NumCmpSelectPatternInst = 0;<BR>- ReductionInstDesc ReduxDesc(false, nullptr);<BR>-<BR>- SmallPtrSet<Instruction *, 8> VisitedInsts;<BR>- SmallVector<Instruction *, 8> Worklist;<BR>- Worklist.push_back(Phi);<BR>- VisitedInsts.insert(Phi);<BR>-<BR>- // A value in the reduction can be used:<BR>- // - By the reduction:<BR>- // - Reduction operation:<BR>- // - One use of reduction value (safe).<BR>- // - Multiple use of reduction value (not safe).<BR>- // - PHI:<BR>- // - All uses of the PHI must be the reduction (safe).<BR>- // - Otherwise, not safe.<BR>- // - By one instruction outside of the loop (safe).<BR>- // - By further instructions outside of the loop (not safe).<BR>- // - By an instruction that is not part of the reduction (not safe).<BR>- // This is either:<BR>- // * An instruction type other than PHI or the reduction operation.<BR>- // * A PHI in the header other than the initial PHI.<BR>- while (!Worklist.empty()) {<BR>- Instruction *Cur = Worklist.back();<BR>- Worklist.pop_back();<BR>-<BR>- // No Users.<BR>- // If the instruction has no users then this is a broken chain and can't be<BR>- // a reduction variable.<BR>- if (Cur->use_empty())<BR>- return false;<BR>-<BR>- bool IsAPhi = isa<PHINode>(Cur);<BR>-<BR>- // A header PHI use other than the original PHI.<BR>- if (Cur != Phi && IsAPhi && Cur->getParent() == Phi->getParent())<BR>- return false;<BR>-<BR>- // Reductions of instructions such as Div, and Sub is only possible if the<BR>- // LHS is the reduction variable.<BR>- if (!Cur->isCommutative() && !IsAPhi && !isa<SelectInst>(Cur) &&<BR>- !isa<ICmpInst>(Cur) && !isa<FCmpInst>(Cur) &&<BR>- !VisitedInsts.count(dyn_cast<Instruction>(Cur->getOperand(0))))<BR>- return false;<BR>-<BR>- // Any reduction instruction must be of one of the allowed kinds.<BR>- ReduxDesc = isReductionInstr(Cur, Kind, ReduxDesc);<BR>- if (!ReduxDesc.IsReduction)<BR>- return false;<BR>-<BR>- // A reduction operation must only have one use of the reduction value.<BR>- if (!IsAPhi && Kind != RK_IntegerMinMax && Kind != RK_FloatMinMax &&<BR>- hasMultipleUsesOf(Cur, VisitedInsts))<BR>- return false;<BR>-<BR>- // All inputs to a PHI node must be a reduction value.<BR>- if(IsAPhi && Cur != Phi && !areAllUsesIn(Cur, VisitedInsts))<BR>- return false;<BR>-<BR>- if (Kind == RK_IntegerMinMax && (isa<ICmpInst>(Cur) ||<BR>- isa<SelectInst>(Cur)))<BR>- ++NumCmpSelectPatternInst;<BR>- if (Kind == RK_FloatMinMax && (isa<FCmpInst>(Cur) ||<BR>- isa<SelectInst>(Cur)))<BR>- ++NumCmpSelectPatternInst;<BR>-<BR>- // Check whether we found a reduction operator.<BR>- FoundReduxOp |= !IsAPhi;<BR>-<BR>- // Process users of current instruction. Push non-PHI nodes after PHI nodes<BR>- // onto the stack. This way we are going to have seen all inputs to PHI<BR>- // nodes once we get to them.<BR>- SmallVector<Instruction *, 8> NonPHIs;<BR>- SmallVector<Instruction *, 8> PHIs;<BR>- for (User *U : Cur->users()) {<BR>- Instruction *UI = cast<Instruction>(U);<BR>-<BR>- // Check if we found the exit user.<BR>- BasicBlock *Parent = UI->getParent();<BR>- if (!TheLoop->contains(Parent)) {<BR>- // Exit if you find multiple outside users or if the header phi node is<BR>- // being used. In this case the user uses the value of the previous<BR>- // iteration, in which case we would loose "VF-1" iterations of the<BR>- // reduction operation if we vectorize.<BR>- if (ExitInstruction != nullptr || Cur == Phi)<BR>- return false;<BR>-<BR>- // The instruction used by an outside user must be the last instruction<BR>- // before we feed back to the reduction phi. Otherwise, we loose VF-1<BR>- // operations on the value.<BR>- if (std::find(Phi->op_begin(), Phi->op_end(), Cur) == Phi->op_end())<BR>- return false;<BR>-<BR>- ExitInstruction = Cur;<BR>- continue;<BR>- }<BR>-<BR>- // Process instructions only once (termination). Each reduction cycle<BR>- // value must only be used once, except by phi nodes and min/max<BR>- // reductions which are represented as a cmp followed by a select.<BR>- ReductionInstDesc IgnoredVal(false, nullptr);<BR>- if (VisitedInsts.insert(UI).second) {<BR>- if (isa<PHINode>(UI))<BR>- PHIs.push_back(UI);<BR>- else<BR>- NonPHIs.push_back(UI);<BR>- } else if (!isa<PHINode>(UI) &&<BR>- ((!isa<FCmpInst>(UI) &&<BR>- !isa<ICmpInst>(UI) &&<BR>- !isa<SelectInst>(UI)) ||<BR>- !isMinMaxSelectCmpPattern(UI, IgnoredVal).IsReduction))<BR>- return false;<BR>-<BR>- // Remember that we completed the cycle.<BR>- if (UI == Phi)<BR>- FoundStartPHI = true;<BR>- }<BR>- Worklist.append(PHIs.begin(), PHIs.end());<BR>- Worklist.append(NonPHIs.begin(), NonPHIs.end());<BR>- }<BR>-<BR>- // This means we have seen one but not the other instruction of the<BR>- // pattern or more than just a select and cmp.<BR>- if ((Kind == RK_IntegerMinMax || Kind == RK_FloatMinMax) &&<BR>- NumCmpSelectPatternInst != 2)<BR>- return false;<BR>-<BR>- if (!FoundStartPHI || !FoundReduxOp || !ExitInstruction)<BR>- return false;<BR>-<BR>- // We found a reduction var if we have reached the original phi node and we<BR>- // only have a single instruction with out-of-loop users.<BR>-<BR>- // This instruction is allowed to have out-of-loop users.<BR>- AllowedExit.insert(ExitInstruction);<BR>-<BR>- // Save the description of this reduction variable.<BR>- ReductionDescriptor RD(RdxStart, ExitInstruction, Kind,<BR>- ReduxDesc.MinMaxKind);<BR>- Reductions[Phi] = RD;<BR>- // We've ended the cycle. This is a reduction variable if we have an<BR>- // outside user and it has a binary op.<BR>-<BR>- return true;<BR>-}<BR>-<BR>-/// Returns true if the instruction is a Select(ICmp(X, Y), X, Y) instruction<BR>-/// pattern corresponding to a min(X, Y) or max(X, Y).<BR>-LoopVectorizationLegality::ReductionInstDesc<BR>-LoopVectorizationLegality::isMinMaxSelectCmpPattern(Instruction *I,<BR>- ReductionInstDesc &Prev) {<BR>-<BR>- assert((isa<ICmpInst>(I) || isa<FCmpInst>(I) || isa<SelectInst>(I)) &&<BR>- "Expect a select instruction");<BR>- Instruction *Cmp = nullptr;<BR>- SelectInst *Select = nullptr;<BR>-<BR>- // We must handle the select(cmp()) as a single instruction. Advance to the<BR>- // select.<BR>- if ((Cmp = dyn_cast<ICmpInst>(I)) || (Cmp = dyn_cast<FCmpInst>(I))) {<BR>- if (!Cmp->hasOneUse() || !(Select = dyn_cast<SelectInst>(*I->user_begin())))<BR>- return ReductionInstDesc(false, I);<BR>- return ReductionInstDesc(Select, Prev.MinMaxKind);<BR>- }<BR>-<BR>- // Only handle single use cases for now.<BR>- if (!(Select = dyn_cast<SelectInst>(I)))<BR>- return ReductionInstDesc(false, I);<BR>- if (!(Cmp = dyn_cast<ICmpInst>(I->getOperand(0))) &&<BR>- !(Cmp = dyn_cast<FCmpInst>(I->getOperand(0))))<BR>- return ReductionInstDesc(false, I);<BR>- if (!Cmp->hasOneUse())<BR>- return ReductionInstDesc(false, I);<BR>-<BR>- Value *CmpLeft;<BR>- Value *CmpRight;<BR>-<BR>- // Look for a min/max pattern.<BR>- if (m_UMin(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>- return ReductionInstDesc(Select, MRK_UIntMin);<BR>- else if (m_UMax(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>- return ReductionInstDesc(Select, MRK_UIntMax);<BR>- else if (m_SMax(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>- return ReductionInstDesc(Select, MRK_SIntMax);<BR>- else if (m_SMin(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>- return ReductionInstDesc(Select, MRK_SIntMin);<BR>- else if (m_OrdFMin(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>- return ReductionInstDesc(Select, MRK_FloatMin);<BR>- else if (m_OrdFMax(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>- return ReductionInstDesc(Select, MRK_FloatMax);<BR>- else if (m_UnordFMin(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>- return ReductionInstDesc(Select, MRK_FloatMin);<BR>- else if (m_UnordFMax(m_Value(CmpLeft), m_Value(CmpRight)).match(Select))<BR>- return ReductionInstDesc(Select, MRK_FloatMax);<BR>-<BR>- return ReductionInstDesc(false, I);<BR>-}<BR>-<BR>-LoopVectorizationLegality::ReductionInstDesc<BR>-LoopVectorizationLegality::isReductionInstr(Instruction *I,<BR>- ReductionKind Kind,<BR>- ReductionInstDesc &Prev) {<BR>- bool FP = I->getType()->isFloatingPointTy();<BR>- bool FastMath = FP && I->hasUnsafeAlgebra();<BR>- switch (I->getOpcode()) {<BR>- default:<BR>- return ReductionInstDesc(false, I);<BR>- case Instruction::PHI:<BR>- if (FP && (Kind != RK_FloatMult && Kind != RK_FloatAdd &&<BR>- Kind != RK_FloatMinMax))<BR>- return ReductionInstDesc(false, I);<BR>- return ReductionInstDesc(I, Prev.MinMaxKind);<BR>- case Instruction::Sub:<BR>- case Instruction::Add:<BR>- return ReductionInstDesc(Kind == RK_IntegerAdd, I);<BR>- case Instruction::Mul:<BR>- return ReductionInstDesc(Kind == RK_IntegerMult, I);<BR>- case Instruction::And:<BR>- return ReductionInstDesc(Kind == RK_IntegerAnd, I);<BR>- case Instruction::Or:<BR>- return ReductionInstDesc(Kind == RK_IntegerOr, I);<BR>- case Instruction::Xor:<BR>- return ReductionInstDesc(Kind == RK_IntegerXor, I);<BR>- case Instruction::FMul:<BR>- return ReductionInstDesc(Kind == RK_FloatMult && FastMath, I);<BR>- case Instruction::FSub:<BR>- case Instruction::FAdd:<BR>- return ReductionInstDesc(Kind == RK_FloatAdd && FastMath, I);<BR>- case Instruction::FCmp:<BR>- case Instruction::ICmp:<BR>- case Instruction::Select:<BR>- if (Kind != RK_IntegerMinMax &&<BR>- (!HasFunNoNaNAttr || Kind != RK_FloatMinMax))<BR>- return ReductionInstDesc(false, I);<BR>- return isMinMaxSelectCmpPattern(I, Prev);<BR>- }<BR>-}<BR>-<BR> bool llvm::isInductionPHI(PHINode *Phi, ScalarEvolution *SE,<BR> ConstantInt *&StepValue) {<BR> Type *PhiTy = Phi->getType();<BR><BR><BR>_______________________________________________<BR>llvm-commits mailing list<BR><A href="mailto:llvm-commits@cs.uiuc.edu">llvm-commits@cs.uiuc.edu</A><BR><A href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits" target=_blank>http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits</A><BR></BLOCKQUOTE></DIV><BR></DIV>
<P> </P><!--SP:kv.bhat--><!--kv.bhat:EP-->
<P> </P>
<TABLE id=confidentialsignimg>
<TBODY>
<TR>
<TD NAMO_LOCK>
<P><IMG border=0 src="cid:BGFC2LL5XOK0@namo.co.kr"></P></TD></TR></TBODY></TABLE></BODY></HTML><img src='http://ext.samsung.net/mailcheck/SeenTimeChecker?do=deb3de73a46b1d55330a6373d2b88fd498679821d3b4431c1046eb2a7007a79e6a34984216ac66b0a34d950c0c4aaac5e1f93dc7001c940a07805447a154a46fcf878f9a26ce15a0' border=0 width=0 height=0 style='display:none'>