| //===- HexagonGenExtract.cpp ----------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include "llvm/ADT/APInt.h" | 
 | #include "llvm/ADT/GraphTraits.h" | 
 | #include "llvm/IR/BasicBlock.h" | 
 | #include "llvm/IR/CFG.h" | 
 | #include "llvm/IR/Constants.h" | 
 | #include "llvm/IR/Dominators.h" | 
 | #include "llvm/IR/Function.h" | 
 | #include "llvm/IR/IRBuilder.h" | 
 | #include "llvm/IR/Instruction.h" | 
 | #include "llvm/IR/Instructions.h" | 
 | #include "llvm/IR/Intrinsics.h" | 
 | #include "llvm/IR/IntrinsicsHexagon.h" | 
 | #include "llvm/IR/PatternMatch.h" | 
 | #include "llvm/IR/Type.h" | 
 | #include "llvm/IR/Value.h" | 
 | #include "llvm/InitializePasses.h" | 
 | #include "llvm/Pass.h" | 
 | #include "llvm/Support/CommandLine.h" | 
 | #include <algorithm> | 
 | #include <cstdint> | 
 | #include <iterator> | 
 |  | 
 | using namespace llvm; | 
 |  | 
 | static cl::opt<unsigned> ExtractCutoff("extract-cutoff", cl::init(~0U), | 
 |   cl::Hidden, cl::desc("Cutoff for generating \"extract\"" | 
 |   " instructions")); | 
 |  | 
 | // This prevents generating extract instructions that have the offset of 0. | 
 | // One of the reasons for "extract" is to put a sequence of bits in a regis- | 
 | // ter, starting at offset 0 (so that these bits can then be used by an | 
 | // "insert"). If the bits are already at offset 0, it is better not to gene- | 
 | // rate "extract", since logical bit operations can be merged into compound | 
 | // instructions (as opposed to "extract"). | 
 | static cl::opt<bool> NoSR0("extract-nosr0", cl::init(true), cl::Hidden, | 
 |   cl::desc("No extract instruction with offset 0")); | 
 |  | 
 | static cl::opt<bool> NeedAnd("extract-needand", cl::init(true), cl::Hidden, | 
 |   cl::desc("Require & in extract patterns")); | 
 |  | 
 | namespace llvm { | 
 |  | 
 | void initializeHexagonGenExtractPass(PassRegistry&); | 
 | FunctionPass *createHexagonGenExtract(); | 
 |  | 
 | } // end namespace llvm | 
 |  | 
 | namespace { | 
 |  | 
 |   class HexagonGenExtract : public FunctionPass { | 
 |   public: | 
 |     static char ID; | 
 |  | 
 |     HexagonGenExtract() : FunctionPass(ID) { | 
 |       initializeHexagonGenExtractPass(*PassRegistry::getPassRegistry()); | 
 |     } | 
 |  | 
 |     StringRef getPassName() const override { | 
 |       return "Hexagon generate \"extract\" instructions"; | 
 |     } | 
 |  | 
 |     bool runOnFunction(Function &F) override; | 
 |  | 
 |     void getAnalysisUsage(AnalysisUsage &AU) const override { | 
 |       AU.addRequired<DominatorTreeWrapperPass>(); | 
 |       AU.addPreserved<DominatorTreeWrapperPass>(); | 
 |       FunctionPass::getAnalysisUsage(AU); | 
 |     } | 
 |  | 
 |   private: | 
 |     bool visitBlock(BasicBlock *B); | 
 |     bool convert(Instruction *In); | 
 |  | 
 |     unsigned ExtractCount = 0; | 
 |     DominatorTree *DT; | 
 |   }; | 
 |  | 
 | } // end anonymous namespace | 
 |  | 
 | char HexagonGenExtract::ID = 0; | 
 |  | 
 | INITIALIZE_PASS_BEGIN(HexagonGenExtract, "hextract", "Hexagon generate " | 
 |   "\"extract\" instructions", false, false) | 
 | INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) | 
 | INITIALIZE_PASS_END(HexagonGenExtract, "hextract", "Hexagon generate " | 
 |   "\"extract\" instructions", false, false) | 
 |  | 
 | bool HexagonGenExtract::convert(Instruction *In) { | 
 |   using namespace PatternMatch; | 
 |  | 
 |   Value *BF = nullptr; | 
 |   ConstantInt *CSL = nullptr, *CSR = nullptr, *CM = nullptr; | 
 |   BasicBlock *BB = In->getParent(); | 
 |   LLVMContext &Ctx = BB->getContext(); | 
 |   bool LogicalSR; | 
 |  | 
 |   // (and (shl (lshr x, #sr), #sl), #m) | 
 |   LogicalSR = true; | 
 |   bool Match = match(In, m_And(m_Shl(m_LShr(m_Value(BF), m_ConstantInt(CSR)), | 
 |                                m_ConstantInt(CSL)), | 
 |                          m_ConstantInt(CM))); | 
 |  | 
 |   if (!Match) { | 
 |     // (and (shl (ashr x, #sr), #sl), #m) | 
 |     LogicalSR = false; | 
 |     Match = match(In, m_And(m_Shl(m_AShr(m_Value(BF), m_ConstantInt(CSR)), | 
 |                             m_ConstantInt(CSL)), | 
 |                       m_ConstantInt(CM))); | 
 |   } | 
 |   if (!Match) { | 
 |     // (and (shl x, #sl), #m) | 
 |     LogicalSR = true; | 
 |     CSR = ConstantInt::get(Type::getInt32Ty(Ctx), 0); | 
 |     Match = match(In, m_And(m_Shl(m_Value(BF), m_ConstantInt(CSL)), | 
 |                       m_ConstantInt(CM))); | 
 |     if (Match && NoSR0) | 
 |       return false; | 
 |   } | 
 |   if (!Match) { | 
 |     // (and (lshr x, #sr), #m) | 
 |     LogicalSR = true; | 
 |     CSL = ConstantInt::get(Type::getInt32Ty(Ctx), 0); | 
 |     Match = match(In, m_And(m_LShr(m_Value(BF), m_ConstantInt(CSR)), | 
 |                             m_ConstantInt(CM))); | 
 |   } | 
 |   if (!Match) { | 
 |     // (and (ashr x, #sr), #m) | 
 |     LogicalSR = false; | 
 |     CSL = ConstantInt::get(Type::getInt32Ty(Ctx), 0); | 
 |     Match = match(In, m_And(m_AShr(m_Value(BF), m_ConstantInt(CSR)), | 
 |                             m_ConstantInt(CM))); | 
 |   } | 
 |   if (!Match) { | 
 |     CM = nullptr; | 
 |     // (shl (lshr x, #sr), #sl) | 
 |     LogicalSR = true; | 
 |     Match = match(In, m_Shl(m_LShr(m_Value(BF), m_ConstantInt(CSR)), | 
 |                             m_ConstantInt(CSL))); | 
 |   } | 
 |   if (!Match) { | 
 |     CM = nullptr; | 
 |     // (shl (ashr x, #sr), #sl) | 
 |     LogicalSR = false; | 
 |     Match = match(In, m_Shl(m_AShr(m_Value(BF), m_ConstantInt(CSR)), | 
 |                             m_ConstantInt(CSL))); | 
 |   } | 
 |   if (!Match) | 
 |     return false; | 
 |  | 
 |   Type *Ty = BF->getType(); | 
 |   if (!Ty->isIntegerTy()) | 
 |     return false; | 
 |   unsigned BW = Ty->getPrimitiveSizeInBits(); | 
 |   if (BW != 32 && BW != 64) | 
 |     return false; | 
 |  | 
 |   uint32_t SR = CSR->getZExtValue(); | 
 |   uint32_t SL = CSL->getZExtValue(); | 
 |  | 
 |   if (!CM) { | 
 |     // If there was no and, and the shift left did not remove all potential | 
 |     // sign bits created by the shift right, then extractu cannot reproduce | 
 |     // this value. | 
 |     if (!LogicalSR && (SR > SL)) | 
 |       return false; | 
 |     APInt A = APInt(BW, ~0ULL).lshr(SR).shl(SL); | 
 |     CM = ConstantInt::get(Ctx, A); | 
 |   } | 
 |  | 
 |   // CM is the shifted-left mask. Shift it back right to remove the zero | 
 |   // bits on least-significant positions. | 
 |   APInt M = CM->getValue().lshr(SL); | 
 |   uint32_t T = M.countTrailingOnes(); | 
 |  | 
 |   // During the shifts some of the bits will be lost. Calculate how many | 
 |   // of the original value will remain after shift right and then left. | 
 |   uint32_t U = BW - std::max(SL, SR); | 
 |   // The width of the extracted field is the minimum of the original bits | 
 |   // that remain after the shifts and the number of contiguous 1s in the mask. | 
 |   uint32_t W = std::min(U, T); | 
 |   if (W == 0 || W == 1) | 
 |     return false; | 
 |  | 
 |   // Check if the extracted bits are contained within the mask that it is | 
 |   // and-ed with. The extract operation will copy these bits, and so the | 
 |   // mask cannot any holes in it that would clear any of the bits of the | 
 |   // extracted field. | 
 |   if (!LogicalSR) { | 
 |     // If the shift right was arithmetic, it could have included some 1 bits. | 
 |     // It is still ok to generate extract, but only if the mask eliminates | 
 |     // those bits (i.e. M does not have any bits set beyond U). | 
 |     APInt C = APInt::getHighBitsSet(BW, BW-U); | 
 |     if (M.intersects(C) || !M.isMask(W)) | 
 |       return false; | 
 |   } else { | 
 |     // Check if M starts with a contiguous sequence of W times 1 bits. Get | 
 |     // the low U bits of M (which eliminates the 0 bits shifted in on the | 
 |     // left), and check if the result is APInt's "mask": | 
 |     if (!M.getLoBits(U).isMask(W)) | 
 |       return false; | 
 |   } | 
 |  | 
 |   IRBuilder<> IRB(In); | 
 |   Intrinsic::ID IntId = (BW == 32) ? Intrinsic::hexagon_S2_extractu | 
 |                                    : Intrinsic::hexagon_S2_extractup; | 
 |   Module *Mod = BB->getParent()->getParent(); | 
 |   Function *ExtF = Intrinsic::getDeclaration(Mod, IntId); | 
 |   Value *NewIn = IRB.CreateCall(ExtF, {BF, IRB.getInt32(W), IRB.getInt32(SR)}); | 
 |   if (SL != 0) | 
 |     NewIn = IRB.CreateShl(NewIn, SL, CSL->getName()); | 
 |   In->replaceAllUsesWith(NewIn); | 
 |   return true; | 
 | } | 
 |  | 
 | bool HexagonGenExtract::visitBlock(BasicBlock *B) { | 
 |   // Depth-first, bottom-up traversal. | 
 |   for (auto *DTN : children<DomTreeNode*>(DT->getNode(B))) | 
 |     visitBlock(DTN->getBlock()); | 
 |  | 
 |   // Allow limiting the number of generated extracts for debugging purposes. | 
 |   bool HasCutoff = ExtractCutoff.getPosition(); | 
 |   unsigned Cutoff = ExtractCutoff; | 
 |  | 
 |   bool Changed = false; | 
 |   BasicBlock::iterator I = std::prev(B->end()), NextI, Begin = B->begin(); | 
 |   while (true) { | 
 |     if (HasCutoff && (ExtractCount >= Cutoff)) | 
 |       return Changed; | 
 |     bool Last = (I == Begin); | 
 |     if (!Last) | 
 |       NextI = std::prev(I); | 
 |     Instruction *In = &*I; | 
 |     bool Done = convert(In); | 
 |     if (HasCutoff && Done) | 
 |       ExtractCount++; | 
 |     Changed |= Done; | 
 |     if (Last) | 
 |       break; | 
 |     I = NextI; | 
 |   } | 
 |   return Changed; | 
 | } | 
 |  | 
 | bool HexagonGenExtract::runOnFunction(Function &F) { | 
 |   if (skipFunction(F)) | 
 |     return false; | 
 |  | 
 |   DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree(); | 
 |   bool Changed; | 
 |  | 
 |   // Traverse the function bottom-up, to see super-expressions before their | 
 |   // sub-expressions. | 
 |   BasicBlock *Entry = GraphTraits<Function*>::getEntryNode(&F); | 
 |   Changed = visitBlock(Entry); | 
 |  | 
 |   return Changed; | 
 | } | 
 |  | 
 | FunctionPass *llvm::createHexagonGenExtract() { | 
 |   return new HexagonGenExtract(); | 
 | } |