15ffd83dbSDimitry Andric //===----- SVEIntrinsicOpts - SVE ACLE Intrinsics Opts --------------------===// 25ffd83dbSDimitry Andric // 35ffd83dbSDimitry Andric // The LLVM Compiler Infrastructure 45ffd83dbSDimitry Andric // 55ffd83dbSDimitry Andric // This file is distributed under the University of Illinois Open Source 65ffd83dbSDimitry Andric // License. See LICENSE.TXT for details. 75ffd83dbSDimitry Andric // 85ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 95ffd83dbSDimitry Andric // 105ffd83dbSDimitry Andric // Performs general IR level optimizations on SVE intrinsics. 115ffd83dbSDimitry Andric // 125ffd83dbSDimitry Andric // The main goal of this pass is to remove unnecessary reinterpret 135ffd83dbSDimitry Andric // intrinsics (llvm.aarch64.sve.convert.[to|from].svbool), e.g: 145ffd83dbSDimitry Andric // 155ffd83dbSDimitry Andric // %1 = @llvm.aarch64.sve.convert.to.svbool.nxv4i1(<vscale x 4 x i1> %a) 165ffd83dbSDimitry Andric // %2 = @llvm.aarch64.sve.convert.from.svbool.nxv4i1(<vscale x 16 x i1> %1) 175ffd83dbSDimitry Andric // 185ffd83dbSDimitry Andric // This pass also looks for ptest intrinsics & phi instructions where the 195ffd83dbSDimitry Andric // operands are being needlessly converted to and from svbool_t. 205ffd83dbSDimitry Andric // 215ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 225ffd83dbSDimitry Andric 235ffd83dbSDimitry Andric #include "Utils/AArch64BaseInfo.h" 245ffd83dbSDimitry Andric #include "llvm/ADT/PostOrderIterator.h" 255ffd83dbSDimitry Andric #include "llvm/ADT/SetVector.h" 265ffd83dbSDimitry Andric #include "llvm/IR/Constants.h" 275ffd83dbSDimitry Andric #include "llvm/IR/Dominators.h" 285ffd83dbSDimitry Andric #include "llvm/IR/IRBuilder.h" 295ffd83dbSDimitry Andric #include "llvm/IR/Instructions.h" 305ffd83dbSDimitry Andric #include "llvm/IR/IntrinsicInst.h" 315ffd83dbSDimitry Andric #include "llvm/IR/IntrinsicsAArch64.h" 325ffd83dbSDimitry Andric #include "llvm/IR/LLVMContext.h" 335ffd83dbSDimitry Andric #include "llvm/IR/PatternMatch.h" 345ffd83dbSDimitry Andric #include "llvm/InitializePasses.h" 355ffd83dbSDimitry Andric #include "llvm/Support/Debug.h" 365ffd83dbSDimitry Andric 375ffd83dbSDimitry Andric using namespace llvm; 385ffd83dbSDimitry Andric using namespace llvm::PatternMatch; 395ffd83dbSDimitry Andric 405ffd83dbSDimitry Andric #define DEBUG_TYPE "sve-intrinsic-opts" 415ffd83dbSDimitry Andric 425ffd83dbSDimitry Andric namespace llvm { 435ffd83dbSDimitry Andric void initializeSVEIntrinsicOptsPass(PassRegistry &); 445ffd83dbSDimitry Andric } 455ffd83dbSDimitry Andric 465ffd83dbSDimitry Andric namespace { 475ffd83dbSDimitry Andric struct SVEIntrinsicOpts : public ModulePass { 485ffd83dbSDimitry Andric static char ID; // Pass identification, replacement for typeid 495ffd83dbSDimitry Andric SVEIntrinsicOpts() : ModulePass(ID) { 505ffd83dbSDimitry Andric initializeSVEIntrinsicOptsPass(*PassRegistry::getPassRegistry()); 515ffd83dbSDimitry Andric } 525ffd83dbSDimitry Andric 535ffd83dbSDimitry Andric bool runOnModule(Module &M) override; 545ffd83dbSDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override; 555ffd83dbSDimitry Andric 565ffd83dbSDimitry Andric private: 575ffd83dbSDimitry Andric static IntrinsicInst *isReinterpretToSVBool(Value *V); 585ffd83dbSDimitry Andric 595ffd83dbSDimitry Andric static bool optimizeIntrinsic(Instruction *I); 605ffd83dbSDimitry Andric 615ffd83dbSDimitry Andric bool optimizeFunctions(SmallSetVector<Function *, 4> &Functions); 625ffd83dbSDimitry Andric 635ffd83dbSDimitry Andric static bool optimizeConvertFromSVBool(IntrinsicInst *I); 645ffd83dbSDimitry Andric static bool optimizePTest(IntrinsicInst *I); 655ffd83dbSDimitry Andric 665ffd83dbSDimitry Andric static bool processPhiNode(IntrinsicInst *I); 675ffd83dbSDimitry Andric }; 685ffd83dbSDimitry Andric } // end anonymous namespace 695ffd83dbSDimitry Andric 705ffd83dbSDimitry Andric void SVEIntrinsicOpts::getAnalysisUsage(AnalysisUsage &AU) const { 715ffd83dbSDimitry Andric AU.addRequired<DominatorTreeWrapperPass>(); 725ffd83dbSDimitry Andric AU.setPreservesCFG(); 735ffd83dbSDimitry Andric } 745ffd83dbSDimitry Andric 755ffd83dbSDimitry Andric char SVEIntrinsicOpts::ID = 0; 765ffd83dbSDimitry Andric static const char *name = "SVE intrinsics optimizations"; 775ffd83dbSDimitry Andric INITIALIZE_PASS_BEGIN(SVEIntrinsicOpts, DEBUG_TYPE, name, false, false) 785ffd83dbSDimitry Andric INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass); 795ffd83dbSDimitry Andric INITIALIZE_PASS_END(SVEIntrinsicOpts, DEBUG_TYPE, name, false, false) 805ffd83dbSDimitry Andric 815ffd83dbSDimitry Andric namespace llvm { 825ffd83dbSDimitry Andric ModulePass *createSVEIntrinsicOptsPass() { return new SVEIntrinsicOpts(); } 835ffd83dbSDimitry Andric } // namespace llvm 845ffd83dbSDimitry Andric 855ffd83dbSDimitry Andric /// Returns V if it's a cast from <n x 16 x i1> (aka svbool_t), nullptr 865ffd83dbSDimitry Andric /// otherwise. 875ffd83dbSDimitry Andric IntrinsicInst *SVEIntrinsicOpts::isReinterpretToSVBool(Value *V) { 885ffd83dbSDimitry Andric IntrinsicInst *I = dyn_cast<IntrinsicInst>(V); 895ffd83dbSDimitry Andric if (!I) 905ffd83dbSDimitry Andric return nullptr; 915ffd83dbSDimitry Andric 925ffd83dbSDimitry Andric if (I->getIntrinsicID() != Intrinsic::aarch64_sve_convert_to_svbool) 935ffd83dbSDimitry Andric return nullptr; 945ffd83dbSDimitry Andric 955ffd83dbSDimitry Andric return I; 965ffd83dbSDimitry Andric } 975ffd83dbSDimitry Andric 985ffd83dbSDimitry Andric /// The function will remove redundant reinterprets casting in the presence 995ffd83dbSDimitry Andric /// of the control flow 1005ffd83dbSDimitry Andric bool SVEIntrinsicOpts::processPhiNode(IntrinsicInst *X) { 1015ffd83dbSDimitry Andric 1025ffd83dbSDimitry Andric SmallVector<Instruction *, 32> Worklist; 1035ffd83dbSDimitry Andric auto RequiredType = X->getType(); 1045ffd83dbSDimitry Andric 1055ffd83dbSDimitry Andric auto *PN = dyn_cast<PHINode>(X->getArgOperand(0)); 1065ffd83dbSDimitry Andric assert(PN && "Expected Phi Node!"); 1075ffd83dbSDimitry Andric 1085ffd83dbSDimitry Andric // Don't create a new Phi unless we can remove the old one. 1095ffd83dbSDimitry Andric if (!PN->hasOneUse()) 1105ffd83dbSDimitry Andric return false; 1115ffd83dbSDimitry Andric 1125ffd83dbSDimitry Andric for (Value *IncValPhi : PN->incoming_values()) { 1135ffd83dbSDimitry Andric auto *Reinterpret = isReinterpretToSVBool(IncValPhi); 1145ffd83dbSDimitry Andric if (!Reinterpret || 1155ffd83dbSDimitry Andric RequiredType != Reinterpret->getArgOperand(0)->getType()) 1165ffd83dbSDimitry Andric return false; 1175ffd83dbSDimitry Andric } 1185ffd83dbSDimitry Andric 1195ffd83dbSDimitry Andric // Create the new Phi 1205ffd83dbSDimitry Andric LLVMContext &Ctx = PN->getContext(); 1215ffd83dbSDimitry Andric IRBuilder<> Builder(Ctx); 1225ffd83dbSDimitry Andric Builder.SetInsertPoint(PN); 1235ffd83dbSDimitry Andric PHINode *NPN = Builder.CreatePHI(RequiredType, PN->getNumIncomingValues()); 1245ffd83dbSDimitry Andric Worklist.push_back(PN); 1255ffd83dbSDimitry Andric 1265ffd83dbSDimitry Andric for (unsigned I = 0; I < PN->getNumIncomingValues(); I++) { 1275ffd83dbSDimitry Andric auto *Reinterpret = cast<Instruction>(PN->getIncomingValue(I)); 1285ffd83dbSDimitry Andric NPN->addIncoming(Reinterpret->getOperand(0), PN->getIncomingBlock(I)); 1295ffd83dbSDimitry Andric Worklist.push_back(Reinterpret); 1305ffd83dbSDimitry Andric } 1315ffd83dbSDimitry Andric 1325ffd83dbSDimitry Andric // Cleanup Phi Node and reinterprets 1335ffd83dbSDimitry Andric X->replaceAllUsesWith(NPN); 1345ffd83dbSDimitry Andric X->eraseFromParent(); 1355ffd83dbSDimitry Andric 1365ffd83dbSDimitry Andric for (auto &I : Worklist) 1375ffd83dbSDimitry Andric if (I->use_empty()) 1385ffd83dbSDimitry Andric I->eraseFromParent(); 1395ffd83dbSDimitry Andric 1405ffd83dbSDimitry Andric return true; 1415ffd83dbSDimitry Andric } 1425ffd83dbSDimitry Andric 1435ffd83dbSDimitry Andric bool SVEIntrinsicOpts::optimizePTest(IntrinsicInst *I) { 1445ffd83dbSDimitry Andric IntrinsicInst *Op1 = dyn_cast<IntrinsicInst>(I->getArgOperand(0)); 1455ffd83dbSDimitry Andric IntrinsicInst *Op2 = dyn_cast<IntrinsicInst>(I->getArgOperand(1)); 1465ffd83dbSDimitry Andric 1475ffd83dbSDimitry Andric if (Op1 && Op2 && 1485ffd83dbSDimitry Andric Op1->getIntrinsicID() == Intrinsic::aarch64_sve_convert_to_svbool && 1495ffd83dbSDimitry Andric Op2->getIntrinsicID() == Intrinsic::aarch64_sve_convert_to_svbool && 1505ffd83dbSDimitry Andric Op1->getArgOperand(0)->getType() == Op2->getArgOperand(0)->getType()) { 1515ffd83dbSDimitry Andric 1525ffd83dbSDimitry Andric Value *Ops[] = {Op1->getArgOperand(0), Op2->getArgOperand(0)}; 1535ffd83dbSDimitry Andric Type *Tys[] = {Op1->getArgOperand(0)->getType()}; 1545ffd83dbSDimitry Andric Module *M = I->getParent()->getParent()->getParent(); 1555ffd83dbSDimitry Andric 1565ffd83dbSDimitry Andric auto Fn = Intrinsic::getDeclaration(M, I->getIntrinsicID(), Tys); 1575ffd83dbSDimitry Andric auto CI = CallInst::Create(Fn, Ops, I->getName(), I); 1585ffd83dbSDimitry Andric 1595ffd83dbSDimitry Andric I->replaceAllUsesWith(CI); 1605ffd83dbSDimitry Andric I->eraseFromParent(); 1615ffd83dbSDimitry Andric if (Op1->use_empty()) 1625ffd83dbSDimitry Andric Op1->eraseFromParent(); 163*75b4d546SDimitry Andric if (Op1 != Op2 && Op2->use_empty()) 1645ffd83dbSDimitry Andric Op2->eraseFromParent(); 1655ffd83dbSDimitry Andric 1665ffd83dbSDimitry Andric return true; 1675ffd83dbSDimitry Andric } 1685ffd83dbSDimitry Andric 1695ffd83dbSDimitry Andric return false; 1705ffd83dbSDimitry Andric } 1715ffd83dbSDimitry Andric 1725ffd83dbSDimitry Andric bool SVEIntrinsicOpts::optimizeConvertFromSVBool(IntrinsicInst *I) { 1735ffd83dbSDimitry Andric assert(I->getIntrinsicID() == Intrinsic::aarch64_sve_convert_from_svbool && 1745ffd83dbSDimitry Andric "Unexpected opcode"); 1755ffd83dbSDimitry Andric 1765ffd83dbSDimitry Andric // If the reinterpret instruction operand is a PHI Node 1775ffd83dbSDimitry Andric if (isa<PHINode>(I->getArgOperand(0))) 1785ffd83dbSDimitry Andric return processPhiNode(I); 1795ffd83dbSDimitry Andric 1805ffd83dbSDimitry Andric // If we have a reinterpret intrinsic I of type A which is converting from 1815ffd83dbSDimitry Andric // another reinterpret Y of type B, and the source type of Y is A, then we can 1825ffd83dbSDimitry Andric // elide away both reinterprets if there are no other users of Y. 1835ffd83dbSDimitry Andric auto *Y = isReinterpretToSVBool(I->getArgOperand(0)); 1845ffd83dbSDimitry Andric if (!Y) 1855ffd83dbSDimitry Andric return false; 1865ffd83dbSDimitry Andric 1875ffd83dbSDimitry Andric Value *SourceVal = Y->getArgOperand(0); 1885ffd83dbSDimitry Andric if (I->getType() != SourceVal->getType()) 1895ffd83dbSDimitry Andric return false; 1905ffd83dbSDimitry Andric 1915ffd83dbSDimitry Andric I->replaceAllUsesWith(SourceVal); 1925ffd83dbSDimitry Andric I->eraseFromParent(); 1935ffd83dbSDimitry Andric if (Y->use_empty()) 1945ffd83dbSDimitry Andric Y->eraseFromParent(); 1955ffd83dbSDimitry Andric 1965ffd83dbSDimitry Andric return true; 1975ffd83dbSDimitry Andric } 1985ffd83dbSDimitry Andric 1995ffd83dbSDimitry Andric bool SVEIntrinsicOpts::optimizeIntrinsic(Instruction *I) { 2005ffd83dbSDimitry Andric IntrinsicInst *IntrI = dyn_cast<IntrinsicInst>(I); 2015ffd83dbSDimitry Andric if (!IntrI) 2025ffd83dbSDimitry Andric return false; 2035ffd83dbSDimitry Andric 2045ffd83dbSDimitry Andric switch (IntrI->getIntrinsicID()) { 2055ffd83dbSDimitry Andric case Intrinsic::aarch64_sve_convert_from_svbool: 2065ffd83dbSDimitry Andric return optimizeConvertFromSVBool(IntrI); 2075ffd83dbSDimitry Andric case Intrinsic::aarch64_sve_ptest_any: 2085ffd83dbSDimitry Andric case Intrinsic::aarch64_sve_ptest_first: 2095ffd83dbSDimitry Andric case Intrinsic::aarch64_sve_ptest_last: 2105ffd83dbSDimitry Andric return optimizePTest(IntrI); 2115ffd83dbSDimitry Andric default: 2125ffd83dbSDimitry Andric return false; 2135ffd83dbSDimitry Andric } 2145ffd83dbSDimitry Andric 2155ffd83dbSDimitry Andric return true; 2165ffd83dbSDimitry Andric } 2175ffd83dbSDimitry Andric 2185ffd83dbSDimitry Andric bool SVEIntrinsicOpts::optimizeFunctions( 2195ffd83dbSDimitry Andric SmallSetVector<Function *, 4> &Functions) { 2205ffd83dbSDimitry Andric bool Changed = false; 2215ffd83dbSDimitry Andric for (auto *F : Functions) { 2225ffd83dbSDimitry Andric DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>(*F).getDomTree(); 2235ffd83dbSDimitry Andric 2245ffd83dbSDimitry Andric // Traverse the DT with an rpo walk so we see defs before uses, allowing 2255ffd83dbSDimitry Andric // simplification to be done incrementally. 2265ffd83dbSDimitry Andric BasicBlock *Root = DT->getRoot(); 2275ffd83dbSDimitry Andric ReversePostOrderTraversal<BasicBlock *> RPOT(Root); 2285ffd83dbSDimitry Andric for (auto *BB : RPOT) 2295ffd83dbSDimitry Andric for (Instruction &I : make_early_inc_range(*BB)) 2305ffd83dbSDimitry Andric Changed |= optimizeIntrinsic(&I); 2315ffd83dbSDimitry Andric } 2325ffd83dbSDimitry Andric return Changed; 2335ffd83dbSDimitry Andric } 2345ffd83dbSDimitry Andric 2355ffd83dbSDimitry Andric bool SVEIntrinsicOpts::runOnModule(Module &M) { 2365ffd83dbSDimitry Andric bool Changed = false; 2375ffd83dbSDimitry Andric SmallSetVector<Function *, 4> Functions; 2385ffd83dbSDimitry Andric 2395ffd83dbSDimitry Andric // Check for SVE intrinsic declarations first so that we only iterate over 2405ffd83dbSDimitry Andric // relevant functions. Where an appropriate declaration is found, store the 2415ffd83dbSDimitry Andric // function(s) where it is used so we can target these only. 2425ffd83dbSDimitry Andric for (auto &F : M.getFunctionList()) { 2435ffd83dbSDimitry Andric if (!F.isDeclaration()) 2445ffd83dbSDimitry Andric continue; 2455ffd83dbSDimitry Andric 2465ffd83dbSDimitry Andric switch (F.getIntrinsicID()) { 2475ffd83dbSDimitry Andric case Intrinsic::aarch64_sve_convert_from_svbool: 2485ffd83dbSDimitry Andric case Intrinsic::aarch64_sve_ptest_any: 2495ffd83dbSDimitry Andric case Intrinsic::aarch64_sve_ptest_first: 2505ffd83dbSDimitry Andric case Intrinsic::aarch64_sve_ptest_last: 2515ffd83dbSDimitry Andric for (auto I = F.user_begin(), E = F.user_end(); I != E;) { 2525ffd83dbSDimitry Andric auto *Inst = dyn_cast<Instruction>(*I++); 2535ffd83dbSDimitry Andric Functions.insert(Inst->getFunction()); 2545ffd83dbSDimitry Andric } 2555ffd83dbSDimitry Andric break; 2565ffd83dbSDimitry Andric default: 2575ffd83dbSDimitry Andric break; 2585ffd83dbSDimitry Andric } 2595ffd83dbSDimitry Andric } 2605ffd83dbSDimitry Andric 2615ffd83dbSDimitry Andric if (!Functions.empty()) 2625ffd83dbSDimitry Andric Changed |= optimizeFunctions(Functions); 2635ffd83dbSDimitry Andric 2645ffd83dbSDimitry Andric return Changed; 2655ffd83dbSDimitry Andric } 266