1*7b6f760fSDavid Green //===- MVELaneInterleaving.cpp - Inverleave for MVE instructions ----------===//
2*7b6f760fSDavid Green //
3*7b6f760fSDavid Green // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*7b6f760fSDavid Green // See https://llvm.org/LICENSE.txt for license information.
5*7b6f760fSDavid Green // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*7b6f760fSDavid Green //
7*7b6f760fSDavid Green //===----------------------------------------------------------------------===//
8*7b6f760fSDavid Green //
9*7b6f760fSDavid Green // This pass interleaves around sext/zext/trunc instructions. MVE does not have
10*7b6f760fSDavid Green // a single sext/zext or trunc instruction that takes the bottom half of a
11*7b6f760fSDavid Green // vector and extends to a full width, like NEON has with MOVL. Instead it is
12*7b6f760fSDavid Green // expected that this happens through top/bottom instructions. So the MVE
13*7b6f760fSDavid Green // equivalent VMOVLT/B instructions take either the even or odd elements of the
14*7b6f760fSDavid Green // input and extend them to the larger type, producing a vector with half the
15*7b6f760fSDavid Green // number of elements each of double the bitwidth. As there is no simple
16*7b6f760fSDavid Green // instruction, we often have to turn sext/zext/trunc into a series of lane
17*7b6f760fSDavid Green // moves (or stack loads/stores, which we do not do yet).
18*7b6f760fSDavid Green //
19*7b6f760fSDavid Green // This pass takes vector code that starts at truncs, looks for interconnected
20*7b6f760fSDavid Green // blobs of operations that end with sext/zext (or constants/splats) of the
21*7b6f760fSDavid Green // form:
22*7b6f760fSDavid Green //   %sa = sext v8i16 %a to v8i32
23*7b6f760fSDavid Green //   %sb = sext v8i16 %b to v8i32
24*7b6f760fSDavid Green //   %add = add v8i32 %sa, %sb
25*7b6f760fSDavid Green //   %r = trunc %add to v8i16
26*7b6f760fSDavid Green // And adds shuffles to allow the use of VMOVL/VMOVN instrctions:
27*7b6f760fSDavid Green //   %sha = shuffle v8i16 %a, undef, <0, 2, 4, 6, 1, 3, 5, 7>
28*7b6f760fSDavid Green //   %sa = sext v8i16 %sha to v8i32
29*7b6f760fSDavid Green //   %shb = shuffle v8i16 %b, undef, <0, 2, 4, 6, 1, 3, 5, 7>
30*7b6f760fSDavid Green //   %sb = sext v8i16 %shb to v8i32
31*7b6f760fSDavid Green //   %add = add v8i32 %sa, %sb
32*7b6f760fSDavid Green //   %r = trunc %add to v8i16
33*7b6f760fSDavid Green //   %shr = shuffle v8i16 %r, undef, <0, 4, 1, 5, 2, 6, 3, 7>
34*7b6f760fSDavid Green // Which can then be split and lowered to MVE instructions efficiently:
35*7b6f760fSDavid Green //   %sa_b = VMOVLB.s16 %a
36*7b6f760fSDavid Green //   %sa_t = VMOVLT.s16 %a
37*7b6f760fSDavid Green //   %sb_b = VMOVLB.s16 %b
38*7b6f760fSDavid Green //   %sb_t = VMOVLT.s16 %b
39*7b6f760fSDavid Green //   %add_b = VADD.i32 %sa_b, %sb_b
40*7b6f760fSDavid Green //   %add_t = VADD.i32 %sa_t, %sb_t
41*7b6f760fSDavid Green //   %r = VMOVNT.i16 %add_b, %add_t
42*7b6f760fSDavid Green //
43*7b6f760fSDavid Green //===----------------------------------------------------------------------===//
44*7b6f760fSDavid Green 
45*7b6f760fSDavid Green #include "ARM.h"
46*7b6f760fSDavid Green #include "ARMBaseInstrInfo.h"
47*7b6f760fSDavid Green #include "ARMSubtarget.h"
48*7b6f760fSDavid Green #include "llvm/Analysis/TargetTransformInfo.h"
49*7b6f760fSDavid Green #include "llvm/CodeGen/TargetLowering.h"
50*7b6f760fSDavid Green #include "llvm/CodeGen/TargetPassConfig.h"
51*7b6f760fSDavid Green #include "llvm/CodeGen/TargetSubtargetInfo.h"
52*7b6f760fSDavid Green #include "llvm/IR/BasicBlock.h"
53*7b6f760fSDavid Green #include "llvm/IR/Constant.h"
54*7b6f760fSDavid Green #include "llvm/IR/Constants.h"
55*7b6f760fSDavid Green #include "llvm/IR/DerivedTypes.h"
56*7b6f760fSDavid Green #include "llvm/IR/Function.h"
57*7b6f760fSDavid Green #include "llvm/IR/IRBuilder.h"
58*7b6f760fSDavid Green #include "llvm/IR/InstIterator.h"
59*7b6f760fSDavid Green #include "llvm/IR/InstrTypes.h"
60*7b6f760fSDavid Green #include "llvm/IR/Instruction.h"
61*7b6f760fSDavid Green #include "llvm/IR/Instructions.h"
62*7b6f760fSDavid Green #include "llvm/IR/IntrinsicInst.h"
63*7b6f760fSDavid Green #include "llvm/IR/Intrinsics.h"
64*7b6f760fSDavid Green #include "llvm/IR/IntrinsicsARM.h"
65*7b6f760fSDavid Green #include "llvm/IR/PatternMatch.h"
66*7b6f760fSDavid Green #include "llvm/IR/Type.h"
67*7b6f760fSDavid Green #include "llvm/IR/Value.h"
68*7b6f760fSDavid Green #include "llvm/InitializePasses.h"
69*7b6f760fSDavid Green #include "llvm/Pass.h"
70*7b6f760fSDavid Green #include "llvm/Support/Casting.h"
71*7b6f760fSDavid Green #include <algorithm>
72*7b6f760fSDavid Green #include <cassert>
73*7b6f760fSDavid Green 
74*7b6f760fSDavid Green using namespace llvm;
75*7b6f760fSDavid Green 
76*7b6f760fSDavid Green #define DEBUG_TYPE "mve-laneinterleave"
77*7b6f760fSDavid Green 
78*7b6f760fSDavid Green cl::opt<bool> EnableInterleave(
79*7b6f760fSDavid Green     "enable-mve-interleave", cl::Hidden, cl::init(true),
80*7b6f760fSDavid Green     cl::desc("Enable interleave MVE vector operation lowering"));
81*7b6f760fSDavid Green 
82*7b6f760fSDavid Green namespace {
83*7b6f760fSDavid Green 
84*7b6f760fSDavid Green class MVELaneInterleaving : public FunctionPass {
85*7b6f760fSDavid Green public:
86*7b6f760fSDavid Green   static char ID; // Pass identification, replacement for typeid
87*7b6f760fSDavid Green 
88*7b6f760fSDavid Green   explicit MVELaneInterleaving() : FunctionPass(ID) {
89*7b6f760fSDavid Green     initializeMVELaneInterleavingPass(*PassRegistry::getPassRegistry());
90*7b6f760fSDavid Green   }
91*7b6f760fSDavid Green 
92*7b6f760fSDavid Green   bool runOnFunction(Function &F) override;
93*7b6f760fSDavid Green 
94*7b6f760fSDavid Green   StringRef getPassName() const override { return "MVE lane interleaving"; }
95*7b6f760fSDavid Green 
96*7b6f760fSDavid Green   void getAnalysisUsage(AnalysisUsage &AU) const override {
97*7b6f760fSDavid Green     AU.setPreservesCFG();
98*7b6f760fSDavid Green     AU.addRequired<TargetPassConfig>();
99*7b6f760fSDavid Green     FunctionPass::getAnalysisUsage(AU);
100*7b6f760fSDavid Green   }
101*7b6f760fSDavid Green };
102*7b6f760fSDavid Green 
103*7b6f760fSDavid Green } // end anonymous namespace
104*7b6f760fSDavid Green 
105*7b6f760fSDavid Green char MVELaneInterleaving::ID = 0;
106*7b6f760fSDavid Green 
107*7b6f760fSDavid Green INITIALIZE_PASS(MVELaneInterleaving, DEBUG_TYPE, "MVE lane interleaving", false,
108*7b6f760fSDavid Green                 false)
109*7b6f760fSDavid Green 
110*7b6f760fSDavid Green Pass *llvm::createMVELaneInterleavingPass() {
111*7b6f760fSDavid Green   return new MVELaneInterleaving();
112*7b6f760fSDavid Green }
113*7b6f760fSDavid Green 
114*7b6f760fSDavid Green static bool isProfitableToInterleave(SmallSetVector<Instruction *, 4> &Exts,
115*7b6f760fSDavid Green                                      SmallSetVector<Instruction *, 4> &Truncs) {
116*7b6f760fSDavid Green   // This is not always beneficial to transform. Exts can be incorporated into
117*7b6f760fSDavid Green   // loads, Truncs can be folded into stores.
118*7b6f760fSDavid Green   // Truncs are usually the same number of instructions,
119*7b6f760fSDavid Green   //  VSTRH.32(A);VSTRH.32(B) vs VSTRH.16(VMOVNT A, B) with interleaving
120*7b6f760fSDavid Green   // Exts are unfortunately more instructions in the general case:
121*7b6f760fSDavid Green   //  A=VLDRH.32; B=VLDRH.32;
122*7b6f760fSDavid Green   // vs with interleaving:
123*7b6f760fSDavid Green   //  T=VLDRH.16; A=VMOVNB T; B=VMOVNT T
124*7b6f760fSDavid Green   // But those VMOVL may be folded into a VMULL.
125*7b6f760fSDavid Green 
126*7b6f760fSDavid Green   // But expensive extends/truncs are always good to remove.
127*7b6f760fSDavid Green   for (auto *E : Exts)
128*7b6f760fSDavid Green     if (!isa<LoadInst>(E->getOperand(0))) {
129*7b6f760fSDavid Green       LLVM_DEBUG(dbgs() << "Beneficial due to " << *E << "\n");
130*7b6f760fSDavid Green       return true;
131*7b6f760fSDavid Green     }
132*7b6f760fSDavid Green   for (auto *T : Truncs)
133*7b6f760fSDavid Green     if (T->hasOneUse() && !isa<StoreInst>(*T->user_begin())) {
134*7b6f760fSDavid Green       LLVM_DEBUG(dbgs() << "Beneficial due to " << *T << "\n");
135*7b6f760fSDavid Green       return true;
136*7b6f760fSDavid Green     }
137*7b6f760fSDavid Green 
138*7b6f760fSDavid Green   // Otherwise, we know we have a load(ext), see if any of the Extends are a
139*7b6f760fSDavid Green   // vmull. This is a simple heuristic and certainly not perfect.
140*7b6f760fSDavid Green   for (auto *E : Exts) {
141*7b6f760fSDavid Green     if (!E->hasOneUse() ||
142*7b6f760fSDavid Green         cast<Instruction>(*E->user_begin())->getOpcode() != Instruction::Mul) {
143*7b6f760fSDavid Green       LLVM_DEBUG(dbgs() << "Not beneficial due to " << *E << "\n");
144*7b6f760fSDavid Green       return false;
145*7b6f760fSDavid Green     }
146*7b6f760fSDavid Green   }
147*7b6f760fSDavid Green   return true;
148*7b6f760fSDavid Green }
149*7b6f760fSDavid Green 
150*7b6f760fSDavid Green static bool tryInterleave(Instruction *Start,
151*7b6f760fSDavid Green                           SmallPtrSetImpl<Instruction *> &Visited) {
152*7b6f760fSDavid Green   LLVM_DEBUG(dbgs() << "tryInterleave from " << *Start << "\n");
153*7b6f760fSDavid Green   auto *VT = cast<FixedVectorType>(Start->getType());
154*7b6f760fSDavid Green 
155*7b6f760fSDavid Green   if (!isa<Instruction>(Start->getOperand(0)))
156*7b6f760fSDavid Green     return false;
157*7b6f760fSDavid Green 
158*7b6f760fSDavid Green   // Look for connected operations starting from Ext's, terminating at Truncs.
159*7b6f760fSDavid Green   std::vector<Instruction *> Worklist;
160*7b6f760fSDavid Green   Worklist.push_back(Start);
161*7b6f760fSDavid Green   Worklist.push_back(cast<Instruction>(Start->getOperand(0)));
162*7b6f760fSDavid Green 
163*7b6f760fSDavid Green   SmallSetVector<Instruction *, 4> Truncs;
164*7b6f760fSDavid Green   SmallSetVector<Instruction *, 4> Exts;
165*7b6f760fSDavid Green   SmallSetVector<Instruction *, 4> Ops;
166*7b6f760fSDavid Green 
167*7b6f760fSDavid Green   while (!Worklist.empty()) {
168*7b6f760fSDavid Green     Instruction *I = Worklist.back();
169*7b6f760fSDavid Green     Worklist.pop_back();
170*7b6f760fSDavid Green 
171*7b6f760fSDavid Green     switch (I->getOpcode()) {
172*7b6f760fSDavid Green     // Truncs
173*7b6f760fSDavid Green     case Instruction::Trunc:
174*7b6f760fSDavid Green       if (Truncs.count(I))
175*7b6f760fSDavid Green         continue;
176*7b6f760fSDavid Green       Truncs.insert(I);
177*7b6f760fSDavid Green       Visited.insert(I);
178*7b6f760fSDavid Green       break;
179*7b6f760fSDavid Green 
180*7b6f760fSDavid Green     // Extend leafs
181*7b6f760fSDavid Green     case Instruction::SExt:
182*7b6f760fSDavid Green     case Instruction::ZExt:
183*7b6f760fSDavid Green       if (Exts.count(I))
184*7b6f760fSDavid Green         continue;
185*7b6f760fSDavid Green       for (auto *Use : I->users())
186*7b6f760fSDavid Green         Worklist.push_back(cast<Instruction>(Use));
187*7b6f760fSDavid Green       Exts.insert(I);
188*7b6f760fSDavid Green       break;
189*7b6f760fSDavid Green 
190*7b6f760fSDavid Green     // Binary/tertiary ops
191*7b6f760fSDavid Green     case Instruction::Add:
192*7b6f760fSDavid Green     case Instruction::Sub:
193*7b6f760fSDavid Green     case Instruction::Mul:
194*7b6f760fSDavid Green     case Instruction::AShr:
195*7b6f760fSDavid Green     case Instruction::LShr:
196*7b6f760fSDavid Green     case Instruction::Shl:
197*7b6f760fSDavid Green     case Instruction::ICmp:
198*7b6f760fSDavid Green     case Instruction::Select:
199*7b6f760fSDavid Green       if (Ops.count(I))
200*7b6f760fSDavid Green         continue;
201*7b6f760fSDavid Green       Ops.insert(I);
202*7b6f760fSDavid Green 
203*7b6f760fSDavid Green       for (Use &Op : I->operands()) {
204*7b6f760fSDavid Green         if (isa<Instruction>(Op))
205*7b6f760fSDavid Green           Worklist.push_back(cast<Instruction>(&Op));
206*7b6f760fSDavid Green         else
207*7b6f760fSDavid Green           return false;
208*7b6f760fSDavid Green       }
209*7b6f760fSDavid Green 
210*7b6f760fSDavid Green       for (auto *Use : I->users())
211*7b6f760fSDavid Green         Worklist.push_back(cast<Instruction>(Use));
212*7b6f760fSDavid Green       break;
213*7b6f760fSDavid Green 
214*7b6f760fSDavid Green     default:
215*7b6f760fSDavid Green       LLVM_DEBUG(dbgs() << "  Unhandled instruction: " << *I << "\n");
216*7b6f760fSDavid Green       return false;
217*7b6f760fSDavid Green     }
218*7b6f760fSDavid Green   }
219*7b6f760fSDavid Green 
220*7b6f760fSDavid Green   LLVM_DEBUG({
221*7b6f760fSDavid Green     dbgs() << "Found group:\n  Exts:";
222*7b6f760fSDavid Green     for (auto *I : Exts)
223*7b6f760fSDavid Green       dbgs() << "  " << *I << "\n";
224*7b6f760fSDavid Green     dbgs() << "  Ops:";
225*7b6f760fSDavid Green     for (auto *I : Ops)
226*7b6f760fSDavid Green       dbgs() << "  " << *I << "\n";
227*7b6f760fSDavid Green     dbgs() << "Truncs:";
228*7b6f760fSDavid Green     for (auto *I : Truncs)
229*7b6f760fSDavid Green       dbgs() << "  " << *I << "\n";
230*7b6f760fSDavid Green   });
231*7b6f760fSDavid Green 
232*7b6f760fSDavid Green   assert(!Truncs.empty() && "Expected some truncs");
233*7b6f760fSDavid Green   assert(!Exts.empty() && "Expected some leaves");
234*7b6f760fSDavid Green 
235*7b6f760fSDavid Green   // Check types
236*7b6f760fSDavid Green   unsigned NumElts = VT->getNumElements();
237*7b6f760fSDavid Green   unsigned BaseElts = VT->getScalarSizeInBits() == 16
238*7b6f760fSDavid Green                           ? 8
239*7b6f760fSDavid Green                           : (VT->getScalarSizeInBits() == 8 ? 16 : 0);
240*7b6f760fSDavid Green   if (BaseElts == 0 || NumElts % BaseElts != 0) {
241*7b6f760fSDavid Green     LLVM_DEBUG(dbgs() << "  Type is unsupported\n");
242*7b6f760fSDavid Green     return false;
243*7b6f760fSDavid Green   }
244*7b6f760fSDavid Green   if (Start->getOperand(0)->getType()->getScalarSizeInBits() !=
245*7b6f760fSDavid Green       VT->getScalarSizeInBits() * 2) {
246*7b6f760fSDavid Green     LLVM_DEBUG(dbgs() << "  Type not double sized\n");
247*7b6f760fSDavid Green     return false;
248*7b6f760fSDavid Green   }
249*7b6f760fSDavid Green   for (Instruction *I : Exts)
250*7b6f760fSDavid Green     if (I->getOperand(0)->getType() != VT) {
251*7b6f760fSDavid Green       LLVM_DEBUG(dbgs() << "  Wrong type on " << *I << "\n");
252*7b6f760fSDavid Green       return false;
253*7b6f760fSDavid Green     }
254*7b6f760fSDavid Green   for (Instruction *I : Truncs)
255*7b6f760fSDavid Green     if (I->getType() != VT) {
256*7b6f760fSDavid Green       LLVM_DEBUG(dbgs() << "  Wrong type on " << *I << "\n");
257*7b6f760fSDavid Green       return false;
258*7b6f760fSDavid Green     }
259*7b6f760fSDavid Green 
260*7b6f760fSDavid Green   // Check that it looks beneficial
261*7b6f760fSDavid Green   if (!isProfitableToInterleave(Exts, Truncs))
262*7b6f760fSDavid Green     return false;
263*7b6f760fSDavid Green 
264*7b6f760fSDavid Green   // Create new shuffles around the extends / truncs / other leaves.
265*7b6f760fSDavid Green   IRBuilder<> Builder(Start);
266*7b6f760fSDavid Green 
267*7b6f760fSDavid Green   SmallVector<int, 16> LeafMask;
268*7b6f760fSDavid Green   SmallVector<int, 16> TruncMask;
269*7b6f760fSDavid Green   // LeafMask : 0, 2, 4, 6, 1, 3, 5, 7   8, 10, 12, 14,  9, 11, 13, 15
270*7b6f760fSDavid Green   // TruncMask: 0, 4, 1, 5, 2, 6, 3, 7   8, 12,  9, 13, 10, 14, 11, 15
271*7b6f760fSDavid Green   for (unsigned Base = 0; Base < NumElts; Base += BaseElts) {
272*7b6f760fSDavid Green     for (unsigned i = 0; i < BaseElts / 2; i++)
273*7b6f760fSDavid Green       LeafMask.push_back(Base + i * 2);
274*7b6f760fSDavid Green     for (unsigned i = 0; i < BaseElts / 2; i++)
275*7b6f760fSDavid Green       LeafMask.push_back(Base + i * 2 + 1);
276*7b6f760fSDavid Green   }
277*7b6f760fSDavid Green   for (unsigned Base = 0; Base < NumElts; Base += BaseElts) {
278*7b6f760fSDavid Green     for (unsigned i = 0; i < BaseElts / 2; i++) {
279*7b6f760fSDavid Green       TruncMask.push_back(Base + i);
280*7b6f760fSDavid Green       TruncMask.push_back(Base + i + BaseElts / 2);
281*7b6f760fSDavid Green     }
282*7b6f760fSDavid Green   }
283*7b6f760fSDavid Green 
284*7b6f760fSDavid Green   for (Instruction *I : Exts) {
285*7b6f760fSDavid Green     LLVM_DEBUG(dbgs() << "Replacing ext " << *I << "\n");
286*7b6f760fSDavid Green     Builder.SetInsertPoint(I);
287*7b6f760fSDavid Green     Value *Shuffle = Builder.CreateShuffleVector(I->getOperand(0), LeafMask);
288*7b6f760fSDavid Green     bool Sext = isa<SExtInst>(I);
289*7b6f760fSDavid Green     Value *Ext = Sext ? Builder.CreateSExt(Shuffle, I->getType())
290*7b6f760fSDavid Green                       : Builder.CreateZExt(Shuffle, I->getType());
291*7b6f760fSDavid Green     I->replaceAllUsesWith(Ext);
292*7b6f760fSDavid Green     LLVM_DEBUG(dbgs() << "  with " << *Shuffle << "\n");
293*7b6f760fSDavid Green   }
294*7b6f760fSDavid Green 
295*7b6f760fSDavid Green   for (Instruction *I : Truncs) {
296*7b6f760fSDavid Green     LLVM_DEBUG(dbgs() << "Replacing trunc " << *I << "\n");
297*7b6f760fSDavid Green 
298*7b6f760fSDavid Green     Builder.SetInsertPoint(I->getParent(), ++I->getIterator());
299*7b6f760fSDavid Green     Value *Shuf = Builder.CreateShuffleVector(I, TruncMask);
300*7b6f760fSDavid Green     I->replaceAllUsesWith(Shuf);
301*7b6f760fSDavid Green     cast<Instruction>(Shuf)->setOperand(0, I);
302*7b6f760fSDavid Green 
303*7b6f760fSDavid Green     LLVM_DEBUG(dbgs() << "  with " << *Shuf << "\n");
304*7b6f760fSDavid Green   }
305*7b6f760fSDavid Green 
306*7b6f760fSDavid Green   return false;
307*7b6f760fSDavid Green }
308*7b6f760fSDavid Green 
309*7b6f760fSDavid Green bool MVELaneInterleaving::runOnFunction(Function &F) {
310*7b6f760fSDavid Green   if (!EnableInterleave)
311*7b6f760fSDavid Green     return false;
312*7b6f760fSDavid Green   auto &TPC = getAnalysis<TargetPassConfig>();
313*7b6f760fSDavid Green   auto &TM = TPC.getTM<TargetMachine>();
314*7b6f760fSDavid Green   auto *ST = &TM.getSubtarget<ARMSubtarget>(F);
315*7b6f760fSDavid Green   if (!ST->hasMVEIntegerOps())
316*7b6f760fSDavid Green     return false;
317*7b6f760fSDavid Green 
318*7b6f760fSDavid Green   bool Changed = false;
319*7b6f760fSDavid Green 
320*7b6f760fSDavid Green   SmallPtrSet<Instruction *, 16> Visited;
321*7b6f760fSDavid Green   for (Instruction &I : reverse(instructions(F))) {
322*7b6f760fSDavid Green     if (I.getType()->isVectorTy() &&
323*7b6f760fSDavid Green         (isa<TruncInst>(I) || isa<FPTruncInst>(I)) && !Visited.count(&I))
324*7b6f760fSDavid Green       Changed |= tryInterleave(&I, Visited);
325*7b6f760fSDavid Green   }
326*7b6f760fSDavid Green 
327*7b6f760fSDavid Green   return Changed;
328*7b6f760fSDavid Green }
329