17b6f760fSDavid Green //===- MVELaneInterleaving.cpp - Inverleave for MVE instructions ----------===// 27b6f760fSDavid Green // 37b6f760fSDavid Green // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 47b6f760fSDavid Green // See https://llvm.org/LICENSE.txt for license information. 57b6f760fSDavid Green // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 67b6f760fSDavid Green // 77b6f760fSDavid Green //===----------------------------------------------------------------------===// 87b6f760fSDavid Green // 97b6f760fSDavid Green // This pass interleaves around sext/zext/trunc instructions. MVE does not have 107b6f760fSDavid Green // a single sext/zext or trunc instruction that takes the bottom half of a 117b6f760fSDavid Green // vector and extends to a full width, like NEON has with MOVL. Instead it is 127b6f760fSDavid Green // expected that this happens through top/bottom instructions. So the MVE 137b6f760fSDavid Green // equivalent VMOVLT/B instructions take either the even or odd elements of the 147b6f760fSDavid Green // input and extend them to the larger type, producing a vector with half the 157b6f760fSDavid Green // number of elements each of double the bitwidth. As there is no simple 167b6f760fSDavid Green // instruction, we often have to turn sext/zext/trunc into a series of lane 177b6f760fSDavid Green // moves (or stack loads/stores, which we do not do yet). 187b6f760fSDavid Green // 197b6f760fSDavid Green // This pass takes vector code that starts at truncs, looks for interconnected 207b6f760fSDavid Green // blobs of operations that end with sext/zext (or constants/splats) of the 217b6f760fSDavid Green // form: 227b6f760fSDavid Green // %sa = sext v8i16 %a to v8i32 237b6f760fSDavid Green // %sb = sext v8i16 %b to v8i32 247b6f760fSDavid Green // %add = add v8i32 %sa, %sb 257b6f760fSDavid Green // %r = trunc %add to v8i16 267b6f760fSDavid Green // And adds shuffles to allow the use of VMOVL/VMOVN instrctions: 277b6f760fSDavid Green // %sha = shuffle v8i16 %a, undef, <0, 2, 4, 6, 1, 3, 5, 7> 287b6f760fSDavid Green // %sa = sext v8i16 %sha to v8i32 297b6f760fSDavid Green // %shb = shuffle v8i16 %b, undef, <0, 2, 4, 6, 1, 3, 5, 7> 307b6f760fSDavid Green // %sb = sext v8i16 %shb to v8i32 317b6f760fSDavid Green // %add = add v8i32 %sa, %sb 327b6f760fSDavid Green // %r = trunc %add to v8i16 337b6f760fSDavid Green // %shr = shuffle v8i16 %r, undef, <0, 4, 1, 5, 2, 6, 3, 7> 347b6f760fSDavid Green // Which can then be split and lowered to MVE instructions efficiently: 357b6f760fSDavid Green // %sa_b = VMOVLB.s16 %a 367b6f760fSDavid Green // %sa_t = VMOVLT.s16 %a 377b6f760fSDavid Green // %sb_b = VMOVLB.s16 %b 387b6f760fSDavid Green // %sb_t = VMOVLT.s16 %b 397b6f760fSDavid Green // %add_b = VADD.i32 %sa_b, %sb_b 407b6f760fSDavid Green // %add_t = VADD.i32 %sa_t, %sb_t 417b6f760fSDavid Green // %r = VMOVNT.i16 %add_b, %add_t 427b6f760fSDavid Green // 437b6f760fSDavid Green //===----------------------------------------------------------------------===// 447b6f760fSDavid Green 457b6f760fSDavid Green #include "ARM.h" 467b6f760fSDavid Green #include "ARMBaseInstrInfo.h" 477b6f760fSDavid Green #include "ARMSubtarget.h" 487b6f760fSDavid Green #include "llvm/Analysis/TargetTransformInfo.h" 497b6f760fSDavid Green #include "llvm/CodeGen/TargetLowering.h" 507b6f760fSDavid Green #include "llvm/CodeGen/TargetPassConfig.h" 517b6f760fSDavid Green #include "llvm/CodeGen/TargetSubtargetInfo.h" 527b6f760fSDavid Green #include "llvm/IR/BasicBlock.h" 537b6f760fSDavid Green #include "llvm/IR/Constant.h" 547b6f760fSDavid Green #include "llvm/IR/Constants.h" 557b6f760fSDavid Green #include "llvm/IR/DerivedTypes.h" 567b6f760fSDavid Green #include "llvm/IR/Function.h" 577b6f760fSDavid Green #include "llvm/IR/IRBuilder.h" 587b6f760fSDavid Green #include "llvm/IR/InstIterator.h" 597b6f760fSDavid Green #include "llvm/IR/InstrTypes.h" 607b6f760fSDavid Green #include "llvm/IR/Instruction.h" 617b6f760fSDavid Green #include "llvm/IR/Instructions.h" 627b6f760fSDavid Green #include "llvm/IR/IntrinsicInst.h" 637b6f760fSDavid Green #include "llvm/IR/Intrinsics.h" 647b6f760fSDavid Green #include "llvm/IR/IntrinsicsARM.h" 657b6f760fSDavid Green #include "llvm/IR/PatternMatch.h" 667b6f760fSDavid Green #include "llvm/IR/Type.h" 677b6f760fSDavid Green #include "llvm/IR/Value.h" 687b6f760fSDavid Green #include "llvm/InitializePasses.h" 697b6f760fSDavid Green #include "llvm/Pass.h" 707b6f760fSDavid Green #include "llvm/Support/Casting.h" 717b6f760fSDavid Green #include <algorithm> 727b6f760fSDavid Green #include <cassert> 737b6f760fSDavid Green 747b6f760fSDavid Green using namespace llvm; 757b6f760fSDavid Green 767b6f760fSDavid Green #define DEBUG_TYPE "mve-laneinterleave" 777b6f760fSDavid Green 787b6f760fSDavid Green cl::opt<bool> EnableInterleave( 797b6f760fSDavid Green "enable-mve-interleave", cl::Hidden, cl::init(true), 807b6f760fSDavid Green cl::desc("Enable interleave MVE vector operation lowering")); 817b6f760fSDavid Green 827b6f760fSDavid Green namespace { 837b6f760fSDavid Green 847b6f760fSDavid Green class MVELaneInterleaving : public FunctionPass { 857b6f760fSDavid Green public: 867b6f760fSDavid Green static char ID; // Pass identification, replacement for typeid 877b6f760fSDavid Green 887b6f760fSDavid Green explicit MVELaneInterleaving() : FunctionPass(ID) { 897b6f760fSDavid Green initializeMVELaneInterleavingPass(*PassRegistry::getPassRegistry()); 907b6f760fSDavid Green } 917b6f760fSDavid Green 927b6f760fSDavid Green bool runOnFunction(Function &F) override; 937b6f760fSDavid Green 947b6f760fSDavid Green StringRef getPassName() const override { return "MVE lane interleaving"; } 957b6f760fSDavid Green 967b6f760fSDavid Green void getAnalysisUsage(AnalysisUsage &AU) const override { 977b6f760fSDavid Green AU.setPreservesCFG(); 987b6f760fSDavid Green AU.addRequired<TargetPassConfig>(); 997b6f760fSDavid Green FunctionPass::getAnalysisUsage(AU); 1007b6f760fSDavid Green } 1017b6f760fSDavid Green }; 1027b6f760fSDavid Green 1037b6f760fSDavid Green } // end anonymous namespace 1047b6f760fSDavid Green 1057b6f760fSDavid Green char MVELaneInterleaving::ID = 0; 1067b6f760fSDavid Green 1077b6f760fSDavid Green INITIALIZE_PASS(MVELaneInterleaving, DEBUG_TYPE, "MVE lane interleaving", false, 1087b6f760fSDavid Green false) 1097b6f760fSDavid Green 1107b6f760fSDavid Green Pass *llvm::createMVELaneInterleavingPass() { 1117b6f760fSDavid Green return new MVELaneInterleaving(); 1127b6f760fSDavid Green } 1137b6f760fSDavid Green 1147b6f760fSDavid Green static bool isProfitableToInterleave(SmallSetVector<Instruction *, 4> &Exts, 1157b6f760fSDavid Green SmallSetVector<Instruction *, 4> &Truncs) { 1167b6f760fSDavid Green // This is not always beneficial to transform. Exts can be incorporated into 1177b6f760fSDavid Green // loads, Truncs can be folded into stores. 1187b6f760fSDavid Green // Truncs are usually the same number of instructions, 1197b6f760fSDavid Green // VSTRH.32(A);VSTRH.32(B) vs VSTRH.16(VMOVNT A, B) with interleaving 1207b6f760fSDavid Green // Exts are unfortunately more instructions in the general case: 1217b6f760fSDavid Green // A=VLDRH.32; B=VLDRH.32; 1227b6f760fSDavid Green // vs with interleaving: 1237b6f760fSDavid Green // T=VLDRH.16; A=VMOVNB T; B=VMOVNT T 1247b6f760fSDavid Green // But those VMOVL may be folded into a VMULL. 1257b6f760fSDavid Green 1267b6f760fSDavid Green // But expensive extends/truncs are always good to remove. 1277b6f760fSDavid Green for (auto *E : Exts) 1287b6f760fSDavid Green if (!isa<LoadInst>(E->getOperand(0))) { 1297b6f760fSDavid Green LLVM_DEBUG(dbgs() << "Beneficial due to " << *E << "\n"); 1307b6f760fSDavid Green return true; 1317b6f760fSDavid Green } 1327b6f760fSDavid Green for (auto *T : Truncs) 1337b6f760fSDavid Green if (T->hasOneUse() && !isa<StoreInst>(*T->user_begin())) { 1347b6f760fSDavid Green LLVM_DEBUG(dbgs() << "Beneficial due to " << *T << "\n"); 1357b6f760fSDavid Green return true; 1367b6f760fSDavid Green } 1377b6f760fSDavid Green 1387b6f760fSDavid Green // Otherwise, we know we have a load(ext), see if any of the Extends are a 1397b6f760fSDavid Green // vmull. This is a simple heuristic and certainly not perfect. 1407b6f760fSDavid Green for (auto *E : Exts) { 1417b6f760fSDavid Green if (!E->hasOneUse() || 1427b6f760fSDavid Green cast<Instruction>(*E->user_begin())->getOpcode() != Instruction::Mul) { 1437b6f760fSDavid Green LLVM_DEBUG(dbgs() << "Not beneficial due to " << *E << "\n"); 1447b6f760fSDavid Green return false; 1457b6f760fSDavid Green } 1467b6f760fSDavid Green } 1477b6f760fSDavid Green return true; 1487b6f760fSDavid Green } 1497b6f760fSDavid Green 1507b6f760fSDavid Green static bool tryInterleave(Instruction *Start, 1517b6f760fSDavid Green SmallPtrSetImpl<Instruction *> &Visited) { 1527b6f760fSDavid Green LLVM_DEBUG(dbgs() << "tryInterleave from " << *Start << "\n"); 1537b6f760fSDavid Green auto *VT = cast<FixedVectorType>(Start->getType()); 1547b6f760fSDavid Green 1557b6f760fSDavid Green if (!isa<Instruction>(Start->getOperand(0))) 1567b6f760fSDavid Green return false; 1577b6f760fSDavid Green 1587b6f760fSDavid Green // Look for connected operations starting from Ext's, terminating at Truncs. 1597b6f760fSDavid Green std::vector<Instruction *> Worklist; 1607b6f760fSDavid Green Worklist.push_back(Start); 1617b6f760fSDavid Green Worklist.push_back(cast<Instruction>(Start->getOperand(0))); 1627b6f760fSDavid Green 1637b6f760fSDavid Green SmallSetVector<Instruction *, 4> Truncs; 1647b6f760fSDavid Green SmallSetVector<Instruction *, 4> Exts; 165*3a68c6d2SDavid Green SmallSetVector<Use *, 4> OtherLeafs; 1667b6f760fSDavid Green SmallSetVector<Instruction *, 4> Ops; 1677b6f760fSDavid Green 1687b6f760fSDavid Green while (!Worklist.empty()) { 1697b6f760fSDavid Green Instruction *I = Worklist.back(); 1707b6f760fSDavid Green Worklist.pop_back(); 1717b6f760fSDavid Green 1727b6f760fSDavid Green switch (I->getOpcode()) { 1737b6f760fSDavid Green // Truncs 1747b6f760fSDavid Green case Instruction::Trunc: 1757b6f760fSDavid Green if (Truncs.count(I)) 1767b6f760fSDavid Green continue; 1777b6f760fSDavid Green Truncs.insert(I); 1787b6f760fSDavid Green Visited.insert(I); 1797b6f760fSDavid Green break; 1807b6f760fSDavid Green 1817b6f760fSDavid Green // Extend leafs 1827b6f760fSDavid Green case Instruction::SExt: 1837b6f760fSDavid Green case Instruction::ZExt: 1847b6f760fSDavid Green if (Exts.count(I)) 1857b6f760fSDavid Green continue; 1867b6f760fSDavid Green for (auto *Use : I->users()) 1877b6f760fSDavid Green Worklist.push_back(cast<Instruction>(Use)); 1887b6f760fSDavid Green Exts.insert(I); 1897b6f760fSDavid Green break; 1907b6f760fSDavid Green 1917b6f760fSDavid Green // Binary/tertiary ops 1927b6f760fSDavid Green case Instruction::Add: 1937b6f760fSDavid Green case Instruction::Sub: 1947b6f760fSDavid Green case Instruction::Mul: 1957b6f760fSDavid Green case Instruction::AShr: 1967b6f760fSDavid Green case Instruction::LShr: 1977b6f760fSDavid Green case Instruction::Shl: 1987b6f760fSDavid Green case Instruction::ICmp: 1997b6f760fSDavid Green case Instruction::Select: 2007b6f760fSDavid Green if (Ops.count(I)) 2017b6f760fSDavid Green continue; 2027b6f760fSDavid Green Ops.insert(I); 2037b6f760fSDavid Green 2047b6f760fSDavid Green for (Use &Op : I->operands()) { 2057b6f760fSDavid Green if (isa<Instruction>(Op)) 2067b6f760fSDavid Green Worklist.push_back(cast<Instruction>(&Op)); 2077b6f760fSDavid Green else 208*3a68c6d2SDavid Green OtherLeafs.insert(&Op); 2097b6f760fSDavid Green } 2107b6f760fSDavid Green 2117b6f760fSDavid Green for (auto *Use : I->users()) 2127b6f760fSDavid Green Worklist.push_back(cast<Instruction>(Use)); 2137b6f760fSDavid Green break; 2147b6f760fSDavid Green 2157b6f760fSDavid Green default: 2167b6f760fSDavid Green LLVM_DEBUG(dbgs() << " Unhandled instruction: " << *I << "\n"); 2177b6f760fSDavid Green return false; 2187b6f760fSDavid Green } 2197b6f760fSDavid Green } 2207b6f760fSDavid Green 221*3a68c6d2SDavid Green if (Exts.empty() && OtherLeafs.empty()) 222*3a68c6d2SDavid Green return false; 223*3a68c6d2SDavid Green 2247b6f760fSDavid Green LLVM_DEBUG({ 2257b6f760fSDavid Green dbgs() << "Found group:\n Exts:"; 2267b6f760fSDavid Green for (auto *I : Exts) 2277b6f760fSDavid Green dbgs() << " " << *I << "\n"; 2287b6f760fSDavid Green dbgs() << " Ops:"; 2297b6f760fSDavid Green for (auto *I : Ops) 2307b6f760fSDavid Green dbgs() << " " << *I << "\n"; 231*3a68c6d2SDavid Green dbgs() << " OtherLeafs:"; 232*3a68c6d2SDavid Green for (auto *I : OtherLeafs) 233*3a68c6d2SDavid Green dbgs() << " " << *I << "\n"; 2347b6f760fSDavid Green dbgs() << "Truncs:"; 2357b6f760fSDavid Green for (auto *I : Truncs) 2367b6f760fSDavid Green dbgs() << " " << *I << "\n"; 2377b6f760fSDavid Green }); 2387b6f760fSDavid Green 2397b6f760fSDavid Green assert(!Truncs.empty() && "Expected some truncs"); 2407b6f760fSDavid Green 2417b6f760fSDavid Green // Check types 2427b6f760fSDavid Green unsigned NumElts = VT->getNumElements(); 2437b6f760fSDavid Green unsigned BaseElts = VT->getScalarSizeInBits() == 16 2447b6f760fSDavid Green ? 8 2457b6f760fSDavid Green : (VT->getScalarSizeInBits() == 8 ? 16 : 0); 2467b6f760fSDavid Green if (BaseElts == 0 || NumElts % BaseElts != 0) { 2477b6f760fSDavid Green LLVM_DEBUG(dbgs() << " Type is unsupported\n"); 2487b6f760fSDavid Green return false; 2497b6f760fSDavid Green } 2507b6f760fSDavid Green if (Start->getOperand(0)->getType()->getScalarSizeInBits() != 2517b6f760fSDavid Green VT->getScalarSizeInBits() * 2) { 2527b6f760fSDavid Green LLVM_DEBUG(dbgs() << " Type not double sized\n"); 2537b6f760fSDavid Green return false; 2547b6f760fSDavid Green } 2557b6f760fSDavid Green for (Instruction *I : Exts) 2567b6f760fSDavid Green if (I->getOperand(0)->getType() != VT) { 2577b6f760fSDavid Green LLVM_DEBUG(dbgs() << " Wrong type on " << *I << "\n"); 2587b6f760fSDavid Green return false; 2597b6f760fSDavid Green } 2607b6f760fSDavid Green for (Instruction *I : Truncs) 2617b6f760fSDavid Green if (I->getType() != VT) { 2627b6f760fSDavid Green LLVM_DEBUG(dbgs() << " Wrong type on " << *I << "\n"); 2637b6f760fSDavid Green return false; 2647b6f760fSDavid Green } 2657b6f760fSDavid Green 2667b6f760fSDavid Green // Check that it looks beneficial 2677b6f760fSDavid Green if (!isProfitableToInterleave(Exts, Truncs)) 2687b6f760fSDavid Green return false; 2697b6f760fSDavid Green 2707b6f760fSDavid Green // Create new shuffles around the extends / truncs / other leaves. 2717b6f760fSDavid Green IRBuilder<> Builder(Start); 2727b6f760fSDavid Green 2737b6f760fSDavid Green SmallVector<int, 16> LeafMask; 2747b6f760fSDavid Green SmallVector<int, 16> TruncMask; 2757b6f760fSDavid Green // LeafMask : 0, 2, 4, 6, 1, 3, 5, 7 8, 10, 12, 14, 9, 11, 13, 15 2767b6f760fSDavid Green // TruncMask: 0, 4, 1, 5, 2, 6, 3, 7 8, 12, 9, 13, 10, 14, 11, 15 2777b6f760fSDavid Green for (unsigned Base = 0; Base < NumElts; Base += BaseElts) { 2787b6f760fSDavid Green for (unsigned i = 0; i < BaseElts / 2; i++) 2797b6f760fSDavid Green LeafMask.push_back(Base + i * 2); 2807b6f760fSDavid Green for (unsigned i = 0; i < BaseElts / 2; i++) 2817b6f760fSDavid Green LeafMask.push_back(Base + i * 2 + 1); 2827b6f760fSDavid Green } 2837b6f760fSDavid Green for (unsigned Base = 0; Base < NumElts; Base += BaseElts) { 2847b6f760fSDavid Green for (unsigned i = 0; i < BaseElts / 2; i++) { 2857b6f760fSDavid Green TruncMask.push_back(Base + i); 2867b6f760fSDavid Green TruncMask.push_back(Base + i + BaseElts / 2); 2877b6f760fSDavid Green } 2887b6f760fSDavid Green } 2897b6f760fSDavid Green 2907b6f760fSDavid Green for (Instruction *I : Exts) { 2917b6f760fSDavid Green LLVM_DEBUG(dbgs() << "Replacing ext " << *I << "\n"); 2927b6f760fSDavid Green Builder.SetInsertPoint(I); 2937b6f760fSDavid Green Value *Shuffle = Builder.CreateShuffleVector(I->getOperand(0), LeafMask); 2947b6f760fSDavid Green bool Sext = isa<SExtInst>(I); 2957b6f760fSDavid Green Value *Ext = Sext ? Builder.CreateSExt(Shuffle, I->getType()) 2967b6f760fSDavid Green : Builder.CreateZExt(Shuffle, I->getType()); 2977b6f760fSDavid Green I->replaceAllUsesWith(Ext); 2987b6f760fSDavid Green LLVM_DEBUG(dbgs() << " with " << *Shuffle << "\n"); 2997b6f760fSDavid Green } 3007b6f760fSDavid Green 301*3a68c6d2SDavid Green for (Use *I : OtherLeafs) { 302*3a68c6d2SDavid Green LLVM_DEBUG(dbgs() << "Replacing leaf " << *I << "\n"); 303*3a68c6d2SDavid Green Builder.SetInsertPoint(cast<Instruction>(I->getUser())); 304*3a68c6d2SDavid Green Value *Shuffle = Builder.CreateShuffleVector(I->get(), LeafMask); 305*3a68c6d2SDavid Green I->getUser()->setOperand(I->getOperandNo(), Shuffle); 306*3a68c6d2SDavid Green LLVM_DEBUG(dbgs() << " with " << *Shuffle << "\n"); 307*3a68c6d2SDavid Green } 308*3a68c6d2SDavid Green 3097b6f760fSDavid Green for (Instruction *I : Truncs) { 3107b6f760fSDavid Green LLVM_DEBUG(dbgs() << "Replacing trunc " << *I << "\n"); 3117b6f760fSDavid Green 3127b6f760fSDavid Green Builder.SetInsertPoint(I->getParent(), ++I->getIterator()); 3137b6f760fSDavid Green Value *Shuf = Builder.CreateShuffleVector(I, TruncMask); 3147b6f760fSDavid Green I->replaceAllUsesWith(Shuf); 3157b6f760fSDavid Green cast<Instruction>(Shuf)->setOperand(0, I); 3167b6f760fSDavid Green 3177b6f760fSDavid Green LLVM_DEBUG(dbgs() << " with " << *Shuf << "\n"); 3187b6f760fSDavid Green } 3197b6f760fSDavid Green 3206c88ffedSDavid Green return true; 3217b6f760fSDavid Green } 3227b6f760fSDavid Green 3237b6f760fSDavid Green bool MVELaneInterleaving::runOnFunction(Function &F) { 3247b6f760fSDavid Green if (!EnableInterleave) 3257b6f760fSDavid Green return false; 3267b6f760fSDavid Green auto &TPC = getAnalysis<TargetPassConfig>(); 3277b6f760fSDavid Green auto &TM = TPC.getTM<TargetMachine>(); 3287b6f760fSDavid Green auto *ST = &TM.getSubtarget<ARMSubtarget>(F); 3297b6f760fSDavid Green if (!ST->hasMVEIntegerOps()) 3307b6f760fSDavid Green return false; 3317b6f760fSDavid Green 3327b6f760fSDavid Green bool Changed = false; 3337b6f760fSDavid Green 3347b6f760fSDavid Green SmallPtrSet<Instruction *, 16> Visited; 3357b6f760fSDavid Green for (Instruction &I : reverse(instructions(F))) { 3367b6f760fSDavid Green if (I.getType()->isVectorTy() && 3377b6f760fSDavid Green (isa<TruncInst>(I) || isa<FPTruncInst>(I)) && !Visited.count(&I)) 3387b6f760fSDavid Green Changed |= tryInterleave(&I, Visited); 3397b6f760fSDavid Green } 3407b6f760fSDavid Green 3417b6f760fSDavid Green return Changed; 3427b6f760fSDavid Green } 343