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