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