xref: /freebsd-src/contrib/llvm-project/llvm/lib/Target/AArch64/SVEIntrinsicOpts.cpp (revision 75b4d546cd9d9f62a5a3466e6df5629262aef7b1)
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