<div dir="ltr"><pre id="gmail-hterm:copy-to-clipboard-source" style="color:rgb(0,0,0)">$ cat convolver.cpp
int a;
char *b;
char c;
short *d;
void h() {
  for (;;) {
    int e, f, g;
    for (; g < 1; ++g, f += a)
      e += d[g] * b[f];
    c = e;
  }
}

$ ./bin/clang++ -cc1 -triple thumbv7-unknown-linux-android -emit-obj   -O2  -vectorize-loops -vectorize-slp -x c++   convolver.cpp
clang++: /usr/local/google/home/rnk/llvm-project/llvm/include/llvm/Support/Casting.h:255: typename cast_retty<X, Y *>::ret_type llvm::cast(Y *) [X = llvm::IntegerType, Y = const llvm::Type]: Assertion `isa<X>(Val) && "cast<Ty>() argument of incompatible type!"' failed.</pre></div><br><div class="gmail_quote"><div dir="ltr">On Fri, Sep 14, 2018 at 11:46 AM Reid Kleckner <<a href="mailto:rnk@google.com">rnk@google.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div dir="ltr">This caused assertion failures while building Chromium for Android, so I have reverted it and am starting a reduction:<div><a href="https://ci.chromium.org/buildbot/chromium.clang/ToTAndroid/4550" target="_blank">https://ci.chromium.org/buildbot/chromium.clang/ToTAndroid/4550</a><br></div></div></div><br><div class="gmail_quote"><div dir="ltr">On Fri, Sep 14, 2018 at 1:10 AM Sam Parker via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: sam_parker<br>
Date: Fri Sep 14 01:09:09 2018<br>
New Revision: 342210<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=342210&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=342210&view=rev</a><br>
Log:<br>
[ARM] bottom-top mul support in ARMParallelDSP<br>
<br>
On failing to find sequences that can be converted into dual macs,<br>
try to find sequential 16-bit loads that are used by muls which we<br>
can then use smultb, smulbt, smultt with a wide load.<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D51983" rel="noreferrer" target="_blank">https://reviews.llvm.org/D51983</a><br>
<br>
Added:<br>
    llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom-neg.ll<br>
    llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom.ll<br>
Modified:<br>
    llvm/trunk/lib/Target/ARM/ARMParallelDSP.cpp<br>
<br>
Modified: llvm/trunk/lib/Target/ARM/ARMParallelDSP.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/ARMParallelDSP.cpp?rev=342210&r1=342209&r2=342210&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/ARM/ARMParallelDSP.cpp?rev=342210&r1=342209&r2=342210&view=diff</a><br>
==============================================================================<br>
--- llvm/trunk/lib/Target/ARM/ARMParallelDSP.cpp (original)<br>
+++ llvm/trunk/lib/Target/ARM/ARMParallelDSP.cpp Fri Sep 14 01:09:09 2018<br>
@@ -55,6 +55,7 @@ namespace {<br>
   using ReductionList   = SmallVector<Reduction, 8>;<br>
   using ValueList       = SmallVector<Value*, 8>;<br>
   using MemInstList     = SmallVector<Instruction*, 8>;<br>
+  using LoadInstList    = SmallVector<LoadInst*, 8>;<br>
   using PMACPair        = std::pair<BinOpChain*,BinOpChain*>;<br>
   using PMACPairList    = SmallVector<PMACPair, 8>;<br>
   using Instructions    = SmallVector<Instruction*,16>;<br>
@@ -63,7 +64,8 @@ namespace {<br>
   struct OpChain {<br>
     Instruction   *Root;<br>
     ValueList     AllValues;<br>
-    MemInstList   VecLd;    // List of all load instructions.<br>
+    MemInstList   VecLd;    // List of all sequential load instructions.<br>
+    LoadInstList  Loads;    // List of all load instructions.<br>
     MemLocList    MemLocs;  // All memory locations read by this tree.<br>
     bool          ReadOnly = true;<br>
<br>
@@ -76,8 +78,10 @@ namespace {<br>
         if (auto *I = dyn_cast<Instruction>(V)) {<br>
           if (I->mayWriteToMemory())<br>
             ReadOnly = false;<br>
-          if (auto *Ld = dyn_cast<LoadInst>(V))<br>
+          if (auto *Ld = dyn_cast<LoadInst>(V)) {<br>
             MemLocs.push_back(MemoryLocation(Ld->getPointerOperand(), Size));<br>
+            Loads.push_back(Ld);<br>
+          }<br>
         }<br>
       }<br>
     }<br>
@@ -135,6 +139,7 @@ namespace {<br>
     /// exchange the halfwords of the second operand before performing the<br>
     /// arithmetic.<br>
     bool MatchSMLAD(Function &F);<br>
+    bool MatchTopBottomMuls(BasicBlock *LoopBody);<br>
<br>
   public:<br>
     static char ID;<br>
@@ -203,6 +208,8 @@ namespace {<br>
       LLVM_DEBUG(dbgs() << "\n== Parallel DSP pass ==\n");<br>
       LLVM_DEBUG(dbgs() << " - " << F.getName() << "\n\n");<br>
       Changes = MatchSMLAD(F);<br>
+      if (!Changes)<br>
+        Changes = MatchTopBottomMuls(Header);<br>
       return Changes;<br>
     }<br>
   };<br>
@@ -496,10 +503,10 @@ static void MatchReductions(Function &F,<br>
   );<br>
 }<br>
<br>
-static void AddMACCandidate(OpChainList &Candidates,<br>
+static void AddMulCandidate(OpChainList &Candidates,<br>
                             Instruction *Mul,<br>
                             Value *MulOp0, Value *MulOp1) {<br>
-  LLVM_DEBUG(dbgs() << "OK, found acc mul:\t"; Mul->dump());<br>
+  LLVM_DEBUG(dbgs() << "OK, found mul:\t"; Mul->dump());<br>
   assert(Mul->getOpcode() == Instruction::Mul &&<br>
          "expected mul instruction");<br>
   ValueList LHS;<br>
@@ -533,14 +540,14 @@ static void MatchParallelMACSequences(Re<br>
       break;<br>
     case Instruction::Mul:<br>
       if (match (I, (m_Mul(m_Value(MulOp0), m_Value(MulOp1))))) {<br>
-        AddMACCandidate(Candidates, I, MulOp0, MulOp1);<br>
+        AddMulCandidate(Candidates, I, MulOp0, MulOp1);<br>
         return false;<br>
       }<br>
       break;<br>
     case Instruction::SExt:<br>
       if (match (I, (m_SExt(m_Mul(m_Value(MulOp0), m_Value(MulOp1)))))) {<br>
         Instruction *Mul = cast<Instruction>(I->getOperand(0));<br>
-        AddMACCandidate(Candidates, Mul, MulOp0, MulOp1);<br>
+        AddMulCandidate(Candidates, Mul, MulOp0, MulOp1);<br>
         return false;<br>
       }<br>
       break;<br>
@@ -569,23 +576,24 @@ static void AliasCandidates(BasicBlock *<br>
 // the memory locations accessed by the MAC-chains.<br>
 // TODO: we need the read statements when we accept more complicated chains.<br>
 static bool AreAliased(AliasAnalysis *AA, Instructions &Reads,<br>
-                       Instructions &Writes, OpChainList &MACCandidates) {<br>
+                       Instructions &Writes, OpChainList &Candidates) {<br>
   LLVM_DEBUG(dbgs() << "Alias checks:\n");<br>
-  for (auto &MAC : MACCandidates) {<br>
-    LLVM_DEBUG(dbgs() << "mul: "; MAC->Root->dump());<br>
+  for (auto &Candidate : Candidates) {<br>
+    LLVM_DEBUG(dbgs() << "mul: "; Candidate->Root->dump());<br>
+    Candidate->SetMemoryLocations();<br>
<br>
     // At the moment, we allow only simple chains that only consist of reads,<br>
     // accumulate their result with an integer add, and thus that don't write<br>
     // memory, and simply bail if they do.<br>
-    if (!MAC->ReadOnly)<br>
+    if (!Candidate->ReadOnly)<br>
       return true;<br>
<br>
     // Now for all writes in the basic block, check that they don't alias with<br>
     // the memory locations accessed by our MAC-chain:<br>
     for (auto *I : Writes) {<br>
       LLVM_DEBUG(dbgs() << "- "; I->dump());<br>
-      assert(MAC->MemLocs.size() >= 2 && "expecting at least 2 memlocs");<br>
-      for (auto &MemLoc : MAC->MemLocs) {<br>
+      assert(Candidate->MemLocs.size() >= 2 && "expecting at least 2 memlocs");<br>
+      for (auto &MemLoc : Candidate->MemLocs) {<br>
         if (isModOrRefSet(intersectModRef(AA->getModRefInfo(I, MemLoc),<br>
                                           ModRefInfo::ModRef))) {<br>
           LLVM_DEBUG(dbgs() << "Yes, aliases found\n");<br>
@@ -599,7 +607,7 @@ static bool AreAliased(AliasAnalysis *AA<br>
   return false;<br>
 }<br>
<br>
-static bool CheckMACMemory(OpChainList &Candidates) {<br>
+static bool CheckMulMemory(OpChainList &Candidates) {<br>
   for (auto &C : Candidates) {<br>
     // A mul has 2 operands, and a narrow op consist of sext and a load; thus<br>
     // we expect at least 4 items in this operand value list.<br>
@@ -607,7 +615,6 @@ static bool CheckMACMemory(OpChainList &<br>
       LLVM_DEBUG(dbgs() << "Operand list too short.\n");<br>
       return false;<br>
     }<br>
-    C->SetMemoryLocations();<br>
     ValueList &LHS = static_cast<BinOpChain*>(C.get())->LHS;<br>
     ValueList &RHS = static_cast<BinOpChain*>(C.get())->RHS;<br>
<br>
@@ -620,6 +627,131 @@ static bool CheckMACMemory(OpChainList &<br>
   return true;<br>
 }<br>
<br>
+static LoadInst *CreateLoadIns(IRBuilder<NoFolder> &IRB, LoadInst *BaseLoad,<br>
+                               const Type *LoadTy) {<br>
+  const unsigned AddrSpace = BaseLoad->getPointerAddressSpace();<br>
+ <br>
+  Value *VecPtr = IRB.CreateBitCast(BaseLoad->getPointerOperand(),<br>
+                                     LoadTy->getPointerTo(AddrSpace));<br>
+  return IRB.CreateAlignedLoad(VecPtr, BaseLoad->getAlignment());<br>
+}<br>
+<br>
+/// Attempt to widen loads and use smulbb, smulbt, smultb and smultt muls.<br>
+// TODO: This, like smlad generation, expects the leave operands to be loads<br>
+// that are sign extended. We should be able to handle scalar values as well<br>
+// performing these muls on word x half types to generate smulwb and smulwt.<br>
+bool ARMParallelDSP::MatchTopBottomMuls(BasicBlock *LoopBody) {<br>
+  LLVM_DEBUG(dbgs() << "Attempting to find BT|TB muls.\n");<br>
+<br>
+  OpChainList Candidates;<br>
+  for (auto &I : *LoopBody) {<br>
+    if (I.getOpcode() == Instruction::Mul) {<br>
+      if (I.getType()->getScalarSizeInBits() == 32 ||<br>
+          I.getType()->getScalarSizeInBits() == 64)<br>
+      AddMulCandidate(Candidates, &I, I.getOperand(0), I.getOperand(1));<br>
+    }<br>
+  }<br>
+<br>
+  if (Candidates.empty())<br>
+    return false;<br>
+<br>
+  Instructions Reads;<br>
+  Instructions Writes;<br>
+  AliasCandidates(LoopBody, Reads, Writes);<br>
+<br>
+  if (AreAliased(AA, Reads, Writes, Candidates))<br>
+    return false;<br>
+<br>
+  DenseMap<LoadInst*, Instruction*> LoadUsers;<br>
+  DenseMap<LoadInst*, LoadInst*> SeqLoads;<br>
+  SmallPtrSet<LoadInst*, 8> OffsetLoads;<br>
+<br>
+  for (unsigned i = 0; i < Candidates.size(); ++i) {<br>
+    for (unsigned j = 0; j < Candidates.size(); ++j) {<br>
+      if (i == j)<br>
+        continue;<br>
+<br>
+      OpChain *MulChain0 = Candidates[i].get();<br>
+      OpChain *MulChain1 = Candidates[j].get();<br>
+<br>
+      for (auto *Ld0 : MulChain0->Loads) {<br>
+        if (SeqLoads.count(Ld0) || OffsetLoads.count(Ld0))<br>
+          continue;<br>
+<br>
+        for (auto *Ld1 : MulChain1->Loads) {<br>
+          if (SeqLoads.count(Ld1) || OffsetLoads.count(Ld1))<br>
+            continue;<br>
+<br>
+          MemInstList VecMem;<br>
+          if (AreSequentialLoads(Ld0, Ld1, VecMem)) {<br>
+            SeqLoads[Ld0] = Ld1;<br>
+            OffsetLoads.insert(Ld1);<br>
+            LoadUsers[Ld0] = MulChain0->Root;<br>
+            LoadUsers[Ld1] = MulChain1->Root;<br>
+          }<br>
+        }<br>
+      }<br>
+    }<br>
+  }<br>
+<br>
+  if (SeqLoads.empty())<br>
+    return false;<br>
+<br>
+  IRBuilder<NoFolder> IRB(LoopBody);<br>
+  const Type *Ty = IntegerType::get(M->getContext(), 32);<br>
+<br>
+  // We know that at least one of the operands is a SExt of Ld.<br>
+  auto GetSExt = [](Instruction *I, LoadInst *Ld, unsigned OpIdx) -> Instruction* {<br>
+    if (!isa<Instruction>(I->getOperand(OpIdx)))<br>
+      return nullptr;<br>
+<br>
+    Value *SExt = nullptr;<br>
+    if (cast<Instruction>(I->getOperand(OpIdx))->getOperand(0) == Ld)<br>
+      SExt = I->getOperand(0);<br>
+    else<br>
+      SExt = I->getOperand(1);<br>
+<br>
+    return cast<Instruction>(SExt);<br>
+  };<br>
+<br>
+  LLVM_DEBUG(dbgs() << "Found some sequential loads, now widening:\n");<br>
+  for (auto &Pair : SeqLoads) {<br>
+    LoadInst *BaseLd = Pair.first;<br>
+    LoadInst *OffsetLd = Pair.second;<br>
+    IRB.SetInsertPoint(BaseLd);<br>
+    LoadInst *WideLd = CreateLoadIns(IRB, BaseLd, Ty);<br>
+    LLVM_DEBUG(dbgs() << " - with base load: " << *BaseLd << "\n");<br>
+    LLVM_DEBUG(dbgs() << " - created wide load: " << *WideLd << "\n");<br>
+    Instruction *BaseUser = LoadUsers[BaseLd];<br>
+    Instruction *OffsetUser = LoadUsers[OffsetLd];<br>
+<br>
+    Instruction *BaseSExt = GetSExt(BaseUser, BaseLd, 0);<br>
+    if (!BaseSExt)<br>
+      BaseSExt = GetSExt(BaseUser, BaseLd, 1);<br>
+    Instruction *OffsetSExt = GetSExt(OffsetUser, OffsetLd, 0);<br>
+    if (!OffsetSExt)<br>
+      OffsetSExt = GetSExt(OffsetUser, OffsetLd, 1);<br>
+<br>
+    assert((BaseSExt && OffsetSExt) && "failed to find SExts");<br>
+<br>
+    // BaseUser needs to: (asr (shl WideLoad, 16), 16)<br>
+    // OffsetUser needs to: (asr WideLoad, 16)<br>
+    auto *Shl = cast<Instruction>(IRB.CreateShl(WideLd, 16));<br>
+    auto *Bottom = cast<Instruction>(IRB.CreateAShr(Shl, 16));<br>
+    auto *Top = cast<Instruction>(IRB.CreateAShr(WideLd, 16));<br>
+    BaseUser->replaceUsesOfWith(BaseSExt, Bottom);<br>
+    OffsetUser->replaceUsesOfWith(OffsetSExt, Top);<br>
+<br>
+    BaseSExt->eraseFromParent();<br>
+    OffsetSExt->eraseFromParent();<br>
+    BaseLd->eraseFromParent();<br>
+    OffsetLd->eraseFromParent();<br>
+  }<br>
+  LLVM_DEBUG(dbgs() << "Block after top bottom mul replacements:\n"<br>
+             << *LoopBody << "\n");<br>
+  return true;<br>
+}<br>
+<br>
 // Loop Pass that needs to identify integer add/sub reductions of 16-bit vector<br>
 // multiplications.<br>
 // To use SMLAD:<br>
@@ -658,14 +790,15 @@ bool ARMParallelDSP::MatchSMLAD(Function<br>
              dbgs() << "Header block:\n"; Header->dump();<br>
              dbgs() << "Loop info:\n\n"; L->dump());<br>
<br>
-  bool Changed = false;<br>
   ReductionList Reductions;<br>
   MatchReductions(F, L, Header, Reductions);<br>
+  if (Reductions.empty())<br>
+    return false;<br>
<br>
   for (auto &R : Reductions) {<br>
     OpChainList MACCandidates;<br>
     MatchParallelMACSequences(R, MACCandidates);<br>
-    if (!CheckMACMemory(MACCandidates))<br>
+    if (!CheckMulMemory(MACCandidates))<br>
       continue;<br>
<br>
     R.MACCandidates = std::move(MACCandidates);<br>
@@ -682,6 +815,7 @@ bool ARMParallelDSP::MatchSMLAD(Function<br>
   Instructions Reads, Writes;<br>
   AliasCandidates(Header, Reads, Writes);<br>
<br>
+  bool Changed = false;<br>
   for (auto &R : Reductions) {<br>
     if (AreAliased(AA, Reads, Writes, R.MACCandidates))<br>
       return false;<br>
@@ -693,15 +827,6 @@ bool ARMParallelDSP::MatchSMLAD(Function<br>
   return Changed;<br>
 }<br>
<br>
-static LoadInst *CreateLoadIns(IRBuilder<NoFolder> &IRB, LoadInst &BaseLoad,<br>
-                               const Type *LoadTy) {<br>
-  const unsigned AddrSpace = BaseLoad.getPointerAddressSpace();<br>
-<br>
-  Value *VecPtr = IRB.CreateBitCast(BaseLoad.getPointerOperand(),<br>
-                                    LoadTy->getPointerTo(AddrSpace));<br>
-  return IRB.CreateAlignedLoad(VecPtr, BaseLoad.getAlignment());<br>
-}<br>
-<br>
 Instruction *ARMParallelDSP::CreateSMLADCall(LoadInst *VecLd0, LoadInst *VecLd1,<br>
                                              Instruction *Acc, bool Exchange,<br>
                                              Instruction *InsertAfter) {<br>
@@ -716,8 +841,8 @@ Instruction *ARMParallelDSP::CreateSMLAD<br>
<br>
   // Replace the reduction chain with an intrinsic call<br>
   const Type *Ty = IntegerType::get(M->getContext(), 32);<br>
-  LoadInst *NewLd0 = CreateLoadIns(Builder, VecLd0[0], Ty);<br>
-  LoadInst *NewLd1 = CreateLoadIns(Builder, VecLd1[0], Ty);<br>
+  LoadInst *NewLd0 = CreateLoadIns(Builder, &VecLd0[0], Ty);<br>
+  LoadInst *NewLd1 = CreateLoadIns(Builder, &VecLd1[0], Ty);<br>
   Value* Args[] = { NewLd0, NewLd1, Acc };<br>
   Function *SMLAD = nullptr;<br>
   if (Exchange)<br>
<br>
Added: llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom-neg.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom-neg.ll?rev=342210&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom-neg.ll?rev=342210&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom-neg.ll (added)<br>
+++ llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom-neg.ll Fri Sep 14 01:09:09 2018<br>
@@ -0,0 +1,209 @@<br>
+; RUN: opt -mtriple=arm-arm-eabi -mcpu=cortex-m33 < %s -arm-parallel-dsp -S | FileCheck %s<br>
+<br>
+; CHECK-LABEL: topbottom_mul_alias<br>
+; CHECK-NOT: bitcast i16*<br>
+define void @topbottom_mul_alias(i32 %N, i32* nocapture readnone %Out, i16* nocapture readonly %In1, i16* nocapture readonly %In2) {<br>
+entry:<br>
+  br label %for.body<br>
+<br>
+for.body:<br>
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %for.body ]<br>
+  %count = phi i32 [ %N, %entry ], [ %count.next, %for.body ]<br>
+  %PIn1.0 = getelementptr inbounds i16, i16* %In1, i32 %iv<br>
+  %In1.0 = load i16, i16* %PIn1.0, align 2<br>
+  %SIn1.0 = sext i16 %In1.0 to i32<br>
+  %PIn2.0 = getelementptr inbounds i16, i16* %In2, i32 %iv<br>
+  %In2.0 = load i16, i16* %PIn2.0, align 2<br>
+  %SIn2.0 = sext i16 %In2.0 to i32<br>
+  %mul5.us.i.i = mul nsw i32 %SIn1.0, %SIn2.0<br>
+  %Out.0 = getelementptr inbounds i32, i32* %Out, i32 %iv<br>
+  store i32 %mul5.us.i.i, i32* %Out.0, align 4<br>
+  %iv.1 = or i32 %iv, 1<br>
+  %PIn1.1 = getelementptr inbounds i16, i16* %In1, i32 %iv.1<br>
+  %In1.1 = load i16, i16* %PIn1.1, align 2<br>
+  %SIn1.1 = sext i16 %In1.1 to i32<br>
+  %PIn2.1 = getelementptr inbounds i16, i16* %In2, i32 %iv.1<br>
+  %In2.1 = load i16, i16* %PIn2.1, align 2<br>
+  %SIn2.1 = sext i16 %In2.1 to i32<br>
+  %mul5.us.i.1.i = mul nsw i32 %SIn1.1, %SIn2.1<br>
+  %Out.1 = getelementptr inbounds i32, i32* %Out, i32 %iv.1<br>
+  store i32 %mul5.us.i.1.i, i32* %Out.1, align 4<br>
+  %iv.2 = or i32 %iv, 2<br>
+  %PIn1.2 = getelementptr inbounds i16, i16* %In1, i32 %iv.2<br>
+  %In1.2 = load i16, i16* %PIn1.2, align 2<br>
+  %SIn1.2 = sext i16 %In1.2 to i32<br>
+  %PIn2.2 = getelementptr inbounds i16, i16* %In2, i32 %iv.2<br>
+  %In2.2 = load i16, i16* %PIn2.2, align 2<br>
+  %SIn2.2 = sext i16 %In2.2 to i32<br>
+  %mul5.us.i.2.i = mul nsw i32 %SIn1.2, %SIn2.2<br>
+  %Out.2 = getelementptr inbounds i32, i32* %Out, i32 %iv.2<br>
+  store i32 %mul5.us.i.2.i, i32* %Out.2, align 4<br>
+  %iv.3 = or i32 %iv, 3<br>
+  %PIn1.3 = getelementptr inbounds i16, i16* %In1, i32 %iv.3<br>
+  %In1.3 = load i16, i16* %PIn1.3, align 2<br>
+  %SIn1.3 = sext i16 %In1.3 to i32<br>
+  %PIn2.3 = getelementptr inbounds i16, i16* %In2, i32 %iv.3<br>
+  %In2.3 = load i16, i16* %PIn2.3, align 2<br>
+  %SIn2.3 = sext i16 %In2.3 to i32<br>
+  %mul5.us.i.3.i = mul nsw i32 %SIn1.3, %SIn2.3<br>
+  %Out.3 = getelementptr inbounds i32, i32* %Out, i32 %iv.3<br>
+  store i32 %mul5.us.i.3.i, i32* %Out.3, align 4<br>
+  %iv.next = add i32 %iv, 4<br>
+  %count.next = add i32 %count, -4<br>
+  %niter375.ncmp.3.i = icmp eq i32 %count.next, 0<br>
+  br i1 %niter375.ncmp.3.i, label %exit, label %for.body<br>
+<br>
+exit:<br>
+  ret void<br>
+}<br>
+<br>
+; TODO: We should be able to handle this by splatting the const value.<br>
+; CHECK-LABEL: topbottom_mul_const<br>
+; CHECK-NOT: bitcast i16*<br>
+define void @topbottom_mul_const(i32 %N, i32* noalias nocapture readnone %Out, i16* nocapture readonly %In, i16 signext %const) {<br>
+entry:<br>
+  %conv4.i.i = sext i16 %const to i32<br>
+  br label %for.body<br>
+<br>
+for.body:<br>
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %for.body ]<br>
+  %count = phi i32 [ %N, %entry ], [ %count.next, %for.body ]<br>
+  %PIn.0 = getelementptr inbounds i16, i16* %In, i32 %iv<br>
+  %In.0 = load i16, i16* %PIn.0, align 2<br>
+  %conv.us.i144.i = sext i16 %In.0 to i32<br>
+  %mul5.us.i.i = mul nsw i32 %conv.us.i144.i, %conv4.i.i<br>
+  %Out.0 = getelementptr inbounds i32, i32* %Out, i32 %iv<br>
+  store i32 %mul5.us.i.i, i32* %Out.0, align 4<br>
+  %iv.1 = or i32 %iv, 1<br>
+  %PIn.1 = getelementptr inbounds i16, i16* %In, i32 %iv.1<br>
+  %In.1 = load i16, i16* %PIn.1, align 2<br>
+  %conv.us.i144.1.i = sext i16 %In.1 to i32<br>
+  %mul5.us.i.1.i = mul nsw i32 %conv.us.i144.1.i, %conv4.i.i<br>
+  %Out.1 = getelementptr inbounds i32, i32* %Out, i32 %iv.1<br>
+  store i32 %mul5.us.i.1.i, i32* %Out.1, align 4<br>
+  %iv.2 = or i32 %iv, 2<br>
+  %PIn.2 = getelementptr inbounds i16, i16* %In, i32 %iv.2<br>
+  %In.3 = load i16, i16* %PIn.2, align 2<br>
+  %conv.us.i144.2.i = sext i16 %In.3 to i32<br>
+  %mul5.us.i.2.i = mul nsw i32 %conv.us.i144.2.i, %conv4.i.i<br>
+  %Out.2 = getelementptr inbounds i32, i32* %Out, i32 %iv.2<br>
+  store i32 %mul5.us.i.2.i, i32* %Out.2, align 4<br>
+  %iv.3 = or i32 %iv, 3<br>
+  %PIn.3 = getelementptr inbounds i16, i16* %In, i32 %iv.3<br>
+  %In.4 = load i16, i16* %PIn.3, align 2<br>
+  %conv.us.i144.3.i = sext i16 %In.4 to i32<br>
+  %mul5.us.i.3.i = mul nsw i32 %conv.us.i144.3.i, %conv4.i.i<br>
+  %Out.3 = getelementptr inbounds i32, i32* %Out, i32 %iv.3<br>
+  store i32 %mul5.us.i.3.i, i32* %Out.3, align 4<br>
+  %iv.next = add i32 %iv, 4<br>
+  %count.next = add i32 %count, -4<br>
+  %niter375.ncmp.3.i = icmp eq i32 %count.next, 0<br>
+  br i1 %niter375.ncmp.3.i, label %exit, label %for.body<br>
+<br>
+exit:<br>
+  ret void<br>
+}<br>
+<br>
+; TODO: We should be able to handle this and use smulwt and smulwb.<br>
+; CHECK-LABEL: topbottom_mul_word_load_const<br>
+; CHECK-NOT: bitcast i16*<br>
+define void @topbottom_mul_word_load_const(i32 %N, i32* noalias nocapture readnone %Out, i16* nocapture readonly %In, i32* %C) {<br>
+entry:<br>
+  %const = load i32, i32* %C<br>
+  br label %for.body<br>
+<br>
+for.body:<br>
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %for.body ]<br>
+  %count = phi i32 [ %N, %entry ], [ %count.next, %for.body ]<br>
+  %PIn.0 = getelementptr inbounds i16, i16* %In, i32 %iv<br>
+  %In.0 = load i16, i16* %PIn.0, align 2<br>
+  %conv.us.i144.i = sext i16 %In.0 to i32<br>
+  %mul5.us.i.i = mul nsw i32 %conv.us.i144.i, %const<br>
+  %Out.0 = getelementptr inbounds i32, i32* %Out, i32 %iv<br>
+  store i32 %mul5.us.i.i, i32* %Out.0, align 4<br>
+  %iv.1 = or i32 %iv, 1<br>
+  %PIn.1 = getelementptr inbounds i16, i16* %In, i32 %iv.1<br>
+  %In.1 = load i16, i16* %PIn.1, align 2<br>
+  %conv.us.i144.1.i = sext i16 %In.1 to i32<br>
+  %mul5.us.i.1.i = mul nsw i32 %conv.us.i144.1.i, %const<br>
+  %Out.1 = getelementptr inbounds i32, i32* %Out, i32 %iv.1<br>
+  store i32 %mul5.us.i.1.i, i32* %Out.1, align 4<br>
+  %iv.2 = or i32 %iv, 2<br>
+  %PIn.2 = getelementptr inbounds i16, i16* %In, i32 %iv.2<br>
+  %In.3 = load i16, i16* %PIn.2, align 2<br>
+  %conv.us.i144.2.i = sext i16 %In.3 to i32<br>
+  %mul5.us.i.2.i = mul nsw i32 %conv.us.i144.2.i, %const<br>
+  %Out.2 = getelementptr inbounds i32, i32* %Out, i32 %iv.2<br>
+  store i32 %mul5.us.i.2.i, i32* %Out.2, align 4<br>
+  %iv.3 = or i32 %iv, 3<br>
+  %PIn.3 = getelementptr inbounds i16, i16* %In, i32 %iv.3<br>
+  %In.4 = load i16, i16* %PIn.3, align 2<br>
+  %conv.us.i144.3.i = sext i16 %In.4 to i32<br>
+  %mul5.us.i.3.i = mul nsw i32 %conv.us.i144.3.i, %const<br>
+  %Out.3 = getelementptr inbounds i32, i32* %Out, i32 %iv.3<br>
+  store i32 %mul5.us.i.3.i, i32* %Out.3, align 4<br>
+  %iv.next = add i32 %iv, 4<br>
+  %count.next = add i32 %count, -4<br>
+  %niter375.ncmp.3.i = icmp eq i32 %count.next, 0<br>
+  br i1 %niter375.ncmp.3.i, label %exit, label %for.body<br>
+<br>
+exit:<br>
+  ret void<br>
+}<br>
+<br>
+; CHECK-LABEL: topbottom_mul_8<br>
+; CHECK-NOT: bitcast i16*<br>
+define void @topbottom_mul_8(i32 %N, i32* noalias nocapture readnone %Out, i8* nocapture readonly %In1, i8* nocapture readonly %In2) {<br>
+entry:<br>
+  br label %for.body<br>
+<br>
+for.body:<br>
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %for.body ]<br>
+  %count = phi i32 [ %N, %entry ], [ %count.next, %for.body ]<br>
+  %PIn1.0 = getelementptr inbounds i8, i8* %In1, i32 %iv<br>
+  %In1.0 = load i8, i8* %PIn1.0, align 1<br>
+  %SIn1.0 = sext i8 %In1.0 to i32<br>
+  %PIn2.0 = getelementptr inbounds i8, i8* %In2, i32 %iv<br>
+  %In2.0 = load i8, i8* %PIn2.0, align 1<br>
+  %SIn2.0 = sext i8 %In2.0 to i32<br>
+  %mul5.us.i.i = mul nsw i32 %SIn1.0, %SIn2.0<br>
+  %Out.0 = getelementptr inbounds i32, i32* %Out, i32 %iv<br>
+  store i32 %mul5.us.i.i, i32* %Out.0, align 4<br>
+  %iv.1 = or i32 %iv, 1<br>
+  %PIn1.1 = getelementptr inbounds i8, i8* %In1, i32 %iv.1<br>
+  %In1.1 = load i8, i8* %PIn1.1, align 1<br>
+  %SIn1.1 = sext i8 %In1.1 to i32<br>
+  %PIn2.1 = getelementptr inbounds i8, i8* %In2, i32 %iv.1<br>
+  %In2.1 = load i8, i8* %PIn2.1, align 1<br>
+  %SIn2.1 = sext i8 %In2.1 to i32<br>
+  %mul5.us.i.1.i = mul nsw i32 %SIn1.1, %SIn2.1<br>
+  %Out.1 = getelementptr inbounds i32, i32* %Out, i32 %iv.1<br>
+  store i32 %mul5.us.i.1.i, i32* %Out.1, align 4<br>
+  %iv.2 = or i32 %iv, 2<br>
+  %PIn1.2 = getelementptr inbounds i8, i8* %In1, i32 %iv.2<br>
+  %In1.2 = load i8, i8* %PIn1.2, align 1<br>
+  %SIn1.2 = sext i8 %In1.2 to i32<br>
+  %PIn2.2 = getelementptr inbounds i8, i8* %In2, i32 %iv.2<br>
+  %In2.2 = load i8, i8* %PIn2.2, align 1<br>
+  %SIn2.2 = sext i8 %In2.2 to i32<br>
+  %mul5.us.i.2.i = mul nsw i32 %SIn1.2, %SIn2.2<br>
+  %Out.2 = getelementptr inbounds i32, i32* %Out, i32 %iv.2<br>
+  store i32 %mul5.us.i.2.i, i32* %Out.2, align 4<br>
+  %iv.3 = or i32 %iv, 3<br>
+  %PIn1.3 = getelementptr inbounds i8, i8* %In1, i32 %iv.3<br>
+  %In1.3 = load i8, i8* %PIn1.3, align 1<br>
+  %SIn1.3 = sext i8 %In1.3 to i32<br>
+  %PIn2.3 = getelementptr inbounds i8, i8* %In2, i32 %iv.3<br>
+  %In2.3 = load i8, i8* %PIn2.3, align 1<br>
+  %SIn2.3 = sext i8 %In2.3 to i32<br>
+  %mul5.us.i.3.i = mul nsw i32 %SIn1.3, %SIn2.3<br>
+  %Out.3 = getelementptr inbounds i32, i32* %Out, i32 %iv.3<br>
+  store i32 %mul5.us.i.3.i, i32* %Out.3, align 4<br>
+  %iv.next = add i32 %iv, 4<br>
+  %count.next = add i32 %count, -4<br>
+  %niter375.ncmp.3.i = icmp eq i32 %count.next, 0<br>
+  br i1 %niter375.ncmp.3.i, label %exit, label %for.body<br>
+<br>
+exit:<br>
+  ret void<br>
+}<br>
<br>
Added: llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom.ll<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom.ll?rev=342210&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom.ll?rev=342210&view=auto</a><br>
==============================================================================<br>
--- llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom.ll (added)<br>
+++ llvm/trunk/test/CodeGen/ARM/paralleldsp-top-bottom.ll Fri Sep 14 01:09:09 2018<br>
@@ -0,0 +1,251 @@<br>
+; RUN: opt -mtriple=arm-arm-eabi -mcpu=cortex-m33 < %s -arm-parallel-dsp -S | FileCheck %s<br>
+<br>
+; CHECK-LABEL: topbottom_mul<br>
+define void @topbottom_mul(i32 %N, i32* noalias nocapture readnone %Out, i16* nocapture readonly %In1, i16* nocapture readonly %In2) {<br>
+entry:<br>
+  br label %for.body<br>
+<br>
+; CHECK: for.body:<br>
+; CHECK: [[Cast_PIn1_0:%[^ ]+]] = bitcast i16* %PIn1.0 to i32*<br>
+; CHECK: [[PIn1_01:%[^ ]+]] = load i32, i32* [[Cast_PIn1_0]], align 2<br>
+; CHECK: [[PIn1_01_shl:%[^ ]+]] = shl i32 [[PIn1_01]], 16<br>
+; CHECK: [[PIn1_0:%[^ ]+]] = ashr i32 [[PIn1_01_shl]], 16<br>
+; CHECK: [[PIn1_1:%[^ ]+]] = ashr i32 [[PIn1_01]], 16<br>
+<br>
+; CHECK: [[Cast_PIn2_0:%[^ ]+]] = bitcast i16* %PIn2.0 to i32*<br>
+; CHECK: [[PIn2_01:%[^ ]+]] = load i32, i32* [[Cast_PIn2_0]], align 2<br>
+; CHECK: [[PIn2_01_shl:%[^ ]+]] = shl i32 [[PIn2_01]], 16<br>
+; CHECK: [[PIn2_0:%[^ ]+]] = ashr i32 [[PIn2_01_shl]], 16<br>
+; CHECK: [[PIn2_1:%[^ ]+]] = ashr i32 [[PIn2_01]], 16<br>
+<br>
+; CHECK: mul nsw i32 [[PIn1_0]], [[PIn2_0]]<br>
+; CHECK: mul nsw i32 [[PIn1_1]], [[PIn2_1]]<br>
+<br>
+; CHECK: [[Cast_PIn1_2:%[^ ]+]] = bitcast i16* %PIn1.2 to i32*<br>
+; CHECK: [[PIn1_23:%[^ ]+]] = load i32, i32* [[Cast_PIn1_2]], align 2<br>
+; CHECK: [[PIn1_23_shl:%[^ ]+]] = shl i32 [[PIn1_23]], 16<br>
+; CHECK: [[PIn1_2:%[^ ]+]] = ashr i32 [[PIn1_23_shl]], 16<br>
+; CHECK: [[PIn1_3:%[^ ]+]] = ashr i32 [[PIn1_23]], 16<br>
+<br>
+; CHECK: [[Cast_PIn2_2:%[^ ]+]] = bitcast i16* %PIn2.2 to i32*<br>
+; CHECK: [[PIn2_23:%[^ ]+]] = load i32, i32* [[Cast_PIn2_2]], align 2<br>
+; CHECK: [[PIn2_23_shl:%[^ ]+]] = shl i32 [[PIn2_23]], 16<br>
+; CHECK: [[PIn2_2:%[^ ]+]] = ashr i32 [[PIn2_23_shl]], 16<br>
+; CHECK: [[PIn2_3:%[^ ]+]] = ashr i32 [[PIn2_23]], 16<br>
+<br>
+; CHECK: mul nsw i32 [[PIn1_2]], [[PIn2_2]]<br>
+; CHECK: mul nsw i32 [[PIn1_3]], [[PIn2_3]]<br>
+<br>
+for.body:<br>
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %for.body ]<br>
+  %count = phi i32 [ %N, %entry ], [ %count.next, %for.body ]<br>
+  %PIn1.0 = getelementptr inbounds i16, i16* %In1, i32 %iv<br>
+  %In1.0 = load i16, i16* %PIn1.0, align 2<br>
+  %SIn1.0 = sext i16 %In1.0 to i32<br>
+  %PIn2.0 = getelementptr inbounds i16, i16* %In2, i32 %iv<br>
+  %In2.0 = load i16, i16* %PIn2.0, align 2<br>
+  %SIn2.0 = sext i16 %In2.0 to i32<br>
+  %mul5.us.i.i = mul nsw i32 %SIn1.0, %SIn2.0<br>
+  %Out.0 = getelementptr inbounds i32, i32* %Out, i32 %iv<br>
+  store i32 %mul5.us.i.i, i32* %Out.0, align 4<br>
+  %iv.1 = or i32 %iv, 1<br>
+  %PIn1.1 = getelementptr inbounds i16, i16* %In1, i32 %iv.1<br>
+  %In1.1 = load i16, i16* %PIn1.1, align 2<br>
+  %SIn1.1 = sext i16 %In1.1 to i32<br>
+  %PIn2.1 = getelementptr inbounds i16, i16* %In2, i32 %iv.1<br>
+  %In2.1 = load i16, i16* %PIn2.1, align 2<br>
+  %SIn2.1 = sext i16 %In2.1 to i32<br>
+  %mul5.us.i.1.i = mul nsw i32 %SIn1.1, %SIn2.1<br>
+  %Out.1 = getelementptr inbounds i32, i32* %Out, i32 %iv.1<br>
+  store i32 %mul5.us.i.1.i, i32* %Out.1, align 4<br>
+  %iv.2 = or i32 %iv, 2<br>
+  %PIn1.2 = getelementptr inbounds i16, i16* %In1, i32 %iv.2<br>
+  %In1.2 = load i16, i16* %PIn1.2, align 2<br>
+  %SIn1.2 = sext i16 %In1.2 to i32<br>
+  %PIn2.2 = getelementptr inbounds i16, i16* %In2, i32 %iv.2<br>
+  %In2.2 = load i16, i16* %PIn2.2, align 2<br>
+  %SIn2.2 = sext i16 %In2.2 to i32<br>
+  %mul5.us.i.2.i = mul nsw i32 %SIn1.2, %SIn2.2<br>
+  %Out.2 = getelementptr inbounds i32, i32* %Out, i32 %iv.2<br>
+  store i32 %mul5.us.i.2.i, i32* %Out.2, align 4<br>
+  %iv.3 = or i32 %iv, 3<br>
+  %PIn1.3 = getelementptr inbounds i16, i16* %In1, i32 %iv.3<br>
+  %In1.3 = load i16, i16* %PIn1.3, align 2<br>
+  %SIn1.3 = sext i16 %In1.3 to i32<br>
+  %PIn2.3 = getelementptr inbounds i16, i16* %In2, i32 %iv.3<br>
+  %In2.3 = load i16, i16* %PIn2.3, align 2<br>
+  %SIn2.3 = sext i16 %In2.3 to i32<br>
+  %mul5.us.i.3.i = mul nsw i32 %SIn1.3, %SIn2.3<br>
+  %Out.3 = getelementptr inbounds i32, i32* %Out, i32 %iv.3<br>
+  store i32 %mul5.us.i.3.i, i32* %Out.3, align 4<br>
+  %iv.next = add i32 %iv, 4<br>
+  %count.next = add i32 %count, -4<br>
+  %niter375.ncmp.3.i = icmp eq i32 %count.next, 0<br>
+  br i1 %niter375.ncmp.3.i, label %exit, label %for.body<br>
+<br>
+exit:<br>
+  ret void<br>
+}<br>
+<br>
+; CHECK-LABEL: topbottom_mul_load_const<br>
+define void @topbottom_mul_load_const(i32 %N, i32* noalias nocapture readnone %Out, i16* nocapture readonly %In, i16* %C) {<br>
+entry:<br>
+  %const = load i16, i16* %C<br>
+  %conv4.i.i = sext i16 %const to i32<br>
+  br label %for.body<br>
+<br>
+; CHECK: for.body:<br>
+; CHECK: [[Cast_PIn_0:%[^ ]+]] = bitcast i16* %PIn.0 to i32*<br>
+; CHECK: [[PIn_01:%[^ ]+]] = load i32, i32* [[Cast_PIn_0]], align 2<br>
+; CHECK: [[PIn_01_shl:%[^ ]+]] = shl i32 [[PIn_01]], 16<br>
+; CHECK: [[PIn_0:%[^ ]+]] = ashr i32 [[PIn_01_shl]], 16<br>
+; CHECK: [[PIn_1:%[^ ]+]] = ashr i32 [[PIn_01]], 16<br>
+<br>
+; CHECK: mul nsw i32 [[PIn_0]], %conv4.i.i<br>
+; CHECK: mul nsw i32 [[PIn_1]], %conv4.i.i<br>
+<br>
+; CHECK: [[Cast_PIn_2:%[^ ]+]] = bitcast i16* %PIn.2 to i32*<br>
+; CHECK: [[PIn_23:%[^ ]+]] = load i32, i32* [[Cast_PIn_2]], align 2<br>
+; CHECK: [[PIn_23_shl:%[^ ]+]] = shl i32 [[PIn_23]], 16<br>
+; CHECK: [[PIn_2:%[^ ]+]] = ashr i32 [[PIn_23_shl]], 16<br>
+; CHECK: [[PIn_3:%[^ ]+]] = ashr i32 [[PIn_23]], 16<br>
+<br>
+; CHECK: mul nsw i32 [[PIn_2]], %conv4.i.i<br>
+; CHECK: mul nsw i32 [[PIn_3]], %conv4.i.i<br>
+<br>
+for.body:<br>
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %for.body ]<br>
+  %count = phi i32 [ %N, %entry ], [ %count.next, %for.body ]<br>
+  %PIn.0 = getelementptr inbounds i16, i16* %In, i32 %iv<br>
+  %In.0 = load i16, i16* %PIn.0, align 2<br>
+  %conv.us.i144.i = sext i16 %In.0 to i32<br>
+  %mul5.us.i.i = mul nsw i32 %conv.us.i144.i, %conv4.i.i<br>
+  %Out.0 = getelementptr inbounds i32, i32* %Out, i32 %iv<br>
+  store i32 %mul5.us.i.i, i32* %Out.0, align 4<br>
+  %iv.1 = or i32 %iv, 1<br>
+  %PIn.1 = getelementptr inbounds i16, i16* %In, i32 %iv.1<br>
+  %In.1 = load i16, i16* %PIn.1, align 2<br>
+  %conv.us.i144.1.i = sext i16 %In.1 to i32<br>
+  %mul5.us.i.1.i = mul nsw i32 %conv.us.i144.1.i, %conv4.i.i<br>
+  %Out.1 = getelementptr inbounds i32, i32* %Out, i32 %iv.1<br>
+  store i32 %mul5.us.i.1.i, i32* %Out.1, align 4<br>
+  %iv.2 = or i32 %iv, 2<br>
+  %PIn.2 = getelementptr inbounds i16, i16* %In, i32 %iv.2<br>
+  %In.3 = load i16, i16* %PIn.2, align 2<br>
+  %conv.us.i144.2.i = sext i16 %In.3 to i32<br>
+  %mul5.us.i.2.i = mul nsw i32 %conv.us.i144.2.i, %conv4.i.i<br>
+  %Out.2 = getelementptr inbounds i32, i32* %Out, i32 %iv.2<br>
+  store i32 %mul5.us.i.2.i, i32* %Out.2, align 4<br>
+  %iv.3 = or i32 %iv, 3<br>
+  %PIn.3 = getelementptr inbounds i16, i16* %In, i32 %iv.3<br>
+  %In.4 = load i16, i16* %PIn.3, align 2<br>
+  %conv.us.i144.3.i = sext i16 %In.4 to i32<br>
+  %mul5.us.i.3.i = mul nsw i32 %conv.us.i144.3.i, %conv4.i.i<br>
+  %Out.3 = getelementptr inbounds i32, i32* %Out, i32 %iv.3<br>
+  store i32 %mul5.us.i.3.i, i32* %Out.3, align 4<br>
+  %iv.next = add i32 %iv, 4<br>
+  %count.next = add i32 %count, -4<br>
+  %niter375.ncmp.3.i = icmp eq i32 %count.next, 0<br>
+  br i1 %niter375.ncmp.3.i, label %exit, label %for.body<br>
+<br>
+exit:<br>
+  ret void<br>
+}<br>
+<br>
+; CHECK-LABEL: topbottom_mul_64<br>
+define void @topbottom_mul_64(i32 %N, i64* noalias nocapture readnone %Out, i16* nocapture readonly %In1, i16* nocapture readonly %In2) {<br>
+entry:<br>
+  br label %for.body<br>
+<br>
+; CHECK: for.body:<br>
+; CHECK: [[Cast_PIn1_0:%[^ ]+]] = bitcast i16* %PIn1.0 to i32*<br>
+; CHECK: [[PIn1_01:%[^ ]+]] = load i32, i32* [[Cast_PIn1_0]], align 2<br>
+; CHECK: [[PIn1_01_shl:%[^ ]+]] = shl i32 [[PIn1_01]], 16<br>
+; CHECK: [[PIn1_0:%[^ ]+]] = ashr i32 [[PIn1_01_shl]], 16<br>
+; CHECK: [[PIn1_1:%[^ ]+]] = ashr i32 [[PIn1_01]], 16<br>
+<br>
+; CHECK: [[Cast_PIn2_0:%[^ ]+]] = bitcast i16* %PIn2.0 to i32*<br>
+; CHECK: [[PIn2_01:%[^ ]+]] = load i32, i32* [[Cast_PIn2_0]], align 2<br>
+; CHECK: [[PIn2_01_shl:%[^ ]+]] = shl i32 [[PIn2_01]], 16<br>
+; CHECK: [[PIn2_0:%[^ ]+]] = ashr i32 [[PIn2_01_shl]], 16<br>
+; CHECK: [[PIn2_1:%[^ ]+]] = ashr i32 [[PIn2_01]], 16<br>
+<br>
+; CHECK: [[Mul0:%[^ ]+]] = mul nsw i32 [[PIn1_0]], [[PIn2_0]]<br>
+; CHECK: [[SMul0:%[^ ]+]] = sext i32 [[Mul0]] to i64<br>
+; CHECK: [[Mul1:%[^ ]+]] = mul nsw i32 [[PIn1_1]], [[PIn2_1]]<br>
+; CHECK: [[SMul1:%[^ ]+]] = sext i32 [[Mul1]] to i64<br>
+; CHECK: add i64 [[SMul0]], [[SMul1]]<br>
+<br>
+; CHECK: [[Cast_PIn1_2:%[^ ]+]] = bitcast i16* %PIn1.2 to i32*<br>
+; CHECK: [[PIn1_23:%[^ ]+]] = load i32, i32* [[Cast_PIn1_2]], align 2<br>
+; CHECK: [[PIn1_23_shl:%[^ ]+]] = shl i32 [[PIn1_23]], 16<br>
+; CHECK: [[PIn1_2:%[^ ]+]] = ashr i32 [[PIn1_23_shl]], 16<br>
+; CHECK: [[PIn1_3:%[^ ]+]] = ashr i32 [[PIn1_23]], 16<br>
+<br>
+; CHECK: [[Cast_PIn2_2:%[^ ]+]] = bitcast i16* %PIn2.2 to i32*<br>
+; CHECK: [[PIn2_23:%[^ ]+]] = load i32, i32* [[Cast_PIn2_2]], align 2<br>
+; CHECK: [[PIn2_23_shl:%[^ ]+]] = shl i32 [[PIn2_23]], 16<br>
+; CHECK: [[PIn2_2:%[^ ]+]] = ashr i32 [[PIn2_23_shl]], 16<br>
+; CHECK: [[PIn2_3:%[^ ]+]] = ashr i32 [[PIn2_23]], 16<br>
+<br>
+; CHECK: [[Mul2:%[^ ]+]] = mul nsw i32 [[PIn1_2]], [[PIn2_2]]<br>
+; CHECK: [[SMul2:%[^ ]+]] = sext i32 [[Mul2]] to i64<br>
+; CHECK: [[Mul3:%[^ ]+]] = mul nsw i32 [[PIn1_3]], [[PIn2_3]]<br>
+; CHECK: [[SMul3:%[^ ]+]] = sext i32 [[Mul3]] to i64<br>
+; CHECK: add i64 [[SMul2]], [[SMul3]]<br>
+<br>
+for.body:<br>
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %for.body ]<br>
+  %iv.out = phi i32 [ 0, %entry] , [ %iv.out.next, %for.body ]<br>
+  %count = phi i32 [ %N, %entry ], [ %count.next, %for.body ]<br>
+  %PIn1.0 = getelementptr inbounds i16, i16* %In1, i32 %iv<br>
+  %In1.0 = load i16, i16* %PIn1.0, align 2<br>
+  %SIn1.0 = sext i16 %In1.0 to i32<br>
+  %PIn2.0 = getelementptr inbounds i16, i16* %In2, i32 %iv<br>
+  %In2.0 = load i16, i16* %PIn2.0, align 2<br>
+  %SIn2.0 = sext i16 %In2.0 to i32<br>
+  %mul5.us.i.i = mul nsw i32 %SIn1.0, %SIn2.0<br>
+  %sext.0 = sext i32 %mul5.us.i.i to i64<br>
+  %iv.1 = or i32 %iv, 1<br>
+  %PIn1.1 = getelementptr inbounds i16, i16* %In1, i32 %iv.1<br>
+  %In1.1 = load i16, i16* %PIn1.1, align 2<br>
+  %SIn1.1 = sext i16 %In1.1 to i32<br>
+  %PIn2.1 = getelementptr inbounds i16, i16* %In2, i32 %iv.1<br>
+  %In2.1 = load i16, i16* %PIn2.1, align 2<br>
+  %SIn2.1 = sext i16 %In2.1 to i32<br>
+  %mul5.us.i.1.i = mul nsw i32 %SIn1.1, %SIn2.1<br>
+  %sext.1 = sext i32 %mul5.us.i.1.i to i64<br>
+  %mac.0 = add i64 %sext.0, %sext.1<br>
+  %Out.0 = getelementptr inbounds i64, i64* %Out, i32 %iv.out<br>
+  store i64 %mac.0, i64* %Out.0, align 4<br>
+  %iv.2 = or i32 %iv, 2<br>
+  %PIn1.2 = getelementptr inbounds i16, i16* %In1, i32 %iv.2<br>
+  %In1.2 = load i16, i16* %PIn1.2, align 2<br>
+  %SIn1.2 = sext i16 %In1.2 to i32<br>
+  %PIn2.2 = getelementptr inbounds i16, i16* %In2, i32 %iv.2<br>
+  %In2.2 = load i16, i16* %PIn2.2, align 2<br>
+  %SIn2.2 = sext i16 %In2.2 to i32<br>
+  %mul5.us.i.2.i = mul nsw i32 %SIn1.2, %SIn2.2<br>
+  %sext.2 = sext i32 %mul5.us.i.2.i to i64<br>
+  %iv.3 = or i32 %iv, 3<br>
+  %PIn1.3 = getelementptr inbounds i16, i16* %In1, i32 %iv.3<br>
+  %In1.3 = load i16, i16* %PIn1.3, align 2<br>
+  %SIn1.3 = sext i16 %In1.3 to i32<br>
+  %PIn2.3 = getelementptr inbounds i16, i16* %In2, i32 %iv.3<br>
+  %In2.3 = load i16, i16* %PIn2.3, align 2<br>
+  %SIn2.3 = sext i16 %In2.3 to i32<br>
+  %mul5.us.i.3.i = mul nsw i32 %SIn1.3, %SIn2.3<br>
+  %sext.3 = sext i32 %mul5.us.i.3.i to i64<br>
+  %mac.1 = add i64 %sext.2, %sext.3<br>
+  %iv.out.1 = or i32 %iv.out, 1<br>
+  %Out.1 = getelementptr inbounds i64, i64* %Out, i32 %iv.out.1<br>
+  store i64 %mac.1, i64* %Out.1, align 4<br>
+  %iv.next = add i32 %iv, 4<br>
+  %iv.out.next = add i32 %iv.out, 2<br>
+  %count.next = add i32 %count, -4<br>
+  %niter375.ncmp.3.i = icmp eq i32 %count.next, 0<br>
+  br i1 %niter375.ncmp.3.i, label %exit, label %for.body<br>
+<br>
+exit:<br>
+  ret void<br>
+}<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="http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div>
</blockquote></div>