| //===- HashRecognize.cpp ----------------------------------------*- C++ -*-===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // The HashRecognize analysis recognizes unoptimized polynomial hash functions | |
| // with operations over a Galois field of characteristic 2, also called binary | |
| // fields, or GF(2^n). 2^n is termed the order of the Galois field. This class | |
| // of hash functions can be optimized using a lookup-table-driven | |
| // implementation, or with target-specific instructions. | |
| // | |
| // Examples: | |
| // | |
| // 1. Cyclic redundancy check (CRC), which is a polynomial division in GF(2). | |
| // 2. Rabin fingerprint, a component of the Rabin-Karp algorithm, which is a | |
| // rolling hash polynomial division in GF(2). | |
| // 3. Rijndael MixColumns, a step in AES computation, which is a polynomial | |
| // multiplication in GF(2^3). | |
| // 4. GHASH, the authentication mechanism in AES Galois/Counter Mode (GCM), | |
| // which is a polynomial evaluation in GF(2^128). | |
| // | |
| // All of them use an irreducible generating polynomial of degree m, | |
| // | |
| // c_m * x^m + c_(m-1) * x^(m-1) + ... + c_0 * x^0 | |
| // | |
| // where each coefficient c is can take values 0 or 1. The polynomial is simply | |
| // represented by m+1 bits, corresponding to the coefficients. The different | |
| // variants of CRC are named by degree of generating polynomial used: so CRC-32 | |
| // would use a polynomial of degree 32. | |
| // | |
| // The reason algorithms on GF(2^n) can be optimized with a lookup-table is the | |
| // following: in such fields, polynomial addition and subtraction are identical | |
| // and equivalent to XOR, polynomial multiplication is an AND, and polynomial | |
| // division is identity: the XOR and AND operations in unoptimized | |
| // implementations are performed bit-wise, and can be optimized to be performed | |
| // chunk-wise, by interleaving copies of the generating polynomial, and storing | |
| // the pre-computed values in a table. | |
| // | |
| // A generating polynomial of m bits always has the MSB set, so we usually | |
| // omit it. An example of a 16-bit polynomial is the CRC-16-CCITT polynomial: | |
| // | |
| // (x^16) + x^12 + x^5 + 1 = (1) 0001 0000 0010 0001 = 0x1021 | |
| // | |
| // Transmissions are either in big-endian or little-endian form, and hash | |
| // algorithms are written according to this. For example, IEEE 802 and RS-232 | |
| // specify little-endian transmission. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // At the moment, we only recognize the CRC algorithm. | |
| // Documentation on CRC32 from the kernel: | |
| // https://www.kernel.org/doc/Documentation/crc32.txt | |
| // | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Analysis/HashRecognize.h" | |
| #include "llvm/ADT/APInt.h" | |
| #include "llvm/Analysis/LoopAnalysisManager.h" | |
| #include "llvm/Analysis/LoopInfo.h" | |
| #include "llvm/Analysis/ScalarEvolution.h" | |
| #include "llvm/Analysis/ScalarEvolutionPatternMatch.h" | |
| #include "llvm/Analysis/ValueTracking.h" | |
| #include "llvm/IR/PatternMatch.h" | |
| #include "llvm/Support/KnownBits.h" | |
| using namespace llvm; | |
| using namespace PatternMatch; | |
| using namespace SCEVPatternMatch; | |
| #define DEBUG_TYPE "hash-recognize" | |
| /// Checks if there's a stray instruction in the loop \p L outside of the | |
| /// use-def chains from \p Roots, or if we escape the loop during the use-def | |
| /// walk. | |
| static bool containsUnreachable(const Loop &L, | |
| ArrayRef<const Instruction *> Roots) { | |
| SmallPtrSet<const Instruction *, 16> Visited; | |
| BasicBlock *Latch = L.getLoopLatch(); | |
| SmallVector<const Instruction *, 16> Worklist(Roots); | |
| while (!Worklist.empty()) { | |
| const Instruction *I = Worklist.pop_back_val(); | |
| // Skip this instruction if we have already visited it before. | |
| if (!Visited.insert(I).second) | |
| continue; | |
| if (isa<PHINode>(I)) | |
| continue; | |
| for (const Use &U : I->operands()) { | |
| if (auto *UI = dyn_cast<Instruction>(U)) { | |
| if (!L.contains(UI)) | |
| return true; | |
| Worklist.push_back(UI); | |
| } | |
| } | |
| } | |
| return Latch->size() != Visited.size(); | |
| } | |
| /// A structure that can hold either a Simple Recurrence or a Conditional | |
| /// Recurrence. Note that in the case of a Simple Recurrence, Step is an operand | |
| /// of the BO, while in a Conditional Recurrence, it is a SelectInst. | |
| struct RecurrenceInfo { | |
| const Loop &L; | |
| const PHINode *Phi = nullptr; | |
| BinaryOperator *BO = nullptr; | |
| Value *Start = nullptr; | |
| Value *Step = nullptr; | |
| std::optional<APInt> ExtraConst; | |
| RecurrenceInfo(const Loop &L) : L(L) {} | |
| operator bool() const { return BO; } | |
| void print(raw_ostream &OS, unsigned Indent = 0) const { | |
| OS.indent(Indent) << "Phi: "; | |
| Phi->print(OS); | |
| OS << "\n"; | |
| OS.indent(Indent) << "BinaryOperator: "; | |
| BO->print(OS); | |
| OS << "\n"; | |
| OS.indent(Indent) << "Start: "; | |
| Start->print(OS); | |
| OS << "\n"; | |
| OS.indent(Indent) << "Step: "; | |
| Step->print(OS); | |
| OS << "\n"; | |
| if (ExtraConst) { | |
| OS.indent(Indent) << "ExtraConst: "; | |
| ExtraConst->print(OS, false); | |
| OS << "\n"; | |
| } | |
| } | |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | |
| LLVM_DUMP_METHOD void dump() const { print(dbgs()); } | |
| #endif | |
| bool matchSimpleRecurrence(const PHINode *P); | |
| bool matchConditionalRecurrence( | |
| const PHINode *P, | |
| Instruction::BinaryOps BOWithConstOpToMatch = Instruction::BinaryOpsEnd); | |
| private: | |
| BinaryOperator *digRecurrence( | |
| Instruction *V, | |
| Instruction::BinaryOps BOWithConstOpToMatch = Instruction::BinaryOpsEnd); | |
| }; | |
| /// Check the well-formedness of the (most|least) significant bit check given \p | |
| /// ConditionalRecurrence, \p SimpleRecurrence, depending on \p IsBigEndian. We | |
| /// check that ConditionalRecurrence.Step is a Select(Cmp()) where the compare | |
| /// is `>= 0` in the big-endian case, and `== 0` in the little-endian case (or | |
| /// the inverse, in which case the branches of the compare are swapped). For | |
| /// little-endian, we also accept a trunc to i1 (extracting bit zero). We | |
| /// check that the LHS is (ConditionalRecurrence.Phi [xor SimpleRecurrence.Phi]) | |
| /// in the big-endian case, and additionally check for an AND with one in the | |
| /// little-endian case. We then check AllowedByR against CheckAllowedByR, which | |
| /// is [0, smin) in the big-endian case, and is [0, 1) in the little-endian | |
| /// case. CheckAllowedByR checks for significant-bit-clear, and we match the | |
| /// corresponding arms of the select against bit-shift and | |
| /// bit-shift-and-xor-gen-poly. | |
| static bool | |
| isSignificantBitCheckWellFormed(const RecurrenceInfo &ConditionalRecurrence, | |
| const RecurrenceInfo &SimpleRecurrence, | |
| bool IsBigEndian) { | |
| auto *SI = cast<SelectInst>(ConditionalRecurrence.Step); | |
| // Match predicate with or without a SimpleRecurrence (the corresponding data | |
| // is LHSAux). | |
| auto MatchPred = m_CombineOr( | |
| m_Specific(ConditionalRecurrence.Phi), | |
| m_c_Xor(m_ZExtOrTruncOrSelf(m_Specific(ConditionalRecurrence.Phi)), | |
| m_ZExtOrTruncOrSelf(m_Specific(SimpleRecurrence.Phi)))); | |
| BinaryOperator *BitShift = ConditionalRecurrence.BO; | |
| auto MatchBitShiftXorGenPoly = m_c_Xor( | |
| m_Specific(BitShift), m_SpecificInt(*ConditionalRecurrence.ExtraConst)); | |
| if (!IsBigEndian && | |
| match(SI, m_Select(m_Trunc(MatchPred), MatchBitShiftXorGenPoly, | |
| m_Specific(BitShift)))) | |
| return true; | |
| CmpPredicate Pred; | |
| const Value *L; | |
| const APInt *R; | |
| Instruction *TV, *FV; | |
| if (!match(SI, m_Select(m_ICmp(Pred, m_Value(L), m_APInt(R)), | |
| m_Instruction(TV), m_Instruction(FV)))) | |
| return false; | |
| bool LWellFormed = | |
| IsBigEndian ? match(L, MatchPred) : match(L, m_c_And(MatchPred, m_One())); | |
| if (!LWellFormed) | |
| return false; | |
| KnownBits KnownR = KnownBits::makeConstant(*R); | |
| unsigned BW = KnownR.getBitWidth(); | |
| auto RCR = ConstantRange::fromKnownBits(KnownR, false); | |
| auto AllowedByR = ConstantRange::makeAllowedICmpRegion(Pred, RCR); | |
| ConstantRange CheckAllowedByR(APInt::getZero(BW), | |
| IsBigEndian ? APInt::getSignedMinValue(BW) | |
| : APInt(BW, 1)); | |
| if (AllowedByR == CheckAllowedByR) | |
| return TV == BitShift && match(FV, MatchBitShiftXorGenPoly); | |
| if (AllowedByR.inverse() == CheckAllowedByR) | |
| return FV == BitShift && match(TV, MatchBitShiftXorGenPoly); | |
| return false; | |
| } | |
| /// Wraps llvm::matchSimpleRecurrence. Match a simple first order recurrence | |
| /// cycle of the form: | |
| /// | |
| /// loop: | |
| /// %rec = phi [%start, %entry], [%BO, %loop] | |
| /// ... | |
| /// %BO = binop %rec, %step | |
| /// | |
| /// or | |
| /// | |
| /// loop: | |
| /// %rec = phi [%start, %entry], [%BO, %loop] | |
| /// ... | |
| /// %BO = binop %step, %rec | |
| /// | |
| bool RecurrenceInfo::matchSimpleRecurrence(const PHINode *P) { | |
| if (llvm::matchSimpleRecurrence(P, BO, Start, Step)) { | |
| Phi = P; | |
| return true; | |
| } | |
| return false; | |
| } | |
| /// Digs for a recurrence starting with \p V hitting the PHI node in a use-def | |
| /// chain. Used by matchConditionalRecurrence. | |
| BinaryOperator * | |
| RecurrenceInfo::digRecurrence(Instruction *V, | |
| Instruction::BinaryOps BOWithConstOpToMatch) { | |
| SmallVector<Instruction *> Worklist; | |
| SmallPtrSet<Instruction *, 16> Visited; | |
| Worklist.push_back(V); | |
| while (!Worklist.empty()) { | |
| Instruction *I = Worklist.pop_back_val(); | |
| // Skip this instruction if we have already visited it before. | |
| if (!Visited.insert(I).second) | |
| continue; | |
| // Don't add a PHI's operands to the Worklist. | |
| if (isa<PHINode>(I)) | |
| continue; | |
| // Find a recurrence over a BinOp, by matching either of its operands | |
| // with with the PHINode. | |
| if (match(I, m_c_BinOp(m_Value(), m_Specific(Phi)))) | |
| return cast<BinaryOperator>(I); | |
| // Bind to ExtraConst, if we match exactly one. | |
| if (I->getOpcode() == BOWithConstOpToMatch) { | |
| if (ExtraConst) | |
| return nullptr; | |
| const APInt *C = nullptr; | |
| if (match(I, m_c_BinOp(m_APInt(C), m_Value()))) | |
| ExtraConst = *C; | |
| } | |
| // Continue along the use-def chain. | |
| for (Use &U : I->operands()) | |
| if (auto *UI = dyn_cast<Instruction>(U)) | |
| if (L.contains(UI)) | |
| Worklist.push_back(UI); | |
| } | |
| return nullptr; | |
| } | |
| /// A Conditional Recurrence is a recurrence of the form: | |
| /// | |
| /// loop: | |
| /// %rec = phi [%start, %entry], [%step, %loop] | |
| /// ... | |
| /// %step = select _, %tv, %fv | |
| /// | |
| /// where %tv and %fv ultimately end up using %rec via the same %BO instruction, | |
| /// after digging through the use-def chain. | |
| /// | |
| /// ExtraConst is relevant if \p BOWithConstOpToMatch is supplied: when digging | |
| /// the use-def chain, a BinOp with opcode \p BOWithConstOpToMatch is matched, | |
| /// and ExtraConst is a constant operand of that BinOp. This peculiarity exists, | |
| /// because in a CRC algorithm, the \p BOWithConstOpToMatch is an XOR, and the | |
| /// ExtraConst ends up being the generating polynomial. | |
| bool RecurrenceInfo::matchConditionalRecurrence( | |
| const PHINode *P, Instruction::BinaryOps BOWithConstOpToMatch) { | |
| Phi = P; | |
| if (Phi->getNumIncomingValues() != 2) | |
| return false; | |
| // Step comes from the loop latch, start comes from the other incoming value. | |
| int LatchIdx = Phi->getBasicBlockIndex(L.getLoopLatch()); | |
| if (LatchIdx < 0) | |
| return false; | |
| Value *FoundStep = Phi->getIncomingValue(LatchIdx); | |
| Value *FoundStart = Phi->getIncomingValue(!LatchIdx); | |
| Instruction *TV, *FV; | |
| if (!match(FoundStep, m_Select(m_Isa<CmpInst, TruncInst>(), m_Instruction(TV), | |
| m_Instruction(FV)))) | |
| return false; | |
| // For a conditional recurrence, both the true and false values of the | |
| // select must ultimately end up in the same recurrent BinOp. | |
| BinaryOperator *FoundBO = digRecurrence(TV, BOWithConstOpToMatch); | |
| BinaryOperator *AltBO = digRecurrence(FV, BOWithConstOpToMatch); | |
| if (!FoundBO || FoundBO != AltBO) | |
| return false; | |
| if (BOWithConstOpToMatch != Instruction::BinaryOpsEnd && !ExtraConst) { | |
| LLVM_DEBUG(dbgs() << "HashRecognize: Unable to match single BinaryOp " | |
| "with constant in conditional recurrence\n"); | |
| return false; | |
| } | |
| BO = FoundBO; | |
| Start = FoundStart; | |
| Step = FoundStep; | |
| return true; | |
| } | |
| /// Iterates over all the phis in \p LoopLatch, and attempts to extract a | |
| /// Conditional Recurrence and an optional Simple Recurrence. | |
| static std::optional<std::pair<RecurrenceInfo, RecurrenceInfo>> | |
| getRecurrences(BasicBlock *LoopLatch, const PHINode *IndVar, const Loop &L) { | |
| auto Phis = LoopLatch->phis(); | |
| unsigned NumPhis = std::distance(Phis.begin(), Phis.end()); | |
| if (NumPhis != 2 && NumPhis != 3) | |
| return {}; | |
| RecurrenceInfo SimpleRecurrence(L); | |
| RecurrenceInfo ConditionalRecurrence(L); | |
| for (PHINode &P : Phis) { | |
| if (&P == IndVar) | |
| continue; | |
| if (!SimpleRecurrence) | |
| SimpleRecurrence.matchSimpleRecurrence(&P); | |
| if (!ConditionalRecurrence) | |
| ConditionalRecurrence.matchConditionalRecurrence( | |
| &P, Instruction::BinaryOps::Xor); | |
| } | |
| if (NumPhis == 3 && (!SimpleRecurrence || !ConditionalRecurrence)) | |
| return {}; | |
| return std::make_pair(SimpleRecurrence, ConditionalRecurrence); | |
| } | |
| PolynomialInfo::PolynomialInfo(unsigned TripCount, Value *LHS, const APInt &RHS, | |
| Value *ComputedValue, bool IsBigEndian, | |
| Value *LHSAux) | |
| : TripCount(TripCount), LHS(LHS), RHS(RHS), ComputedValue(ComputedValue), | |
| IsBigEndian(IsBigEndian), LHSAux(LHSAux) {} | |
| /// Generate a lookup table of 256 entries by interleaving the generating | |
| /// polynomial. The optimization technique of table-lookup for CRC is also | |
| /// called the Sarwate algorithm. | |
| CRCTable HashRecognize::genSarwateTable(const APInt &GenPoly, | |
| bool IsBigEndian) { | |
| unsigned BW = GenPoly.getBitWidth(); | |
| CRCTable Table; | |
| Table[0] = APInt::getZero(BW); | |
| if (IsBigEndian) { | |
| APInt CRCInit = APInt::getSignedMinValue(BW); | |
| for (unsigned I = 1; I < 256; I <<= 1) { | |
| CRCInit = CRCInit.shl(1) ^ | |
| (CRCInit.isSignBitSet() ? GenPoly : APInt::getZero(BW)); | |
| for (unsigned J = 0; J < I; ++J) | |
| Table[I + J] = CRCInit ^ Table[J]; | |
| } | |
| return Table; | |
| } | |
| APInt CRCInit(BW, 1); | |
| for (unsigned I = 128; I; I >>= 1) { | |
| CRCInit = CRCInit.lshr(1) ^ (CRCInit[0] ? GenPoly : APInt::getZero(BW)); | |
| for (unsigned J = 0; J < 256; J += (I << 1)) | |
| Table[I + J] = CRCInit ^ Table[J]; | |
| } | |
| return Table; | |
| } | |
| /// Perform polynomial (GF(2)) floor division. This is based on the | |
| /// floor_division(S, P) algorithm in | |
| /// https://www.corsix.org/content/barrett-reduction-polynomials. Note that the | |
| /// maximum degree of the returned polynomial is | |
| /// max(0, deg(Dividend) - deg(Divisor)), but the bit width will be the same as | |
| /// that of Dividend. | |
| static APInt floorDivideGF2(APInt Dividend, APInt Divisor) { | |
| assert(!Divisor.isZero() && "Cannot divide by zero"); | |
| // Extend the divisor bit width to match the dividend. | |
| Divisor = Divisor.zext(Dividend.getBitWidth()); | |
| // Note that getActiveBits returns deg+1, but the computation below | |
| // still holds. | |
| unsigned DivisorActiveBits = Divisor.getActiveBits(); | |
| // Q = 0 | |
| APInt Quotient = APInt::getZero(Dividend.getBitWidth()); | |
| // S != 0 and deg(S) >= deg(P) | |
| // (S != 0 implied by DivisorActiveBits > 0) | |
| while (Dividend.getActiveBits() >= DivisorActiveBits) { | |
| // T = S[deg(S)] / P[deg(P)] | |
| unsigned Shift = Dividend.getActiveBits() - DivisorActiveBits; | |
| // Q = Q + T | |
| Quotient.setBit(Shift); | |
| // S = S - T * P | |
| Dividend ^= Divisor.shl(Shift); | |
| } | |
| return Quotient; | |
| } | |
| /// Generate the constants for performing a Polynomial (GF(2)) Barrett Reduction | |
| /// according to Intel's Fast CRC Computation white paper with some adjustments | |
| /// to account for the fact that bit width and trip count can vary. | |
| std::pair<APInt, APInt> | |
| HashRecognize::genBarrettConstants(const PolynomialInfo &Info) { | |
| unsigned BW = Info.RHS.getBitWidth(); | |
| unsigned TC = Info.TripCount; | |
| // Recover the full generating polynomial in normal form by reflecting the LE | |
| // case and adding the implied x^BW term. | |
| // deg(P(x)) = BW due to the implied term, and thus P(x) must fit in exactly | |
| // BW+1 bits. | |
| APInt FullGenPoly = | |
| (Info.IsBigEndian ? Info.RHS : Info.RHS.reverseBits()).zext(BW + 1); | |
| FullGenPoly.setBit(BW); | |
| // Calculate mu = floor(x^(BW+TC) / P(x)). | |
| // deg(mu) <= deg(x^(BW+TC)) - deg(P(x)) = BW+TC - BW = TC, and thus mu must | |
| // fit in at most TC+1 bits. | |
| unsigned DivBW = BW + TC + 1; | |
| APInt Mu = floorDivideGF2(APInt::getOneBitSet(DivBW, BW + TC), FullGenPoly) | |
| .trunc(TC + 1); | |
| // In the bit-reflected (little-endian) case, mu and P(x) must be | |
| // bit-reflected across their respective widths for the corresponding Barrett | |
| // reduction steps. | |
| if (!Info.IsBigEndian) { | |
| Mu = Mu.reverseBits(); | |
| FullGenPoly = FullGenPoly.reverseBits(); | |
| } | |
| return {Mu, FullGenPoly}; | |
| } | |
| /// Checks that \p P1 and \p P2 are used together in an XOR in the use-def chain | |
| /// of \p SI's condition, ignoring any casts. The purpose of this function is to | |
| /// ensure that LHSAux from the SimpleRecurrence is used correctly in the CRC | |
| /// computation. | |
| /// | |
| /// In other words, it checks for the following pattern: | |
| /// | |
| /// loop: | |
| /// %P1 = phi [_, %entry], [%P1.next, %loop] | |
| /// %P2 = phi [_, %entry], [%P2.next, %loop] | |
| /// ... | |
| /// %xor = xor (CastOrSelf %P1), (CastOrSelf %P2) | |
| /// | |
| /// where %xor is in the use-def chain of \p SI's condition. | |
| static bool isConditionalOnXorOfPHIs(const SelectInst *SI, const PHINode *P1, | |
| const PHINode *P2, const Loop &L) { | |
| SmallVector<const Instruction *> Worklist; | |
| SmallPtrSet<const Instruction *, 16> Visited; | |
| // matchConditionalRecurrence has already ensured that the SelectInst's | |
| // condition is an Instruction. | |
| Worklist.push_back(cast<Instruction>(SI->getCondition())); | |
| while (!Worklist.empty()) { | |
| const Instruction *I = Worklist.pop_back_val(); | |
| // Skip this instruction if we have already visited it before. | |
| if (!Visited.insert(I).second) | |
| continue; | |
| // Don't add a PHI's operands to the Worklist. | |
| if (isa<PHINode>(I)) | |
| continue; | |
| // If we match an XOR of the two PHIs ignoring casts, we're done. | |
| if (match(I, m_c_Xor(m_ZExtOrTruncOrSelf(m_Specific(P1)), | |
| m_ZExtOrTruncOrSelf(m_Specific(P2))))) | |
| return true; | |
| // Continue along the use-def chain. | |
| for (const Use &U : I->operands()) | |
| if (auto *UI = dyn_cast<Instruction>(U)) | |
| if (L.contains(UI)) | |
| Worklist.push_back(UI); | |
| } | |
| return false; | |
| } | |
| // Recognizes a multiplication or division by the constant two, using SCEV. By | |
| // doing this, we're immune to whether the IR expression is mul/udiv or | |
| // equivalently shl/lshr. Return false when it is a UDiv, true when it is a Mul, | |
| // and std::nullopt otherwise. | |
| static std::optional<bool> isBigEndianBitShift(Value *V, ScalarEvolution &SE) { | |
| if (!V->getType()->isIntegerTy()) | |
| return {}; | |
| const SCEV *E = SE.getSCEV(V); | |
| if (match(E, m_scev_UDiv(m_SCEV(), m_scev_SpecificInt(2)))) | |
| return false; | |
| if (match(E, m_scev_Mul(m_scev_SpecificInt(2), m_SCEV()))) | |
| return true; | |
| return {}; | |
| } | |
| /// The main entry point for analyzing a loop and recognizing the CRC algorithm. | |
| /// Returns a PolynomialInfo on success, and a StringRef on failure. | |
| std::variant<PolynomialInfo, StringRef> HashRecognize::recognizeCRC() const { | |
| if (!L.isInnermost()) | |
| return "Loop is not innermost"; | |
| BasicBlock *Latch = L.getLoopLatch(); | |
| BasicBlock *Exit = L.getExitBlock(); | |
| const PHINode *IndVar = L.getCanonicalInductionVariable(); | |
| if (!Latch || !Exit || !IndVar || L.getNumBlocks() != 1 || | |
| !L.getLatchCmpInst()) | |
| return "Loop not in canonical form"; | |
| unsigned TC = SE.getSmallConstantTripCount(&L); | |
| if (!TC) | |
| return "Unable to find a small constant trip count"; | |
| auto R = getRecurrences(Latch, IndVar, L); | |
| if (!R) | |
| return "Found stray PHI"; | |
| auto [SimpleRecurrence, ConditionalRecurrence] = *R; | |
| if (!ConditionalRecurrence) | |
| return "Unable to find conditional recurrence"; | |
| // Make sure that all recurrences are either all SCEVMul with two or SCEVDiv | |
| // with two, or in other words, that they're single bit-shifts. | |
| std::optional<bool> IsBigEndian = | |
| isBigEndianBitShift(ConditionalRecurrence.BO, SE); | |
| if (!IsBigEndian) | |
| return "Loop with non-unit bitshifts"; | |
| if (SimpleRecurrence) { | |
| if (isBigEndianBitShift(SimpleRecurrence.BO, SE) != IsBigEndian) | |
| return "Loop with non-unit bitshifts"; | |
| // Ensure that the PHIs have exactly two uses: | |
| // the bit-shift, and the XOR (or a cast feeding into the XOR). | |
| // Also ensure that the SimpleRecurrence's evolution doesn't have stray | |
| // users. | |
| if (!ConditionalRecurrence.Phi->hasNUses(2) || | |
| !SimpleRecurrence.Phi->hasNUses(2) || | |
| SimpleRecurrence.BO->getUniqueUndroppableUser() != SimpleRecurrence.Phi) | |
| return "Recurrences have stray uses"; | |
| // Check that the SelectInst ConditionalRecurrence.Step is conditional on | |
| // the XOR of SimpleRecurrence.Phi and ConditionalRecurrence.Phi. | |
| if (!isConditionalOnXorOfPHIs(cast<SelectInst>(ConditionalRecurrence.Step), | |
| SimpleRecurrence.Phi, | |
| ConditionalRecurrence.Phi, L)) | |
| return "Recurrences not intertwined with XOR"; | |
| } | |
| Value *LHS = ConditionalRecurrence.Start; | |
| Value *LHSAux = SimpleRecurrence ? SimpleRecurrence.Start : nullptr; | |
| // In the big-endian case where LHSAux is narrower than LHS, the most | |
| // significant bit check will never be influenced by LHSAux. In this case, | |
| // SimpleRecurrence must still be well-formed, but LHSAux is effectively dead. | |
| // This also averts a possible miscompile later where LHSAux gets shifted by | |
| // its entire bit width, creating poison. | |
| if (*IsBigEndian && LHSAux && | |
| LHSAux->getType()->getIntegerBitWidth() < | |
| LHS->getType()->getIntegerBitWidth()) | |
| LHSAux = nullptr; | |
| // Make sure that the TC doesn't exceed the bitwidth of LHSAux, or LHS. | |
| if (TC > (LHSAux ? LHSAux->getType()->getIntegerBitWidth() | |
| : LHS->getType()->getIntegerBitWidth())) | |
| return "Loop iterations exceed bitwidth of data"; | |
| // Ensure nothing other than the computed value makes its way out of the loop. | |
| // Since the loop is in LCSSA form, this is as simple as checking the PHI | |
| // nodes in the exit block. | |
| auto *ComputedValue = cast<SelectInst>(ConditionalRecurrence.Step); | |
| if (any_of(Exit->phis(), [Latch, ComputedValue](PHINode &PN) { | |
| return PN.getIncomingValueForBlock(Latch) != ComputedValue; | |
| })) | |
| return "Found stray incoming values in loop exit block"; | |
| assert(ConditionalRecurrence.ExtraConst && | |
| "Expected ExtraConst in conditional recurrence"); | |
| const APInt &GenPoly = *ConditionalRecurrence.ExtraConst; | |
| if (!isSignificantBitCheckWellFormed(ConditionalRecurrence, SimpleRecurrence, | |
| *IsBigEndian)) | |
| return "Malformed significant-bit check"; | |
| SmallVector<const Instruction *> Roots( | |
| {ComputedValue, | |
| cast<Instruction>(IndVar->getIncomingValueForBlock(Latch)), | |
| L.getLatchCmpInst(), Latch->getTerminator()}); | |
| if (SimpleRecurrence) | |
| Roots.push_back(SimpleRecurrence.BO); | |
| if (containsUnreachable(L, Roots)) | |
| return "Found stray unvisited instructions"; | |
| return PolynomialInfo(TC, LHS, GenPoly, ComputedValue, *IsBigEndian, LHSAux); | |
| } | |
| void CRCTable::print(raw_ostream &OS) const { | |
| for (unsigned I = 0; I < 256; I++) { | |
| (*this)[I].print(OS, false); | |
| OS << (I % 16 == 15 ? '\n' : ' '); | |
| } | |
| } | |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | |
| void CRCTable::dump() const { print(dbgs()); } | |
| #endif | |
| void HashRecognize::print(raw_ostream &OS) const { | |
| if (!L.isInnermost()) | |
| return; | |
| OS << "HashRecognize: Checking a loop in '" | |
| << L.getHeader()->getParent()->getName() << "' from " << L.getLocStr() | |
| << "\n"; | |
| auto Ret = recognizeCRC(); | |
| if (!std::holds_alternative<PolynomialInfo>(Ret)) { | |
| OS << "Did not find a hash algorithm\n"; | |
| if (std::holds_alternative<StringRef>(Ret)) | |
| OS << "Reason: " << std::get<StringRef>(Ret) << "\n"; | |
| return; | |
| } | |
| auto Info = std::get<PolynomialInfo>(Ret); | |
| OS << "Found" << (Info.IsBigEndian ? " big-endian " : " little-endian ") | |
| << "CRC-" << Info.RHS.getBitWidth() << " loop with trip count " | |
| << Info.TripCount << "\n"; | |
| OS.indent(2) << "Initial CRC: "; | |
| Info.LHS->print(OS); | |
| OS << "\n"; | |
| OS.indent(2) << "Generating polynomial: "; | |
| Info.RHS.print(OS, false); | |
| OS << "\n"; | |
| OS.indent(2) << "Computed CRC: "; | |
| Info.ComputedValue->print(OS); | |
| OS << "\n"; | |
| if (Info.LHSAux) { | |
| OS.indent(2) << "Auxiliary data: "; | |
| Info.LHSAux->print(OS); | |
| OS << "\n"; | |
| } | |
| OS.indent(2) << "Computed CRC lookup table:\n"; | |
| genSarwateTable(Info.RHS, Info.IsBigEndian).print(OS); | |
| OS.indent(2) << "Computed CRC Barrett constants:\n"; | |
| auto [Mu, FullGenPoly] = genBarrettConstants(Info); | |
| OS << "Mu = "; | |
| Mu.print(OS, false); | |
| OS << ", FullGenPoly = "; | |
| FullGenPoly.print(OS, false); | |
| OS << "\n"; | |
| } | |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) | |
| void HashRecognize::dump() const { print(dbgs()); } | |
| #endif | |
| std::optional<PolynomialInfo> HashRecognize::getResult() const { | |
| auto Res = HashRecognize(L, SE).recognizeCRC(); | |
| if (std::holds_alternative<PolynomialInfo>(Res)) | |
| return std::get<PolynomialInfo>(Res); | |
| return std::nullopt; | |
| } | |
| HashRecognize::HashRecognize(const Loop &L, ScalarEvolution &SE) | |
| : L(L), SE(SE) {} | |
| PreservedAnalyses HashRecognizePrinterPass::run(Loop &L, | |
| LoopAnalysisManager &AM, | |
| LoopStandardAnalysisResults &AR, | |
| LPMUpdater &) { | |
| HashRecognize(L, AR.SE).print(OS); | |
| return PreservedAnalyses::all(); | |
| } |