diff --git a/llvm/include/llvm/Analysis/HashRecognize.h b/llvm/include/llvm/Analysis/HashRecognize.h new file mode 100644 index 0000000000000000000000000000000000000000..6dea3d24885ffacf3087642c083b5f71e4124b8d --- /dev/null +++ b/llvm/include/llvm/Analysis/HashRecognize.h @@ -0,0 +1,107 @@ +//===- HashRecognize.h ------------------------------------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// Interface for the HashRecognize analysis, which identifies hash functions +// that can be optimized using a lookup-table or with target-specific +// instructions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_HASHRECOGNIZE_H +#define LLVM_ANALYSIS_HASHRECOGNIZE_H + +#include "llvm/ADT/APInt.h" +#include "llvm/Analysis/LoopAnalysisManager.h" +#include "llvm/Analysis/ScalarEvolution.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/Value.h" +#include + +namespace llvm { + +class LPMUpdater; + +/// A custom std::array with 256 entries, that also has a print function. +struct CRCTable : public std::array { + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const; +#endif +}; + +/// The structure that is returned when a polynomial algorithm was recognized by +/// the analysis. Currently, only the CRC algorithm is recognized. +struct PolynomialInfo { + // The small constant trip-count of the analyzed loop. + unsigned TripCount; + + // The LHS in a polynomial operation, or the initial variable of the + // computation, since all polynomial operations must have a constant RHS, + // which is the generating polynomial. It is the LHS of the polynomial + // division in the case of CRC. Since polynomial division is an XOR in + // GF(2^m), this variable must be XOR'ed with RHS in a loop to yield the + // ComputedValue. + Value *LHS; + + // The generating polynomial, or the RHS of the polynomial division in the + // case of CRC. + APInt RHS; + + // The final computed value. This is a remainder of a polynomial division in + // the case of CRC, which must be zero. + Value *ComputedValue; + + // Set to true in the case of big-endian. + bool ByteOrderSwapped; + + // An optional auxiliary checksum that augments the LHS. In the case of CRC, + // it is XOR'ed with the LHS, so that the computation's final remainder is + // zero. + Value *LHSAux; + + PolynomialInfo(unsigned TripCount, Value *LHS, const APInt &RHS, + Value *ComputedValue, bool ByteOrderSwapped, + Value *LHSAux = nullptr); +}; + +/// The analysis. +class HashRecognize { + const Loop &L; + ScalarEvolution &SE; + +public: + HashRecognize(const Loop &L, ScalarEvolution &SE); + + // The main analysis entry points. + std::variant recognizeCRC() const; + std::optional getResult() const; + + // Auxilary entry point after analysis to interleave the generating polynomial + // and return a 256-entry CRC table. + static CRCTable genSarwateTable(const APInt &GenPoly, bool ByteOrderSwapped); + + void print(raw_ostream &OS) const; + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + LLVM_DUMP_METHOD void dump() const; +#endif +}; + +class HashRecognizePrinterPass + : public PassInfoMixin { + raw_ostream &OS; + +public: + explicit HashRecognizePrinterPass(raw_ostream &OS) : OS(OS) {} + PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, LPMUpdater &); +}; +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Analysis/TargetTransformInfo.h b/llvm/include/llvm/Analysis/TargetTransformInfo.h index 7a18a834410d927bcdf31ba6db4f69e8e9e384c7..c25f430ed38d56731c9c662074b278cae4ec026b 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfo.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfo.h @@ -30,8 +30,11 @@ #include "llvm/Support/BranchProbability.h" #include "llvm/Support/InstructionCost.h" #include +#include #include +#include #include +#include namespace llvm { @@ -1671,6 +1674,16 @@ public: /// \return The maximum number of function arguments the target supports. unsigned getMaxNumArgs() const; + enum class SupportCryptoAlgs : unsigned { Crc32 }; + enum class CryptoWidth : unsigned { i8 = 1, i16 = 2, i32 = 4 }; + using IntrinsicWidth = std::pair; + using IntrinsicAlg = std::vector; + using IntrinsicAlgMap = std::map; + using EnabledSet = std::set; + + /// get and check Crypto Algorithm Supprot and intrinsic + std::optional getCryptoIntrinsic(const TTI::SupportCryptoAlgs Name) const; + /// @} private: @@ -2026,7 +2039,10 @@ public: virtual bool preferPredicatedReductionSelect(unsigned Opcode, Type *Ty, ReductionFlags) const = 0; virtual bool preferEpilogueVectorization() const = 0; - + virtual std::optional getCryptoIntrinsic( + const TTI::SupportCryptoAlgs Name) const { + return {}; + } virtual bool shouldExpandReduction(const IntrinsicInst *II) const = 0; virtual unsigned getGISelRematGlobalCost() const = 0; virtual unsigned getMinTripCountTailFoldingThreshold() const = 0; @@ -2751,6 +2767,11 @@ public: unsigned getMaxNumArgs() const override { return Impl.getMaxNumArgs(); } + + std::optional getCryptoIntrinsic( + const TTI::SupportCryptoAlgs Name) const override { + return Impl.getCryptoIntrinsic(Name); + } }; template diff --git a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h index 14e635127b59d4f8814c5526bd5b1c888a3e1019..a8156e02a4ea500aea3dd033dc6fa1b967ff10d1 100644 --- a/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h +++ b/llvm/include/llvm/Analysis/TargetTransformInfoImpl.h @@ -893,6 +893,11 @@ public: unsigned getMaxNumArgs() const { return UINT_MAX; } + std::optional getCryptoIntrinsic( + const TargetTransformInfo::SupportCryptoAlgs Name) const { + return {}; + } + protected: // Obtain the minimum required size to hold the value (without the sign) // In case of a vector it returns the min required size for one element. diff --git a/llvm/include/llvm/CodeGen/BasicTTIImpl.h b/llvm/include/llvm/CodeGen/BasicTTIImpl.h index b8b74f6ab278b1e3a1d3eadbd75bdd22b33fe62b..840c0294675eef4cc8ed22ba5134774d21e8c55b 100644 --- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h +++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h @@ -710,6 +710,10 @@ public: return getST()->shouldPrefetchAddressSpace(AS); } + virtual std::optional getCryptoIntrinsic( + const TTI::SupportCryptoAlgs Name) const { + return {}; + } /// @} /// \name Vector TTI Implementations diff --git a/llvm/include/llvm/IR/BasicBlock.h b/llvm/include/llvm/IR/BasicBlock.h index 19bf9549a8caec0f11472a3584ef604f173b93e9..3e1c7ebe1033ce5d3602fa21a8df8271ab544516 100644 --- a/llvm/include/llvm/IR/BasicBlock.h +++ b/llvm/include/llvm/IR/BasicBlock.h @@ -173,6 +173,12 @@ public: static_cast(this)->getFirstNonPHI()); } + const_iterator getFirstNonPHIIt() const; + iterator getFirstNonPHIIt() { + return static_cast( + this)->getFirstNonPHIIt().getNonConst(); + } + /// Returns a pointer to the first instruction in this block that is not a /// PHINode or a debug intrinsic, or any pseudo operation if \c SkipPseudoOp /// is true. diff --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h index 621eba6bd0b679b80e912b5ced9994fc079e823d..09e4de23956273f718764e32ce8c00f1c2a18583 100644 --- a/llvm/include/llvm/IR/PatternMatch.h +++ b/llvm/include/llvm/IR/PatternMatch.h @@ -1637,6 +1637,7 @@ inline CastClass_match m_IntToPtr(const OpTy &Op) { return CastClass_match(Op); } + /// Matches Trunc. template inline CastClass_match m_Trunc(const OpTy &Op) { @@ -1689,6 +1690,15 @@ m_ZExtOrSExtOrSelf(const OpTy &Op) { return m_CombineOr(m_ZExtOrSExt(Op), Op); } +template +inline match_combine_or< + match_combine_or, + CastClass_match>, + OpTy> +m_ZExtOrTruncOrSelf(const OpTy &Op) { + return m_CombineOr(m_CombineOr(m_ZExt(Op), m_Trunc(Op)), Op); +} + template inline CastClass_match m_UIToFP(const OpTy &Op) { return CastClass_match(Op); diff --git a/llvm/lib/Analysis/CMakeLists.txt b/llvm/lib/Analysis/CMakeLists.txt index b35764c7fb4b4858b588b23aa897feaa32b743c4..43f0f9a8813518fe5bf88ad96f4c6979a33e189a 100644 --- a/llvm/lib/Analysis/CMakeLists.txt +++ b/llvm/lib/Analysis/CMakeLists.txt @@ -94,6 +94,7 @@ add_llvm_component_library(LLVMAnalysis FunctionPropertiesAnalysis.cpp GlobalsModRef.cpp GuardUtils.cpp + HashRecognize.cpp HeatUtils.cpp IRSimilarityIdentifier.cpp IVDescriptors.cpp diff --git a/llvm/lib/Analysis/HashRecognize.cpp b/llvm/lib/Analysis/HashRecognize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..098f9d70897d9d44db49063ae47d9821cf581658 --- /dev/null +++ b/llvm/lib/Analysis/HashRecognize.cpp @@ -0,0 +1,602 @@ +//===- 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/ScalarEvolutionExpressions.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/IR/PatternMatch.h" +#include "llvm/Support/KnownBits.h" + +using namespace llvm; +using namespace PatternMatch; + +#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 Roots) { + SmallPtrSet Visited; + BasicBlock *Latch = L.getLoopLatch(); + + SmallVector Worklist(Roots); + while (!Worklist.empty()) { + const Instruction *I = Worklist.pop_back_val(); + Visited.insert(I); + + if (isa(I)) + continue; + + for (const Use &U : I->operands()) { + if (auto *UI = dyn_cast(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 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 +/// ByteOrderSwapped. 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). 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 ByteOrderSwapped) { + auto *SI = cast(ConditionalRecurrence.Step); + ICmpInst::Predicate 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; + + // 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)))); + bool LWellFormed = ByteOrderSwapped ? 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), + ByteOrderSwapped ? APInt::getSignedMinValue(BW) + : APInt(BW, 1)); + + BinaryOperator *BitShift = ConditionalRecurrence.BO; + if (AllowedByR == CheckAllowedByR) + return TV == BitShift && + match(FV, m_c_Xor(m_Specific(BitShift), + m_SpecificInt(*ConditionalRecurrence.ExtraConst))); + if (AllowedByR.inverse() == CheckAllowedByR) + return FV == BitShift && + match(TV, m_c_Xor(m_Specific(BitShift), + m_SpecificInt(*ConditionalRecurrence.ExtraConst))); + 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 Worklist; + Worklist.push_back(V); + while (!Worklist.empty()) { + Instruction *I = Worklist.pop_back_val(); + + // Don't add a PHI's operands to the Worklist. + if (isa(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(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(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; + + for (unsigned Idx = 0; Idx != 2; ++Idx) { + Value *FoundStep = Phi->getIncomingValue(Idx); + Value *FoundStart = Phi->getIncomingValue(!Idx); + + Instruction *TV, *FV; + if (!match(FoundStep, + m_Select(m_Cmp(), m_Instruction(TV), m_Instruction(FV)))) + continue; + + // 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; + } + return false; +} + +/// Iterates over all the phis in \p LoopLatch, and attempts to extract a +/// Conditional Recurrence and an optional Simple Recurrence. +static std::optional> +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 ByteOrderSwapped, + Value *LHSAux) + : TripCount(TripCount), LHS(LHS), RHS(RHS), ComputedValue(ComputedValue), + ByteOrderSwapped(ByteOrderSwapped), 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 ByteOrderSwapped) { + unsigned BW = GenPoly.getBitWidth(); + CRCTable Table; + Table[0] = APInt::getZero(BW); + + if (ByteOrderSwapped) { + 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; +} + +/// 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 Worklist; + + // matchConditionalRecurrence has already ensured that the SelectInst's + // condition is an Instruction. + Worklist.push_back(cast(SI->getCondition())); + + while (!Worklist.empty()) { + const Instruction *I = Worklist.pop_back_val(); + + // Don't add a PHI's operands to the Worklist. + if (isa(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(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 isBigEndianBitShift(Value *V, ScalarEvolution &SE) { + if (!V->getType()->isIntegerTy()) + return {}; + + const SCEV *E = SE.getSCEV(V); + + // Check for x / 2 (logical right shift). + if (const auto *UDiv = dyn_cast(E)) { + const auto *Divisor = dyn_cast(UDiv->getRHS()); + if (Divisor && Divisor->getAPInt() == 2) { + return false; + } + } + + // Check for x * 2 or 2 * x (left shift, considering commutativity). + if (const auto *Mul = dyn_cast(E)) { + for (const SCEV *Op : Mul->operands()) { + const auto *Const = dyn_cast(Op); + if (Const && Const->getAPInt() == 2) { + return true; + } + } + } + + return {}; // No match. +} + +/// 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 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) + return "Loop not in canonical form"; + unsigned TC = SE.getSmallConstantTripCount(&L); + if (!TC || TC % 8) + return "Unable to find a small constant byte-multiple 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 ByteOrderSwapped = + isBigEndianBitShift(ConditionalRecurrence.BO, SE); + if (!ByteOrderSwapped) + return "Loop with non-unit bitshifts"; + if (SimpleRecurrence) { + if (isBigEndianBitShift(SimpleRecurrence.BO, SE) != ByteOrderSwapped) + 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). + if (!ConditionalRecurrence.Phi->hasNUses(2) || + !SimpleRecurrence.Phi->hasNUses(2)) + 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(ConditionalRecurrence.Step), + SimpleRecurrence.Phi, + ConditionalRecurrence.Phi, L)) + return "Recurrences not intertwined with XOR"; + } + + // Make sure that the TC doesn't exceed the bitwidth of LHSAux, or LHS. + Value *LHS = ConditionalRecurrence.Start; + Value *LHSAux = SimpleRecurrence ? SimpleRecurrence.Start : nullptr; + if (TC > (LHSAux ? LHSAux->getType()->getIntegerBitWidth() + : LHS->getType()->getIntegerBitWidth())) + return "Loop iterations exceed bitwidth of data"; + + // Make sure that the computed value is used in the exit block: this should be + // true even if it is only really used in an outer loop's exit block, since + // the loop is in LCSSA form. + auto *ComputedValue = cast(ConditionalRecurrence.Step); + if (none_of(ComputedValue->users(), [Exit](User *U) { + auto *UI = dyn_cast(U); + return UI && UI->getParent() == Exit; + })) + return "Unable to find use of computed value in loop exit block"; + + assert(ConditionalRecurrence.ExtraConst && + "Expected ExtraConst in conditional recurrence"); + const APInt &GenPoly = *ConditionalRecurrence.ExtraConst; + + if (!isSignificantBitCheckWellFormed(ConditionalRecurrence, SimpleRecurrence, + *ByteOrderSwapped)) + return "Malformed significant-bit check"; + + SmallVector Roots( + {ComputedValue, + cast(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, *ByteOrderSwapped, + 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 " + << "\n"; + auto Ret = recognizeCRC(); + if (!std::holds_alternative(Ret)) { + OS << "Did not find a hash algorithm\n"; + if (std::holds_alternative(Ret)) + OS << "Reason: " << std::get(Ret) << "\n"; + return; + } + + auto Info = std::get(Ret); + OS << "Found" << (Info.ByteOrderSwapped ? " 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.ByteOrderSwapped).print(OS); +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +void HashRecognize::dump() const { print(dbgs()); } +#endif + +std::optional HashRecognize::getResult() const { + auto Res = HashRecognize(L, SE).recognizeCRC(); + if (std::holds_alternative(Res)) + return std::get(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(); +} diff --git a/llvm/lib/Analysis/TargetTransformInfo.cpp b/llvm/lib/Analysis/TargetTransformInfo.cpp index 5b91415bcb378c73ae154335f8c4d89df6753974..23c93448409036e8f77e10a4d342c9a7ed2c311c 100644 --- a/llvm/lib/Analysis/TargetTransformInfo.cpp +++ b/llvm/lib/Analysis/TargetTransformInfo.cpp @@ -1241,6 +1241,11 @@ bool TargetTransformInfo::hasActiveVectorLength(unsigned Opcode, Type *DataType, return TTIImpl->hasActiveVectorLength(Opcode, DataType, Alignment); } +std::optional TargetTransformInfo::getCryptoIntrinsic( + const TTI::SupportCryptoAlgs Name) const { + return TTIImpl->getCryptoIntrinsic(Name); +} + TargetTransformInfo::Concept::~Concept() = default; TargetIRAnalysis::TargetIRAnalysis() : TTICallback(&getDefaultTTI) {} diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 2ae9bb8f8d658be12381941655d167c74e01d34f..a990c871357be4fc13cb957bf16d7ec49053fb07 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -38,6 +38,7 @@ #include "llvm/Analysis/DominanceFrontier.h" #include "llvm/Analysis/FunctionPropertiesAnalysis.h" #include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/HashRecognize.h" #include "llvm/Analysis/IRSimilarityIdentifier.h" #include "llvm/Analysis/IVUsers.h" #include "llvm/Analysis/InlineAdvisor.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index f6df0fcb7f803fb6b9f66a8e8d499e33246ae1fe..060f91ae96df968fb08c9bf3099de8146b679c74 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -635,6 +635,7 @@ LOOP_PASS("loop-reduce", LoopStrengthReducePass()) LOOP_PASS("indvars", IndVarSimplifyPass()) LOOP_PASS("loop-unroll-full", LoopFullUnrollPass()) LOOP_PASS("print", DDGAnalysisPrinterPass(dbgs())) +LOOP_PASS("print", HashRecognizePrinterPass(dbgs())) LOOP_PASS("print", IVUsersPrinterPass(dbgs())) LOOP_PASS("print", LoopNestPrinterPass(dbgs())) LOOP_PASS("print", LoopCachePrinterPass(dbgs())) diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp index e7927352a5ca7887d242f8ed2962a759331f4a9c..e1f79d7a7bb94c9b14385fa5affd7983b2055f64 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.cpp @@ -3878,3 +3878,22 @@ AArch64TTIImpl::getScalingFactorCost(Type *Ty, GlobalValue *BaseGV, return AM.Scale != 0 && AM.Scale != 1; return -1; } + +const TTI::IntrinsicAlgMap AArch64TTIImpl::IntrinsicAlgTable += { + {TTI::SupportCryptoAlgs::Crc32, { + {Intrinsic::aarch64_crc32w, TTI::CryptoWidth::i32}, + {Intrinsic::aarch64_crc32h, TTI::CryptoWidth::i16}, + {Intrinsic::aarch64_crc32b, TTI::CryptoWidth::i8}} +}}; + +std::optional AArch64TTIImpl::getCryptoIntrinsic( + const TTI::SupportCryptoAlgs ID) const { + + if (ID == TTI::SupportCryptoAlgs::Crc32 && getST()->hasCRC() + && AArch64TTIImpl::IntrinsicAlgTable.count(ID)) { + return AArch64TTIImpl::IntrinsicAlgTable.at(ID); + } + + return {}; +} \ No newline at end of file diff --git a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h index be62dabaa8c82966bb83f51e8c2c3f44f631a76a..c21c0094ed7d20d2fa77a6c09316e011623d227e 100644 --- a/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h +++ b/llvm/lib/Target/AArch64/AArch64TargetTransformInfo.h @@ -410,6 +410,11 @@ public: return BaseT::getStoreMinimumVF(VF, ScalarMemTy, ScalarValTy); } + + static const TTI::IntrinsicAlgMap IntrinsicAlgTable; + + std::optional getCryptoIntrinsic( + const TTI::SupportCryptoAlgs) const override; }; } // end namespace llvm diff --git a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp index 8572a442e784ae2fb5bc93548a72daa70522cae5..8c31d4ec3a53bc02d2a50bfa0c9b03bc2147af89 100644 --- a/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp +++ b/llvm/lib/Transforms/Scalar/LoopIdiomRecognize.cpp @@ -48,6 +48,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/CmpInstAnalysis.h" +#include "llvm/Analysis/HashRecognize.h" #include "llvm/Analysis/LoopAccessAnalysis.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopPass.h" @@ -141,6 +142,11 @@ static cl::opt UseLIRCodeSizeHeurs( "with -Os/-Oz"), cl::init(true), cl::Hidden); +static cl::opt EnableCRCRecognize( + "enable-crc-recognize", + cl::desc("Enable crc loop recognize and optimization (default-false)"), + cl::init(false), cl::Hidden); + namespace { class LoopIdiomRecognize { @@ -229,7 +235,7 @@ private: const SCEV *BECount); bool avoidLIRForMultiBlockLoop(bool IsMemset = false, bool IsLoopMemset = false); - + bool optimizeCRCLoop(const PolynomialInfo &Info); /// @} /// \name Noncountable Loop Idiom Handling /// @{ @@ -308,7 +314,7 @@ bool LoopIdiomRecognize::runOnLoop(Loop *L) { HasMemsetPattern = TLI->has(LibFunc_memset_pattern16); HasMemcpy = TLI->has(LibFunc_memcpy); - if (HasMemset || HasMemsetPattern || HasMemcpy) + if (HasMemset || HasMemsetPattern || HasMemcpy || EnableCRCRecognize) if (SE->hasLoopInvariantBackedgeTakenCount(L)) return runOnCountableLoop(); @@ -352,6 +358,16 @@ bool LoopIdiomRecognize::runOnCountableLoop() { MadeChange |= runOnLoopBlock(BB, BECount, ExitBlocks); } + + // Optimize a CRC loop if HashRecognize found one, provided we're not + // optimizing for size. + if (EnableCRCRecognize) { + auto Res = HashRecognize(*CurLoop, *SE).getResult(); + if (Res) { + MadeChange |= optimizeCRCLoop(*Res); + } + } + return MadeChange; } @@ -2868,3 +2884,65 @@ bool LoopIdiomRecognize::recognizeShiftUntilZero() { ++NumShiftUntilZero; return MadeChange; } + +bool LoopIdiomRecognize::optimizeCRCLoop(const PolynomialInfo &Info) { + auto SupportCrcIntrinsic = TTI->getCryptoIntrinsic(TTI::SupportCryptoAlgs::Crc32); + if (SupportCrcIntrinsic) { + auto &SupportCrcIntrinsicSet = SupportCrcIntrinsic.value(); + Module &M = *CurLoop->getHeader()->getModule(); + + std::map ArchCrcCallMap; + + for (size_t l = 0; l < SupportCrcIntrinsicSet.size(); l++) { + auto ID = std::get<0>(SupportCrcIntrinsicSet[l]); + auto Width = std::get<1>(SupportCrcIntrinsicSet[l]); + auto IntrinsicFunc = Intrinsic::getDeclaration(&M, ID); + ArchCrcCallMap[Width] = IntrinsicFunc; + } + + Instruction *Terminator = CurLoop->getLoopPreheader()->getTerminator(); + LLVMContext &Context = CurLoop->getLoopPreheader()->getContext(); + IRBuilder<> Builder(Terminator); + + Value *Crc = Info.LHS; + Value *Zero = ConstantInt::get(Crc->getType(), 0); + Value *Data = Info.LHSAux == nullptr ? Zero : Info.LHSAux; + Value *ComputedValue = Info.ComputedValue; + + static const APInt SpecialValue(32, 0xEDB88320); + + // Check POLY const variable is 0xEDB88320 + // Check is right shift (ByteOrderSwapped == false) + // Check is CRC 32 bit + if (Crc->getType() == Type::getInt32Ty(Context) && Info.ByteOrderSwapped == false + && Info.RHS.getBitWidth() == 32 && Info.RHS.eq(SpecialValue)) { + Function *Crc32Intrinsic = nullptr; + switch (Info.TripCount) { + case 8: + Crc32Intrinsic = ArchCrcCallMap[TTI::CryptoWidth::i8]; + break; + case 16: + Crc32Intrinsic = ArchCrcCallMap[TTI::CryptoWidth::i16]; + break; + case 32: + Crc32Intrinsic = ArchCrcCallMap[TTI::CryptoWidth::i32]; + break; + default: + break; + } + // If data's type is not i32, use zero extension to i32 + if (Data->getType() == Type::getInt16Ty(Context) + || Data->getType() == Type::getInt8Ty(Context)) { + Data = Builder.CreateZExt(Data, Type::getInt32Ty(Context)); + } + if (Data->getType() == Type::getInt32Ty(Context) && Crc32Intrinsic) { + // Insert intrinsic call at the end of loop header, and get result value + auto NewCrc = Builder.CreateCall(Crc32Intrinsic, {Crc, Data}); + ComputedValue->replaceAllUsesWith(NewCrc); + // after replace uses, the loop body is dead and will be delete by LoopDeletionPass + return true; + } + } + } + return false; +} \ No newline at end of file diff --git a/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll new file mode 100644 index 0000000000000000000000000000000000000000..f97e22dcfcdd0c8be66327014a6f846646a0d283 --- /dev/null +++ b/llvm/test/Analysis/HashRecognize/cyclic-redundancy-check.ll @@ -0,0 +1,1451 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5 +; RUN: opt -passes='print' -disable-output %s 2>&1 | FileCheck %s + +define i16 @crc16.le.tc8(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'crc16.le.tc8' +; CHECK-NEXT: Found little-endian CRC-16 loop with trip count 8 +; CHECK-NEXT: Initial CRC: i16 %checksum +; CHECK-NEXT: Generating polynomial: 40961 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %xor +; CHECK-NEXT: Auxiliary data: i8 %msg +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 49345 49537 320 49921 960 640 49729 50689 1728 1920 51009 1280 50625 50305 1088 +; CHECK-NEXT: 52225 3264 3456 52545 3840 53185 52865 3648 2560 51905 52097 2880 51457 2496 2176 51265 +; CHECK-NEXT: 55297 6336 6528 55617 6912 56257 55937 6720 7680 57025 57217 8000 56577 7616 7296 56385 +; CHECK-NEXT: 5120 54465 54657 5440 55041 6080 5760 54849 53761 4800 4992 54081 4352 53697 53377 4160 +; CHECK-NEXT: 61441 12480 12672 61761 13056 62401 62081 12864 13824 63169 63361 14144 62721 13760 13440 62529 +; CHECK-NEXT: 15360 64705 64897 15680 65281 16320 16000 65089 64001 15040 15232 64321 14592 63937 63617 14400 +; CHECK-NEXT: 10240 59585 59777 10560 60161 11200 10880 59969 60929 11968 12160 61249 11520 60865 60545 11328 +; CHECK-NEXT: 58369 9408 9600 58689 9984 59329 59009 9792 8704 58049 58241 9024 57601 8640 8320 57409 +; CHECK-NEXT: 40961 24768 24960 41281 25344 41921 41601 25152 26112 42689 42881 26432 42241 26048 25728 42049 +; CHECK-NEXT: 27648 44225 44417 27968 44801 28608 28288 44609 43521 27328 27520 43841 26880 43457 43137 26688 +; CHECK-NEXT: 30720 47297 47489 31040 47873 31680 31360 47681 48641 32448 32640 48961 32000 48577 48257 31808 +; CHECK-NEXT: 46081 29888 30080 46401 30464 47041 46721 30272 29184 45761 45953 29504 45313 29120 28800 45121 +; CHECK-NEXT: 20480 37057 37249 20800 37633 21440 21120 37441 38401 22208 22400 38721 21760 38337 38017 21568 +; CHECK-NEXT: 39937 23744 23936 40257 24320 40897 40577 24128 23040 39617 39809 23360 39169 22976 22656 38977 +; CHECK-NEXT: 34817 18624 18816 35137 19200 35777 35457 19008 19968 36545 36737 20288 36097 19904 19584 35905 +; CHECK-NEXT: 17408 33985 34177 17728 34561 18368 18048 34369 33281 17088 17280 33601 16640 33217 32897 16448 +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc.trunc = trunc i16 %crc to i8 + %xor.data.crc = xor i8 %data, %crc.trunc + %and.data.crc = and i8 %xor.data.crc, 1 + %data.next = lshr i8 %data, 1 + %check.sb = icmp eq i8 %and.data.crc, 0 + %crc.lshr = lshr i16 %crc, 1 + %xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @crc16.le.tc8.udiv(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'crc16.le.tc8.udiv' +; CHECK-NEXT: Found little-endian CRC-16 loop with trip count 8 +; CHECK-NEXT: Initial CRC: i16 %checksum +; CHECK-NEXT: Generating polynomial: 40961 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %xor +; CHECK-NEXT: Auxiliary data: i8 %msg +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 49345 49537 320 49921 960 640 49729 50689 1728 1920 51009 1280 50625 50305 1088 +; CHECK-NEXT: 52225 3264 3456 52545 3840 53185 52865 3648 2560 51905 52097 2880 51457 2496 2176 51265 +; CHECK-NEXT: 55297 6336 6528 55617 6912 56257 55937 6720 7680 57025 57217 8000 56577 7616 7296 56385 +; CHECK-NEXT: 5120 54465 54657 5440 55041 6080 5760 54849 53761 4800 4992 54081 4352 53697 53377 4160 +; CHECK-NEXT: 61441 12480 12672 61761 13056 62401 62081 12864 13824 63169 63361 14144 62721 13760 13440 62529 +; CHECK-NEXT: 15360 64705 64897 15680 65281 16320 16000 65089 64001 15040 15232 64321 14592 63937 63617 14400 +; CHECK-NEXT: 10240 59585 59777 10560 60161 11200 10880 59969 60929 11968 12160 61249 11520 60865 60545 11328 +; CHECK-NEXT: 58369 9408 9600 58689 9984 59329 59009 9792 8704 58049 58241 9024 57601 8640 8320 57409 +; CHECK-NEXT: 40961 24768 24960 41281 25344 41921 41601 25152 26112 42689 42881 26432 42241 26048 25728 42049 +; CHECK-NEXT: 27648 44225 44417 27968 44801 28608 28288 44609 43521 27328 27520 43841 26880 43457 43137 26688 +; CHECK-NEXT: 30720 47297 47489 31040 47873 31680 31360 47681 48641 32448 32640 48961 32000 48577 48257 31808 +; CHECK-NEXT: 46081 29888 30080 46401 30464 47041 46721 30272 29184 45761 45953 29504 45313 29120 28800 45121 +; CHECK-NEXT: 20480 37057 37249 20800 37633 21440 21120 37441 38401 22208 22400 38721 21760 38337 38017 21568 +; CHECK-NEXT: 39937 23744 23936 40257 24320 40897 40577 24128 23040 39617 39809 23360 39169 22976 22656 38977 +; CHECK-NEXT: 34817 18624 18816 35137 19200 35777 35457 19008 19968 36545 36737 20288 36097 19904 19584 35905 +; CHECK-NEXT: 17408 33985 34177 17728 34561 18368 18048 34369 33281 17088 17280 33601 16640 33217 32897 16448 +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc.trunc = trunc i16 %crc to i8 + %xor.data.crc = xor i8 %data, %crc.trunc + %and.data.crc = and i8 %xor.data.crc, 1 + %data.next = lshr i8 %data, 1 + %check.sb = icmp eq i8 %and.data.crc, 0 + %crc.lshr = lshr i16 %crc, 1 + %xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @crc16.le.tc16(i16 %msg, i16 %checksum) { +; CHECK-LABEL: 'crc16.le.tc16' +; CHECK-NEXT: Found little-endian CRC-16 loop with trip count 16 +; CHECK-NEXT: Initial CRC: i16 %checksum +; CHECK-NEXT: Generating polynomial: 40961 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor +; CHECK-NEXT: Auxiliary data: i16 %msg +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 49345 49537 320 49921 960 640 49729 50689 1728 1920 51009 1280 50625 50305 1088 +; CHECK-NEXT: 52225 3264 3456 52545 3840 53185 52865 3648 2560 51905 52097 2880 51457 2496 2176 51265 +; CHECK-NEXT: 55297 6336 6528 55617 6912 56257 55937 6720 7680 57025 57217 8000 56577 7616 7296 56385 +; CHECK-NEXT: 5120 54465 54657 5440 55041 6080 5760 54849 53761 4800 4992 54081 4352 53697 53377 4160 +; CHECK-NEXT: 61441 12480 12672 61761 13056 62401 62081 12864 13824 63169 63361 14144 62721 13760 13440 62529 +; CHECK-NEXT: 15360 64705 64897 15680 65281 16320 16000 65089 64001 15040 15232 64321 14592 63937 63617 14400 +; CHECK-NEXT: 10240 59585 59777 10560 60161 11200 10880 59969 60929 11968 12160 61249 11520 60865 60545 11328 +; CHECK-NEXT: 58369 9408 9600 58689 9984 59329 59009 9792 8704 58049 58241 9024 57601 8640 8320 57409 +; CHECK-NEXT: 40961 24768 24960 41281 25344 41921 41601 25152 26112 42689 42881 26432 42241 26048 25728 42049 +; CHECK-NEXT: 27648 44225 44417 27968 44801 28608 28288 44609 43521 27328 27520 43841 26880 43457 43137 26688 +; CHECK-NEXT: 30720 47297 47489 31040 47873 31680 31360 47681 48641 32448 32640 48961 32000 48577 48257 31808 +; CHECK-NEXT: 46081 29888 30080 46401 30464 47041 46721 30272 29184 45761 45953 29504 45313 29120 28800 45121 +; CHECK-NEXT: 20480 37057 37249 20800 37633 21440 21120 37441 38401 22208 22400 38721 21760 38337 38017 21568 +; CHECK-NEXT: 39937 23744 23936 40257 24320 40897 40577 24128 23040 39617 39809 23360 39169 22976 22656 38977 +; CHECK-NEXT: 34817 18624 18816 35137 19200 35777 35457 19008 19968 36545 36737 20288 36097 19904 19584 35905 +; CHECK-NEXT: 17408 33985 34177 17728 34561 18368 18048 34369 33281 17088 17280 33601 16640 33217 32897 16448 +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %and.crc.data = and i16 %xor.crc.data, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i16 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i8 @crc8.le.tc16(i16 %msg, i8 %checksum) { +; CHECK-LABEL: 'crc8.le.tc16' +; CHECK-NEXT: Found little-endian CRC-8 loop with trip count 16 +; CHECK-NEXT: Initial CRC: i8 %checksum +; CHECK-NEXT: Generating polynomial: 29 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i8 %crc.lshr, i8 %crc.xor +; CHECK-NEXT: Auxiliary data: i16 %msg +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 9 18 27 31 22 13 4 5 12 23 30 26 19 8 1 +; CHECK-NEXT: 10 3 24 17 21 28 7 14 15 6 29 20 16 25 2 11 +; CHECK-NEXT: 20 29 6 15 11 2 25 16 17 24 3 10 14 7 28 21 +; CHECK-NEXT: 30 23 12 5 1 8 19 26 27 18 9 0 4 13 22 31 +; CHECK-NEXT: 19 26 1 8 12 5 30 23 22 31 4 13 9 0 27 18 +; CHECK-NEXT: 25 16 11 2 6 15 20 29 28 21 14 7 3 10 17 24 +; CHECK-NEXT: 7 14 21 28 24 17 10 3 2 11 16 25 29 20 15 6 +; CHECK-NEXT: 13 4 31 22 18 27 0 9 8 1 26 19 23 30 5 12 +; CHECK-NEXT: 29 20 15 6 2 11 16 25 24 17 10 3 7 14 21 28 +; CHECK-NEXT: 23 30 5 12 8 1 26 19 18 27 0 9 13 4 31 22 +; CHECK-NEXT: 9 0 27 18 22 31 4 13 12 5 30 23 19 26 1 8 +; CHECK-NEXT: 3 10 17 24 28 21 14 7 6 15 20 29 25 16 11 2 +; CHECK-NEXT: 14 7 28 21 17 24 3 10 11 2 25 16 20 29 6 15 +; CHECK-NEXT: 4 13 22 31 27 18 9 0 1 8 19 26 30 23 12 5 +; CHECK-NEXT: 26 19 8 1 5 12 23 30 31 22 13 4 0 9 18 27 +; CHECK-NEXT: 16 25 2 11 15 6 29 20 21 28 7 14 10 3 24 17 +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i8 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %data.trunc = trunc i16 %data to i8 + %xor.crc.data = xor i8 %crc, %data.trunc + %and.crc.data = and i8 %xor.crc.data, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i8 %and.crc.data, 0 + %crc.lshr = lshr i8 %crc, 1 + %crc.xor = xor i8 %crc.lshr, 29 + %crc.next = select i1 %check.sb, i8 %crc.lshr, i8 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i8 %crc.next +} + +define i16 @crc16.be.tc8.crc.init.li(i16 %checksum, i8 %msg) { +; CHECK-LABEL: 'crc16.be.tc8.crc.init.li' +; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8 +; CHECK-NEXT: Initial CRC: %crc.init = xor i16 %msg.shl, %checksum +; CHECK-NEXT: Generating polynomial: 4129 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 4129 8258 12387 16516 20645 24774 28903 33032 37161 41290 45419 49548 53677 57806 61935 +; CHECK-NEXT: 4657 528 12915 8786 21173 17044 29431 25302 37689 33560 45947 41818 54205 50076 62463 58334 +; CHECK-NEXT: 9314 13379 1056 5121 25830 29895 17572 21637 42346 46411 34088 38153 58862 62927 50604 54669 +; CHECK-NEXT: 13907 9842 5649 1584 30423 26358 22165 18100 46939 42874 38681 34616 63455 59390 55197 51132 +; CHECK-NEXT: 18628 22757 26758 30887 2112 6241 10242 14371 51660 55789 59790 63919 35144 39273 43274 47403 +; CHECK-NEXT: 23285 19156 31415 27286 6769 2640 14899 10770 56317 52188 64447 60318 39801 35672 47931 43802 +; CHECK-NEXT: 27814 31879 19684 23749 11298 15363 3168 7233 60846 64911 52716 56781 44330 48395 36200 40265 +; CHECK-NEXT: 32407 28342 24277 20212 15891 11826 7761 3696 65439 61374 57309 53244 48923 44858 40793 36728 +; CHECK-NEXT: 37256 33193 45514 41451 53516 49453 61774 57711 4224 161 12482 8419 20484 16421 28742 24679 +; CHECK-NEXT: 33721 37784 41979 46042 49981 54044 58239 62302 689 4752 8947 13010 16949 21012 25207 29270 +; CHECK-NEXT: 46570 42443 38312 34185 62830 58703 54572 50445 13538 9411 5280 1153 29798 25671 21540 17413 +; CHECK-NEXT: 42971 47098 34713 38840 59231 63358 50973 55100 9939 14066 1681 5808 26199 30326 17941 22068 +; CHECK-NEXT: 55628 51565 63758 59695 39368 35305 47498 43435 22596 18533 30726 26663 6336 2273 14466 10403 +; CHECK-NEXT: 52093 56156 60223 64286 35833 39896 43963 48026 19061 23124 27191 31254 2801 6864 10931 14994 +; CHECK-NEXT: 64814 60687 56684 52557 48554 44427 40424 36297 31782 27655 23652 19525 15522 11395 7392 3265 +; CHECK-NEXT: 61215 65342 53085 57212 44955 49082 36825 40952 28183 32310 20053 24180 11923 16050 3793 7920 +; +entry: + %msg.ext = zext i8 %msg to i16 + %msg.shl = shl nuw i16 %msg.ext, 8 + %crc.init = xor i16 %msg.shl, %checksum + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @crc16.be.tc8.crc.init.arg(i16 %crc.init) { +; CHECK-LABEL: 'crc16.be.tc8.crc.init.arg' +; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8 +; CHECK-NEXT: Initial CRC: i16 %crc.init +; CHECK-NEXT: Generating polynomial: 4129 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 4129 8258 12387 16516 20645 24774 28903 33032 37161 41290 45419 49548 53677 57806 61935 +; CHECK-NEXT: 4657 528 12915 8786 21173 17044 29431 25302 37689 33560 45947 41818 54205 50076 62463 58334 +; CHECK-NEXT: 9314 13379 1056 5121 25830 29895 17572 21637 42346 46411 34088 38153 58862 62927 50604 54669 +; CHECK-NEXT: 13907 9842 5649 1584 30423 26358 22165 18100 46939 42874 38681 34616 63455 59390 55197 51132 +; CHECK-NEXT: 18628 22757 26758 30887 2112 6241 10242 14371 51660 55789 59790 63919 35144 39273 43274 47403 +; CHECK-NEXT: 23285 19156 31415 27286 6769 2640 14899 10770 56317 52188 64447 60318 39801 35672 47931 43802 +; CHECK-NEXT: 27814 31879 19684 23749 11298 15363 3168 7233 60846 64911 52716 56781 44330 48395 36200 40265 +; CHECK-NEXT: 32407 28342 24277 20212 15891 11826 7761 3696 65439 61374 57309 53244 48923 44858 40793 36728 +; CHECK-NEXT: 37256 33193 45514 41451 53516 49453 61774 57711 4224 161 12482 8419 20484 16421 28742 24679 +; CHECK-NEXT: 33721 37784 41979 46042 49981 54044 58239 62302 689 4752 8947 13010 16949 21012 25207 29270 +; CHECK-NEXT: 46570 42443 38312 34185 62830 58703 54572 50445 13538 9411 5280 1153 29798 25671 21540 17413 +; CHECK-NEXT: 42971 47098 34713 38840 59231 63358 50973 55100 9939 14066 1681 5808 26199 30326 17941 22068 +; CHECK-NEXT: 55628 51565 63758 59695 39368 35305 47498 43435 22596 18533 30726 26663 6336 2273 14466 10403 +; CHECK-NEXT: 52093 56156 60223 64286 35833 39896 43963 48026 19061 23124 27191 31254 2801 6864 10931 14994 +; CHECK-NEXT: 64814 60687 56684 52557 48554 44427 40424 36297 31782 27655 23652 19525 15522 11395 7392 3265 +; CHECK-NEXT: 61215 65342 53085 57212 44955 49082 36825 40952 28183 32310 20053 24180 11923 16050 3793 7920 +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @crc16.be.tc8.crc.init.arg.flipped.sb.check(i16 %crc.init) { +; CHECK-LABEL: 'crc16.be.tc8.crc.init.arg.flipped.sb.check' +; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8 +; CHECK-NEXT: Initial CRC: i16 %crc.init +; CHECK-NEXT: Generating polynomial: 4129 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 4129 8258 12387 16516 20645 24774 28903 33032 37161 41290 45419 49548 53677 57806 61935 +; CHECK-NEXT: 4657 528 12915 8786 21173 17044 29431 25302 37689 33560 45947 41818 54205 50076 62463 58334 +; CHECK-NEXT: 9314 13379 1056 5121 25830 29895 17572 21637 42346 46411 34088 38153 58862 62927 50604 54669 +; CHECK-NEXT: 13907 9842 5649 1584 30423 26358 22165 18100 46939 42874 38681 34616 63455 59390 55197 51132 +; CHECK-NEXT: 18628 22757 26758 30887 2112 6241 10242 14371 51660 55789 59790 63919 35144 39273 43274 47403 +; CHECK-NEXT: 23285 19156 31415 27286 6769 2640 14899 10770 56317 52188 64447 60318 39801 35672 47931 43802 +; CHECK-NEXT: 27814 31879 19684 23749 11298 15363 3168 7233 60846 64911 52716 56781 44330 48395 36200 40265 +; CHECK-NEXT: 32407 28342 24277 20212 15891 11826 7761 3696 65439 61374 57309 53244 48923 44858 40793 36728 +; CHECK-NEXT: 37256 33193 45514 41451 53516 49453 61774 57711 4224 161 12482 8419 20484 16421 28742 24679 +; CHECK-NEXT: 33721 37784 41979 46042 49981 54044 58239 62302 689 4752 8947 13010 16949 21012 25207 29270 +; CHECK-NEXT: 46570 42443 38312 34185 62830 58703 54572 50445 13538 9411 5280 1153 29798 25671 21540 17413 +; CHECK-NEXT: 42971 47098 34713 38840 59231 63358 50973 55100 9939 14066 1681 5808 26199 30326 17941 22068 +; CHECK-NEXT: 55628 51565 63758 59695 39368 35305 47498 43435 22596 18533 30726 26663 6336 2273 14466 10403 +; CHECK-NEXT: 52093 56156 60223 64286 35833 39896 43963 48026 19061 23124 27191 31254 2801 6864 10931 14994 +; CHECK-NEXT: 64814 60687 56684 52557 48554 44427 40424 36297 31782 27655 23652 19525 15522 11395 7392 3265 +; CHECK-NEXT: 61215 65342 53085 57212 44955 49082 36825 40952 28183 32310 20053 24180 11923 16050 3793 7920 +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp sge i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i8 @crc8.be.tc8.ptr.nested.loop(ptr %msg, i32 %loop.limit) { +; CHECK-LABEL: 'crc8.be.tc8.ptr.nested.loop' +; CHECK-NEXT: Found big-endian CRC-8 loop with trip count 8 +; CHECK-NEXT: Initial CRC: %crc.init = xor i8 %msg.load, %crc.outer +; CHECK-NEXT: Generating polynomial: 29 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i8 %crc.xor, i8 %crc.shl +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 29 58 39 116 105 78 83 232 245 210 207 156 129 166 187 +; CHECK-NEXT: 205 208 247 234 185 164 131 158 37 56 31 2 81 76 107 118 +; CHECK-NEXT: 135 154 189 160 243 238 201 212 111 114 85 72 27 6 33 60 +; CHECK-NEXT: 74 87 112 109 62 35 4 25 162 191 152 133 214 203 236 241 +; CHECK-NEXT: 19 14 41 52 103 122 93 64 251 230 193 220 143 146 181 168 +; CHECK-NEXT: 222 195 228 249 170 183 144 141 54 43 12 17 66 95 120 101 +; CHECK-NEXT: 148 137 174 179 224 253 218 199 124 97 70 91 8 21 50 47 +; CHECK-NEXT: 89 68 99 126 45 48 23 10 177 172 139 150 197 216 255 226 +; CHECK-NEXT: 38 59 28 1 82 79 104 117 206 211 244 233 186 167 128 157 +; CHECK-NEXT: 235 246 209 204 159 130 165 184 3 30 57 36 119 106 77 80 +; CHECK-NEXT: 161 188 155 134 213 200 239 242 73 84 115 110 61 32 7 26 +; CHECK-NEXT: 108 113 86 75 24 5 34 63 132 153 190 163 240 237 202 215 +; CHECK-NEXT: 53 40 15 18 65 92 123 102 221 192 231 250 169 180 147 142 +; CHECK-NEXT: 248 229 194 223 140 145 182 171 16 13 42 55 100 121 94 67 +; CHECK-NEXT: 178 175 136 149 198 219 252 225 90 71 96 125 46 51 20 9 +; CHECK-NEXT: 127 98 69 88 11 22 49 44 151 138 173 176 227 254 217 196 +; +entry: + br label %outer.loop + +outer.loop: ; preds = %inner.exit, %entry + %crc.outer = phi i8 [ 0, %entry ], [ %crc.next, %inner.exit ] + %outer.iv = phi i32 [ 0, %entry ], [ %outer.iv.next, %inner.exit ] + %outer.exit.cond = icmp ult i32 %outer.iv, %loop.limit + br i1 %outer.exit.cond, label %ph, label %exit + +ph: ; preds = %outer.loop + %outer.iv.ext = sext i32 %outer.iv to i64 + %msg.outer.iv = getelementptr inbounds i8, ptr %msg, i64 %outer.iv.ext + %msg.load = load i8, ptr %msg.outer.iv, align 1 + %crc.init = xor i8 %msg.load, %crc.outer + br label %inner.loop + +inner.loop: ; preds = %inner.loop, %ph + %inner.iv = phi i32 [ 0, %ph ], [ %inner.iv.next, %inner.loop ] + %crc = phi i8 [ %crc.init, %ph ], [ %crc.next, %inner.loop ] + %crc.shl = shl i8 %crc, 1 + %crc.xor = xor i8 %crc.shl, 29 + %check.sb = icmp slt i8 %crc, 0 + %crc.next = select i1 %check.sb, i8 %crc.xor, i8 %crc.shl + %inner.iv.next = add nuw nsw i32 %inner.iv, 1 + %exit.cond = icmp ult i32 %inner.iv, 7 + br i1 %exit.cond, label %inner.loop, label %inner.exit + +inner.exit: ; preds = %inner.loop + %outer.iv.next = add i32 %outer.iv, 1 + br label %outer.loop + +exit: ; preds = %outer.loop + ret i8 %crc.outer +} + +define i32 @crc32.le.tc8.data32(i32 %checksum, i32 %msg) { +; CHECK-LABEL: 'crc32.le.tc8.data32' +; CHECK-NEXT: Found little-endian CRC-32 loop with trip count 8 +; CHECK-NEXT: Initial CRC: i32 %checksum +; CHECK-NEXT: Generating polynomial: 33800 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %crc.xor +; CHECK-NEXT: Auxiliary data: i32 %msg +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 4489 8978 12955 17956 22445 25910 29887 35912 40385 44890 48851 51820 56293 59774 63735 +; CHECK-NEXT: 4225 264 13203 8730 22181 18220 30135 25662 40137 36160 49115 44626 56045 52068 63999 59510 +; CHECK-NEXT: 8450 12427 528 5017 26406 30383 17460 21949 44362 48323 36440 40913 60270 64231 51324 55797 +; CHECK-NEXT: 12675 8202 4753 792 30631 26158 21685 17724 48587 44098 40665 36688 64495 60006 55549 51572 +; CHECK-NEXT: 16900 21389 24854 28831 1056 5545 10034 14011 52812 57285 60766 64727 34920 39393 43898 47859 +; CHECK-NEXT: 21125 17164 29079 24606 5281 1320 14259 9786 57037 53060 64991 60502 39145 35168 48123 43634 +; CHECK-NEXT: 25350 29327 16404 20893 9506 13483 1584 6073 61262 65223 52316 56789 43370 47331 35448 39921 +; CHECK-NEXT: 29575 25102 20629 16668 13731 9258 5809 1848 65487 60998 56541 52564 47595 43106 39673 35696 +; CHECK-NEXT: 33800 38273 42778 46739 49708 54181 57662 61623 2112 6601 11090 15067 20068 24557 28022 31999 +; CHECK-NEXT: 38025 34048 47003 42514 53933 49956 61887 57398 6337 2376 15315 10842 24293 20332 32247 27774 +; CHECK-NEXT: 42250 46211 34328 38801 58158 62119 49212 53685 10562 14539 2640 7129 28518 32495 19572 24061 +; CHECK-NEXT: 46475 41986 38553 34576 62383 57894 53437 49460 14787 10314 6865 2904 32743 28270 23797 19836 +; CHECK-NEXT: 50700 55173 58654 62615 32808 37281 41786 45747 19012 23501 26966 30943 3168 7657 12146 16123 +; CHECK-NEXT: 54925 50948 62879 58390 37033 33056 46011 41522 23237 19276 31191 26718 7393 3432 16371 11898 +; CHECK-NEXT: 59150 63111 50204 54677 41258 45219 33336 37809 27462 31439 18516 23005 11618 15595 3696 8185 +; CHECK-NEXT: 63375 58886 54429 50452 45483 40994 37561 33584 31687 27214 22741 18780 15843 11370 7921 3960 +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %crc = phi i32 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i32 [ %msg, %entry ], [ %data.next, %loop ] + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %xor.crc.data = xor i32 %crc, %data + %sb.crc.data = and i32 %xor.crc.data, 1 + %check.sb = icmp eq i32 %sb.crc.data, 0 + %crc.lshr = lshr i32 %crc, 1 + %crc.xor = xor i32 %crc.lshr, 33800 + %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %data.next = lshr i32 %data, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i32 %crc.next +} + +define i16 @crc16.be.tc8.zext.data(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'crc16.be.tc8.zext.data' +; CHECK-NEXT: Found big-endian CRC-16 loop with trip count 8 +; CHECK-NEXT: Initial CRC: i16 %checksum +; CHECK-NEXT: Generating polynomial: 258 +; CHECK-NEXT: Computed CRC: %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor +; CHECK-NEXT: Auxiliary data: i8 %msg +; CHECK-NEXT: Computed CRC lookup table: +; CHECK-NEXT: 0 258 516 774 1032 1290 1548 1806 2064 2322 2580 2838 3096 3354 3612 3870 +; CHECK-NEXT: 4128 4386 4644 4902 5160 5418 5676 5934 6192 6450 6708 6966 7224 7482 7740 7998 +; CHECK-NEXT: 8256 8514 8772 9030 9288 9546 9804 10062 10320 10578 10836 11094 11352 11610 11868 12126 +; CHECK-NEXT: 12384 12642 12900 13158 13416 13674 13932 14190 14448 14706 14964 15222 15480 15738 15996 16254 +; CHECK-NEXT: 16512 16770 17028 17286 17544 17802 18060 18318 18576 18834 19092 19350 19608 19866 20124 20382 +; CHECK-NEXT: 20640 20898 21156 21414 21672 21930 22188 22446 22704 22962 23220 23478 23736 23994 24252 24510 +; CHECK-NEXT: 24768 25026 25284 25542 25800 26058 26316 26574 26832 27090 27348 27606 27864 28122 28380 28638 +; CHECK-NEXT: 28896 29154 29412 29670 29928 30186 30444 30702 30960 31218 31476 31734 31992 32250 32508 32766 +; CHECK-NEXT: 33024 32770 33540 33286 34056 33802 34572 34318 35088 34834 35604 35350 36120 35866 36636 36382 +; CHECK-NEXT: 37152 36898 37668 37414 38184 37930 38700 38446 39216 38962 39732 39478 40248 39994 40764 40510 +; CHECK-NEXT: 41280 41026 41796 41542 42312 42058 42828 42574 43344 43090 43860 43606 44376 44122 44892 44638 +; CHECK-NEXT: 45408 45154 45924 45670 46440 46186 46956 46702 47472 47218 47988 47734 48504 48250 49020 48766 +; CHECK-NEXT: 49536 49282 50052 49798 50568 50314 51084 50830 51600 51346 52116 51862 52632 52378 53148 52894 +; CHECK-NEXT: 53664 53410 54180 53926 54696 54442 55212 54958 55728 55474 56244 55990 56760 56506 57276 57022 +; CHECK-NEXT: 57792 57538 58308 58054 58824 58570 59340 59086 59856 59602 60372 60118 60888 60634 61404 61150 +; CHECK-NEXT: 61920 61666 62436 62182 62952 62698 63468 63214 63984 63730 64500 64246 65016 64762 65532 65278 +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data.ext = zext i8 %data to i16 + %xor.crc.data = xor i16 %crc, %data.ext + %check.sb = icmp sge i16 %xor.crc.data, 0 + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 258 + %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor + %data.next = shl i8 %data, 1 + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +; Negative tests + +define i16 @not.crc.non.const.tc(i16 %crc.init, i32 %loop.limit) { +; CHECK-LABEL: 'not.crc.non.const.tc' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Unable to find a small constant byte-multiple trip count +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp sge i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, %loop.limit + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.non.canonical.not.multiple.8(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.non.canonical.not.multiple.8' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Unable to find a small constant byte-multiple trip count +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp eq i32 %iv, 3 + br i1 %exit.cond, label %exit, label %loop + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.non.canonical.loop.countdown(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.non.canonical.loop.countdown' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Loop not in canonical form +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 7, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = sub nuw nsw i32 %iv, 1 + %exit.cond = icmp eq i32 %iv, 0 + br i1 %exit.cond, label %exit, label %loop + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.non.canonical.loop.multiple.blocks(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.non.canonical.loop.multiple.blocks' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Loop not in canonical form +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %continue ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %continue ] + %check.sb = icmp slt i16 %crc, 0 + %crc.shl = shl i16 %crc, 1 + br i1 %check.sb, label %xor, label %continue + +xor: + %crc.xor = xor i16 %crc.shl, 4129 + br label %continue + +continue: + %crc.next = phi i16 [ %crc.xor, %xor ], [ %crc.shl, %loop ] + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp eq i32 %iv, 7 + br i1 %exit.cond, label %exit, label %loop + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.tc.exceeds.data.bw(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.tc.exceeds.data.bw' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Loop iterations exceed bitwidth of data +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 511 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.no.conditional.recurrence(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.no.conditional.recurrence' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Unable to find conditional recurrence +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %shl = shl i16 %crc, 1 + %crc.next = xor i16 %shl, 258 + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + + +define i16 @not.crc.bad.shift.recurrence(i16 %checksum, i8 %msg) { +; CHECK-LABEL: 'not.crc.bad.shift.recurrence' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Loop with non-unit bitshifts +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %crc.lshr = lshr i16 %crc, 8 + %data.ext = zext i8 %data to i16 + %xor.crc.data = xor i16 %crc.lshr, %data.ext + %check.sb = icmp ult i16 %xor.crc.data, 128 + %crc.and = and i16 %crc, 32767 + %crc.xor = xor i16 %crc.and, 258 + %crc.next = select i1 %check.sb, i16 %crc.and, i16 %crc.xor + %data.next = shl i8 %data, 1 + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.nonunit.shifts(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.nonunit.shifts' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Loop with non-unit bitshifts +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 2 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.result.unused(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.result.unused' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Unable to find use of computed value in loop exit block +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc +} + +define i16 @not.crc.wrong.sb.check.const(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.wrong.sb.check.const' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data.ext = zext i8 %data to i16 + %xor.crc.data = xor i16 %crc, %data.ext + %check.sb = icmp ult i16 %xor.crc.data, 128 + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 258 + %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor + %data.next = shl i8 %data, 1 + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.wrong.sb.check.pred(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.wrong.sb.check.pred' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp sgt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.excess.tc(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.excess.tc' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Loop iterations exceed bitwidth of data +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc.trunc = trunc i16 %crc to i8 + %xor.crc.data = xor i8 %crc.trunc, %data + %and.crc.data = and i8 %xor.crc.data, 1 + %data.next = lshr i8 %data, 1 + %check.sb = icmp eq i8 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.init.arg.excess.tc(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.init.arg.excess.tc' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Loop iterations exceed bitwidth of data +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 31 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i32 @not.crc.unknown.icmp.rhs(i32 %checksum, i32 %msg, i32 %unknown) { +; CHECK-LABEL: 'not.crc.unknown.icmp.rhs' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %crc = phi i32 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i32 [ %msg, %entry ], [ %data.next, %loop ] + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %xor.crc.data = xor i32 %crc, %data + %sb.crc.data = or i32 %xor.crc.data, 1 + %check.sb = icmp eq i32 %sb.crc.data, %unknown + %crc.lshr = lshr i32 %crc, 1 + %crc.xor = xor i32 %crc.lshr, 33800 + %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %data.next = lshr i32 %data, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i32 %crc.next +} + +define i32 @not.crc.unknown.icmp.lhs(i32 %checksum, i32 %msg, i32 %unknown) { +; CHECK-LABEL: 'not.crc.unknown.icmp.lhs' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %crc = phi i32 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i32 [ %msg, %entry ], [ %data.next, %loop ] + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %xor.crc.data = xor i32 %crc, %data + %sb.crc.data = or i32 %xor.crc.data, %unknown + %check.sb = icmp eq i32 %sb.crc.data, 0 + %crc.lshr = lshr i32 %crc, 1 + %crc.xor = xor i32 %crc.lshr, 33800 + %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %data.next = lshr i32 %data, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i32 %crc.next +} + + +define i16 @not.crc.stray.or(i16 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.stray.or' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %and.crc.data = and i16 %xor.crc.data, 1 + %crc.corrupt = or i16 %and.crc.data, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp ne i16 %crc.corrupt, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.inverse.sb.check(i16 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.inverse.sb.check' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %and.crc.data = and i16 %xor.crc.data, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp ne i16 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.sb.check.endian.mismatch(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.sb.check.endian.mismatch' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc.trunc = trunc i16 %crc to i8 + %xor.data.crc = xor i8 %data, %crc.trunc + %and.data.crc = and i8 %xor.data.crc, 1 + %data.next = mul i8 %data, 2 + %check.sb = icmp eq i8 %and.data.crc, 0 + %crc.lshr = mul i16 %crc, 2 + %xor = xor i16 %crc.lshr, 0 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.init.arg.inverted.select(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.init.arg.inverted.select' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %sb.crc = and i16 %crc, 1 + %check.sb = icmp eq i16 %sb.crc, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.lshr + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.bad.endian.swapped.sb.check(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.bad.endian.swapped.sb.check' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data.ext = zext i8 %data to i16 + %xor.crc.data = xor i16 %crc, %data.ext + %check.sb = icmp slt i16 %xor.crc.data, 0 + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 29 + %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor + %data.next = shl i8 %data, 1 + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i32 @not.crc.dead.msg.bad.use(i32 %checksum, i32 %msg) { +; CHECK-LABEL: 'not.crc.dead.msg.bad.use' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Recurrences not intertwined with XOR +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %crc = phi i32 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i32 [ %msg, %entry ], [ %data.next, %loop ] + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %data.or = or i32 %data, -1 + %xor.crc.data = xor i32 %crc, %data.or + %sb.crc.data = and i32 %xor.crc.data, 1 + %check.sb = icmp eq i32 %sb.crc.data, 0 + %crc.lshr = lshr i32 %crc, 1 + %crc.xor = xor i32 %crc.lshr, 33800 + %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %data.next = lshr i32 %data, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i32 %crc.next +} + +define i16 @not.crc.dead.msg.no.use(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.dead.msg.no.use' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Recurrences have stray uses +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc.trunc = trunc i16 %crc to i8 + %and.crc = and i8 %crc.trunc, 1 + %data.next = lshr i8 %data, 1 + %check.sb = icmp eq i8 %and.crc, 0 + %crc.lshr = lshr i16 %crc, 1 + %xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + %data.zext = zext i8 %data.next to i16 + %ret = xor i16 %crc.next, %data.zext + ret i16 %ret +} + +define i32 @not.crc.dead.msg.wrong.op(i32 %checksum, i32 %msg) { +; CHECK-LABEL: 'not.crc.dead.msg.wrong.op' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Recurrences not intertwined with XOR +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %crc = phi i32 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i32 [ %msg, %entry ], [ %data.next, %loop ] + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %or.crc.data = or i32 %crc, %data + %sb.crc.data = and i32 %or.crc.data, 1 + %check.sb = icmp eq i32 %sb.crc.data, 0 + %crc.lshr = lshr i32 %crc, 1 + %crc.xor = xor i32 %crc.lshr, 33800 + %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %data.next = lshr i32 %data, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i32 %crc.next +} + +define i16 @not.crc.dead.msg.xor.notin.select.chain(i16 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.dead.msg.xor.notin.select.chain' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Recurrences have stray uses +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %or.crc.data = or i16 %crc, %data + %and.crc.data = and i16 %or.crc.data, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i16 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.bad.xor.crc.data(i16 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.bad.xor.crc.data' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Recurrences have stray uses +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %mul.corrupt = mul i16 %xor.crc.data, 0 + %xor.crc.data.corrupt = xor i16 %mul.corrupt, %crc + %and.crc.data = and i16 %xor.crc.data.corrupt, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i16 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.dead.msg.or.zero(i16 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.dead.msg.or.zero' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Recurrences have stray uses +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %mul.corrupt = mul i16 %xor.crc.data, 0 + %or.crc.data.corrupt = or i16 %mul.corrupt, %crc + %and.crc.data = and i16 %or.crc.data.corrupt, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i16 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.unknown.value(i16 %msg, i16 %checksum, i16 %corrupt) { +; CHECK-LABEL: 'not.crc.unknown.value' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %xor.crc.data.corrupt = mul i16 %xor.crc.data, %corrupt + %and.crc.data = and i16 %xor.crc.data.corrupt, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i16 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.unknown.call.outside.loop(i16 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.unknown.call.outside.loop' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + %corrupt = call i16 @side.effect() + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %xor.crc.data.corrupt = mul i16 %xor.crc.data, %corrupt + %and.crc.data = and i16 %xor.crc.data.corrupt, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i16 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.constant.sb.check.corruption(i16 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.constant.sb.check.corruption' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %xor.crc.data = xor i16 %crc, %data + %xor.crc.data.corrupt = mul i16 %xor.crc.data, 2 + %and.crc.data = and i16 %xor.crc.data.corrupt, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i16 %and.crc.data, 0 + %crc.lshr = lshr i16 %crc, 1 + %crc.xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %crc.xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.float.simple.recurrence(float %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.float.simple.recurrence' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Found stray PHI +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi float [ %msg, %entry ], [ %data.next, %loop ] + %crc.conv = sitofp i16 %crc to float + %frem.data.crc = frem float %data, %crc.conv + %and.data.crc = fdiv float %frem.data.crc, 2.0 + %data.next = fdiv float %data, 2.0 + %check.sb = fcmp oeq float %and.data.crc, 0.0 + %crc.lshr = lshr i16 %crc, 1 + %xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.stray.phi(i8 %msg, i16 %checksum, i1 %c) { +; CHECK-LABEL: 'not.crc.stray.phi' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Found stray PHI +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc.trunc = trunc i16 %crc to i8 + %xor.data.crc = xor i8 %data, %crc.trunc + %and.data.crc = and i8 %xor.data.crc, 1 + %data.next = select i1 %c, i8 %data, i8 1 + %check.sb = icmp eq i8 %and.data.crc, 0 + %crc.lshr = lshr i16 %crc, 1 + %xor = xor i16 %crc.lshr, -24575 + %crc.next = select i1 %check.sb, i16 %crc.lshr, i16 %xor + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.stray.unvisited.call(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.stray.unvisited.call' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Found stray unvisited instructions +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + call void @print(i16 %crc.next) + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +declare void @print(i16) + +define i16 @not.crc.call.sb.check(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.call.sb.check' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %call = call i16 @side.effect() + %check.sb = icmp slt i16 %call, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.bad.lhs.sb.check.be(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.bad.lhs.sb.check.be' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 4129 + %check.sb = icmp slt i16 %crc.shl, 0 + %crc.next = select i1 %check.sb, i16 %crc.xor, i16 %crc.shl + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.bad.cast.sext(i8 %msg, i16 %checksum) { +; CHECK-LABEL: 'not.crc.bad.cast.sext' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Recurrences not intertwined with XOR +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc = phi i16 [ %checksum, %entry ], [ %crc.next, %loop ] + %data.ext = sext i8 %data to i16 + %xor.crc.data = xor i16 %crc, %data.ext + %check.sb = icmp sge i16 %xor.crc.data, 0 + %crc.shl = shl i16 %crc, 1 + %crc.xor = xor i16 %crc.shl, 258 + %crc.next = select i1 %check.sb, i16 %crc.shl, i16 %crc.xor + %data.next = shl i8 %data, 1 + %iv.next = add nuw nsw i8 %iv, 1 + %exit.cond = icmp ult i8 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + ret i16 %crc.next +} + + +define i16 @not.crc.sb.check.patternmatch.fail(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.sb.check.patternmatch.fail' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i16 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %evil.and.iv = and i16 %iv, 2 + %evil.and.1 = add i16 %evil.and.iv, 1 + %evil.mul = mul i16 %crc.shl, %evil.and.1 + %evil.xor = xor i16 %evil.mul, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %evil.xor, i16 %evil.mul + %iv.next = add nuw nsw i16 %iv, 1 + %exitcond.not = icmp eq i16 %iv.next, 8 + br i1 %exitcond.not, label %exit, label %loop + +exit: ; preds = %loop + ret i16 %crc.next +} + +define i16 @not.crc.sb.check.patternmatch.fail.call.outside.loop(i16 %crc.init) { +; CHECK-LABEL: 'not.crc.sb.check.patternmatch.fail.call.outside.loop' +; CHECK-NEXT: Did not find a hash algorithm +; CHECK-NEXT: Reason: Malformed significant-bit check +; +entry: + %corrupt = call i16 @side.effect() + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i16 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i16 [ %crc.init, %entry ], [ %crc.next, %loop ] + %crc.shl = shl i16 %crc, 1 + %evil.and.corrupt = and i16 %corrupt, 2 + %evil.and.1 = add i16 %evil.and.corrupt, 1 + %evil.mul = mul i16 %crc.shl, %evil.and.1 + %evil.xor = xor i16 %evil.mul, 4129 + %check.sb = icmp slt i16 %crc, 0 + %crc.next = select i1 %check.sb, i16 %evil.xor, i16 %evil.mul + %iv.next = add nuw nsw i16 %iv, 1 + %exitcond.not = icmp eq i16 %iv.next, 8 + br i1 %exitcond.not, label %exit, label %loop + +exit: ; preds = %loop + ret i16 %crc.next +} + +declare i16 @side.effect() diff --git a/llvm/test/Transforms/LoopIdiom/check-crc-optimize.ll b/llvm/test/Transforms/LoopIdiom/check-crc-optimize.ll new file mode 100644 index 0000000000000000000000000000000000000000..029efb46ab999ffd868a2bef55372f39d3fa2f48 --- /dev/null +++ b/llvm/test/Transforms/LoopIdiom/check-crc-optimize.ll @@ -0,0 +1,106 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals all --version 5 +; RUN: opt -passes=loop-idiom --enable-crc-recognize -S %s | FileCheck %s + +target triple = "aarch64-unknown-linux-gnu" + +define i32 @crc32.32(i32 %msg, i32 %checksum) #0 { +; CHECK-LABEL: define i32 @crc32.32( +; CHECK-SAME: i32 [[MSG:%.*]], i32 [[CHECKSUM:%.*]]) +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[RES:%.*]] = call i32 @llvm.aarch64.crc32w(i32 [[CHECKSUM]], i32 [[MSG]]) +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHEKC: [[LOOP]]: +; CHECK: [[CRC_NEXT_LCSSA:%.*]] = phi i32 [ [[RES]], %[[LOOP]] ] +; CHECK-NEXT: ret i32 [[CRC_NEXT_LCSSA]] +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i32 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i32 [ %msg, %entry ], [ %data.next, %loop ] + %xor.data.crc = xor i32 %data, %crc + %and.data.crc = and i32 %xor.data.crc, 1 + %data.next = lshr i32 %data, 1 + %check.sb = icmp eq i32 %and.data.crc, 0 + %crc.lshr = lshr i32 %crc, 1 + %xor = xor i32 %crc.lshr, -306674912 + %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %xor + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 31 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + %lcssa = phi i32 [ %crc.next, %loop ] + ret i32 %lcssa +} + + +define i32 @crc32.8(i8 %msg, i32 %checksum) #0 { +; CHECK-LABEL: define i32 @crc32.8( +; CHECK-SAME: i8 [[MSG:%.*]], i32 [[CHECKSUM:%.*]]) +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[EXT:%.*]] = zext i8 [[MSG]] to i32 +; CHECK-NEXT: [[RES:%.*]] = call i32 @llvm.aarch64.crc32b(i32 [[CHECKSUM]], i32 [[EXT]]) +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHEKC: [[LOOP]]: +; CHECK: [[CRC_NEXT_LCSSA:%.*]] = phi i32 [ [[RES]], %[[LOOP]] ] +; CHECK-NEXT: ret i32 [[CRC_NEXT_LCSSA]] +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i32 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i8 [ %msg, %entry ], [ %data.next, %loop ] + %crc.trunc = trunc i32 %crc to i8 + %xor.data.crc = xor i8 %data, %crc.trunc + %and.data.crc = and i8 %xor.data.crc, 1 + %data.next = lshr i8 %data, 1 + %check.sb = icmp eq i8 %and.data.crc, 0 + %crc.lshr = lshr i32 %crc, 1 + %xor = xor i32 %crc.lshr, -306674912 + %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %xor + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 7 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + %lcssa = phi i32 [ %crc.next, %loop ] + ret i32 %lcssa +} + +define i32 @crc32.16(i16 %msg, i32 %checksum) #0 { +; CHECK-LABEL: define i32 @crc32.16( +; CHECK-SAME: i16 [[MSG:%.*]], i32 [[CHECKSUM:%.*]]) +; CHECK-NEXT: [[ENTRY:.*]]: +; CHECK-NEXT: [[EXT:%.*]] = zext i16 [[MSG]] to i32 +; CHECK-NEXT: [[RES:%.*]] = call i32 @llvm.aarch64.crc32h(i32 [[CHECKSUM]], i32 [[EXT]]) +; CHECK-NEXT: br label %[[LOOP:.*]] +; CHEKC: [[LOOP]]: +; CHECK: [[CRC_NEXT_LCSSA:%.*]] = phi i32 [ [[RES]], %[[LOOP]] ] +; CHECK-NEXT: ret i32 [[CRC_NEXT_LCSSA]] +entry: + br label %loop + +loop: ; preds = %loop, %entry + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %crc = phi i32 [ %checksum, %entry ], [ %crc.next, %loop ] + %data = phi i16 [ %msg, %entry ], [ %data.next, %loop ] + %crc.trunc = trunc i32 %crc to i16 + %xor.data.crc = xor i16 %data, %crc.trunc + %and.data.crc = and i16 %xor.data.crc, 1 + %data.next = lshr i16 %data, 1 + %check.sb = icmp eq i16 %and.data.crc, 0 + %crc.lshr = lshr i32 %crc, 1 + %xor = xor i32 %crc.lshr, -306674912 + %crc.next = select i1 %check.sb, i32 %crc.lshr, i32 %xor + %iv.next = add nuw nsw i32 %iv, 1 + %exit.cond = icmp ult i32 %iv, 15 + br i1 %exit.cond, label %loop, label %exit + +exit: ; preds = %loop + %lcssa = phi i32 [ %crc.next, %loop ] + ret i32 %lcssa +} +attributes #0 = {"target-features"="+crc"} diff --git a/llvm/utils/gn/secondary/llvm/lib/Analysis/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Analysis/BUILD.gn index 2150dd02dca47bc423110c69abe809b2755da826..57401e7598538bafa89248db6298c230ffd60d3f 100644 --- a/llvm/utils/gn/secondary/llvm/lib/Analysis/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Analysis/BUILD.gn @@ -50,6 +50,7 @@ static_library("Analysis") { "FunctionPropertiesAnalysis.cpp", "GlobalsModRef.cpp", "GuardUtils.cpp", + "HashRecognize.cpp", "HeatUtils.cpp", "IRSimilarityIdentifier.cpp", "IVDescriptors.cpp",