<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<style type="text/css" style="display:none;"><!-- P {margin-top:0;margin-bottom:0;} --></style>
</head>
<body dir="ltr">
<div id="divtagdefaultwrapper" style="font-size:12pt;color:#000000;font-family:Calibri,Helvetica,sans-serif;" dir="ltr">
<p style="margin-top:0;margin-bottom:0">Thanks Reid, I'll look into it.</p>
<p style="margin-top:0;margin-bottom:0"><br>
</p>
<div id="Signature">
<div id="divtagdefaultwrapper" dir="ltr" style="font-size:12pt; color:rgb(0,0,0); background-color:rgb(255,255,255); font-family:Calibri,Arial,Helvetica,sans-serif,EmojiFont,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Segoe UI Symbol","Android Emoji",EmojiSymbols,EmojiFont,"Apple Color Emoji","Segoe UI Emoji",NotoColorEmoji,"Segoe UI Symbol","Android Emoji",EmojiSymbols">
<p></p>
<p style="font-family:"Times New Roman""><span style="font-family:Calibri,Helvetica,sans-serif">Sam Parker</span></p>
<span style="font-family:Calibri,Helvetica,sans-serif"></span>
<p style="font-family:"Times New Roman""><span style="font-family:Calibri,Helvetica,sans-serif">Compilation Tools Engineer | Arm</span></p>
<span style="font-family:Calibri,Helvetica,sans-serif"></span>
<p style="font-family:"Times New Roman""><span style="font-family:Calibri,Helvetica,sans-serif">. . . . . . . . . . . . . . . . . . . . . . . . . . .</span></p>
<span style="font-family:Calibri,Helvetica,sans-serif"></span>
<p style="font-family:"Times New Roman""><span style="font-family:Calibri,Helvetica,sans-serif">Arm.com</span></p>
<p></p>
</div>
</div>
</div>
<hr style="display:inline-block;width:98%" tabindex="-1">
<div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>From:</b> Reid Kleckner <rnk@google.com><br>
<b>Sent:</b> 14 September 2018 20:31:01<br>
<b>To:</b> Sam Parker<br>
<b>Cc:</b> llvm-commits<br>
<b>Subject:</b> Re: [llvm] r342210 - [ARM] bottom-top mul support in ARMParallelDSP</font>
<div> </div>
</div>
<meta content="text/html; charset=utf-8">
<div>
<div dir="ltr">
<pre id="x_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="x_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="x_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="x_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="x_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>
</div>
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose,
 or store or copy the information in any medium. Thank you.
</body>
</html>