1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150 |
- #pragma once
- #ifdef __GNUC__
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wunused-parameter"
- #endif
- //===- IRSimilarityIdentifier.h - Find similarity in a module --------------==//
- //
- // 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
- //
- //===----------------------------------------------------------------------===//
- //
- // \file
- // Interface file for the IRSimilarityIdentifier for identifying similarities in
- // IR including the IRInstructionMapper, which maps an Instruction to unsigned
- // integers.
- //
- // Two sequences of instructions are called "similar" if they perform the same
- // series of operations for all inputs.
- //
- // \code
- // %1 = add i32 %a, 10
- // %2 = add i32 %a, %1
- // %3 = icmp slt icmp %1, %2
- // \endcode
- //
- // and
- //
- // \code
- // %1 = add i32 11, %a
- // %2 = sub i32 %a, %1
- // %3 = icmp sgt icmp %2, %1
- // \endcode
- //
- // ultimately have the same result, even if the inputs, and structure are
- // slightly different.
- //
- // For instructions, we do not worry about operands that do not have fixed
- // semantic meaning to the program. We consider the opcode that the instruction
- // has, the types, parameters, and extra information such as the function name,
- // or comparison predicate. These are used to create a hash to map instructions
- // to integers to be used in similarity matching in sequences of instructions
- //
- // Terminology:
- // An IRSimilarityCandidate is a region of IRInstructionData (wrapped
- // Instructions), usually used to denote a region of similarity has been found.
- //
- // A SimilarityGroup is a set of IRSimilarityCandidates that are structurally
- // similar to one another.
- //
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_ANALYSIS_IRSIMILARITYIDENTIFIER_H
- #define LLVM_ANALYSIS_IRSIMILARITYIDENTIFIER_H
- #include "llvm/IR/InstVisitor.h"
- #include "llvm/IR/Instructions.h"
- #include "llvm/IR/PassManager.h"
- #include "llvm/Pass.h"
- #include "llvm/Support/Allocator.h"
- #include <optional>
- namespace llvm {
- class Module;
- namespace IRSimilarity {
- struct IRInstructionDataList;
- /// This represents what is and is not supported when finding similarity in
- /// Instructions.
- ///
- /// Legal Instructions are considered when looking at similarity between
- /// Instructions.
- ///
- /// Illegal Instructions cannot be considered when looking for similarity
- /// between Instructions. They act as boundaries between similarity regions.
- ///
- /// Invisible Instructions are skipped over during analysis.
- // TODO: Shared with MachineOutliner
- enum InstrType { Legal, Illegal, Invisible };
- /// This provides the utilities for hashing an Instruction to an unsigned
- /// integer. Two IRInstructionDatas produce the same hash value when their
- /// underlying Instructions perform the same operation (even if they don't have
- /// the same input operands.)
- /// As a more concrete example, consider the following:
- ///
- /// \code
- /// %add1 = add i32 %a, %b
- /// %add2 = add i32 %c, %d
- /// %add3 = add i64 %e, %f
- /// \endcode
- ///
- // Then the IRInstructionData wrappers for these Instructions may be hashed like
- /// so:
- ///
- /// \code
- /// ; These two adds have the same types and operand types, so they hash to the
- /// ; same number.
- /// %add1 = add i32 %a, %b ; Hash: 1
- /// %add2 = add i32 %c, %d ; Hash: 1
- /// ; This add produces an i64. This differentiates it from %add1 and %add2. So,
- /// ; it hashes to a different number.
- /// %add3 = add i64 %e, %f; Hash: 2
- /// \endcode
- ///
- ///
- /// This hashing scheme will be used to represent the program as a very long
- /// string. This string can then be placed in a data structure which can be used
- /// for similarity queries.
- ///
- /// TODO: Handle types of Instructions which can be equal even with different
- /// operands. (E.g. comparisons with swapped predicates.)
- /// TODO: Handle CallInsts, which are only checked for function type
- /// by \ref isSameOperationAs.
- /// TODO: Handle GetElementPtrInsts, as some of the operands have to be the
- /// exact same, and some do not.
- struct IRInstructionData
- : ilist_node<IRInstructionData, ilist_sentinel_tracking<true>> {
- /// The source Instruction that is being wrapped.
- Instruction *Inst = nullptr;
- /// The values of the operands in the Instruction.
- SmallVector<Value *, 4> OperVals;
- /// The legality of the wrapped instruction. This is informed by InstrType,
- /// and is used when checking when two instructions are considered similar.
- /// If either instruction is not legal, the instructions are automatically not
- /// considered similar.
- bool Legal = false;
- /// This is only relevant if we are wrapping a CmpInst where we needed to
- /// change the predicate of a compare instruction from a greater than form
- /// to a less than form. It is None otherwise.
- std::optional<CmpInst::Predicate> RevisedPredicate;
- /// This is only relevant if we are wrapping a CallInst. If we are requiring
- /// that the function calls have matching names as well as types, and the
- /// call is not an indirect call, this will hold the name of the function. If
- /// it is an indirect string, it will be the empty string. However, if this
- /// requirement is not in place it will be the empty string regardless of the
- /// function call type. The value held here is used to create the hash of the
- /// instruction, and check to make sure two instructions are close to one
- /// another.
- std::optional<std::string> CalleeName;
- /// This structure holds the distances of how far "ahead of" or "behind" the
- /// target blocks of a branch, or the incoming blocks of a phi nodes are.
- /// If the value is negative, it means that the block was registered before
- /// the block of this instruction in terms of blocks in the function.
- /// Code Example:
- /// \code
- /// block_1:
- /// br i1 %0, label %block_2, label %block_3
- /// block_2:
- /// br i1 %1, label %block_1, label %block_2
- /// block_3:
- /// br i1 %2, label %block_2, label %block_1
- /// ; Replacing the labels with relative values, this becomes:
- /// block_1:
- /// br i1 %0, distance 1, distance 2
- /// block_2:
- /// br i1 %1, distance -1, distance 0
- /// block_3:
- /// br i1 %2, distance -1, distance -2
- /// \endcode
- /// Taking block_2 as our example, block_1 is "behind" block_2, and block_2 is
- /// "ahead" of block_2.
- SmallVector<int, 4> RelativeBlockLocations;
- /// Gather the information that is difficult to gather for an Instruction, or
- /// is changed. i.e. the operands of an Instruction and the Types of those
- /// operands. This extra information allows for similarity matching to make
- /// assertions that allow for more flexibility when checking for whether an
- /// Instruction performs the same operation.
- IRInstructionData(Instruction &I, bool Legality, IRInstructionDataList &IDL);
- IRInstructionData(IRInstructionDataList &IDL);
- /// Fills data stuctures for IRInstructionData when it is constructed from a
- // reference or a pointer.
- void initializeInstruction();
- /// Get the predicate that the compare instruction is using for hashing the
- /// instruction. the IRInstructionData must be wrapping a CmpInst.
- CmpInst::Predicate getPredicate() const;
- /// Get the callee name that the call instruction is using for hashing the
- /// instruction. The IRInstructionData must be wrapping a CallInst.
- StringRef getCalleeName() const;
- /// A function that swaps the predicates to their less than form if they are
- /// in a greater than form. Otherwise, the predicate is unchanged.
- ///
- /// \param CI - The comparison operation to find a consistent preidcate for.
- /// \return the consistent comparison predicate.
- static CmpInst::Predicate predicateForConsistency(CmpInst *CI);
- /// For an IRInstructionData containing a branch, finds the
- /// relative distances from the source basic block to the target by taking
- /// the difference of the number assigned to the current basic block and the
- /// target basic block of the branch.
- ///
- /// \param BasicBlockToInteger - The mapping of basic blocks to their location
- /// in the module.
- void
- setBranchSuccessors(DenseMap<BasicBlock *, unsigned> &BasicBlockToInteger);
- /// For an IRInstructionData containing a CallInst, set the function name
- /// appropriately. This will be an empty string if it is an indirect call,
- /// or we are not matching by name of the called function. It will be the
- /// name of the function if \p MatchByName is true and it is not an indirect
- /// call. We may decide not to match by name in order to expand the
- /// size of the regions we can match. If a function name has the same type
- /// signature, but the different name, the region of code is still almost the
- /// same. Since function names can be treated as constants, the name itself
- /// could be extrapolated away. However, matching by name provides a
- /// specificity and more "identical" code than not matching by name.
- ///
- /// \param MatchByName - A flag to mark whether we are using the called
- /// function name as a differentiating parameter.
- void setCalleeName(bool MatchByName = true);
- /// For an IRInstructionData containing a PHINode, finds the
- /// relative distances from the incoming basic block to the current block by
- /// taking the difference of the number assigned to the current basic block
- /// and the incoming basic block of the branch.
- ///
- /// \param BasicBlockToInteger - The mapping of basic blocks to their location
- /// in the module.
- void
- setPHIPredecessors(DenseMap<BasicBlock *, unsigned> &BasicBlockToInteger);
- /// Hashes \p Value based on its opcode, types, and operand types.
- /// Two IRInstructionData instances produce the same hash when they perform
- /// the same operation.
- ///
- /// As a simple example, consider the following instructions.
- ///
- /// \code
- /// %add1 = add i32 %x1, %y1
- /// %add2 = add i32 %x2, %y2
- ///
- /// %sub = sub i32 %x1, %y1
- ///
- /// %add_i64 = add i64 %x2, %y2
- /// \endcode
- ///
- /// Because the first two adds operate the same types, and are performing the
- /// same action, they will be hashed to the same value.
- ///
- /// However, the subtraction instruction is not the same as an addition, and
- /// will be hashed to a different value.
- ///
- /// Finally, the last add has a different type compared to the first two add
- /// instructions, so it will also be hashed to a different value that any of
- /// the previous instructions.
- ///
- /// \param [in] ID - The IRInstructionData instance to be hashed.
- /// \returns A hash_value of the IRInstructionData.
- friend hash_code hash_value(const IRInstructionData &ID) {
- SmallVector<Type *, 4> OperTypes;
- for (Value *V : ID.OperVals)
- OperTypes.push_back(V->getType());
- if (isa<CmpInst>(ID.Inst))
- return llvm::hash_combine(
- llvm::hash_value(ID.Inst->getOpcode()),
- llvm::hash_value(ID.Inst->getType()),
- llvm::hash_value(ID.getPredicate()),
- llvm::hash_combine_range(OperTypes.begin(), OperTypes.end()));
- if (IntrinsicInst *II = dyn_cast<IntrinsicInst>(ID.Inst)) {
- // To hash intrinsics, we use the opcode, and types like the other
- // instructions, but also, the Intrinsic ID, and the Name of the
- // intrinsic.
- Intrinsic::ID IntrinsicID = II->getIntrinsicID();
- return llvm::hash_combine(
- llvm::hash_value(ID.Inst->getOpcode()),
- llvm::hash_value(ID.Inst->getType()), llvm::hash_value(IntrinsicID),
- llvm::hash_value(*ID.CalleeName),
- llvm::hash_combine_range(OperTypes.begin(), OperTypes.end()));
- }
- if (isa<CallInst>(ID.Inst)) {
- std::string FunctionName = *ID.CalleeName;
- return llvm::hash_combine(
- llvm::hash_value(ID.Inst->getOpcode()),
- llvm::hash_value(ID.Inst->getType()),
- llvm::hash_value(ID.Inst->getType()), llvm::hash_value(FunctionName),
- llvm::hash_combine_range(OperTypes.begin(), OperTypes.end()));
- }
- return llvm::hash_combine(
- llvm::hash_value(ID.Inst->getOpcode()),
- llvm::hash_value(ID.Inst->getType()),
- llvm::hash_combine_range(OperTypes.begin(), OperTypes.end()));
- }
- IRInstructionDataList *IDL = nullptr;
- };
- struct IRInstructionDataList
- : simple_ilist<IRInstructionData, ilist_sentinel_tracking<true>> {};
- /// Compare one IRInstructionData class to another IRInstructionData class for
- /// whether they are performing a the same operation, and can mapped to the
- /// same value. For regular instructions if the hash value is the same, then
- /// they will also be close.
- ///
- /// \param A - The first IRInstructionData class to compare
- /// \param B - The second IRInstructionData class to compare
- /// \returns true if \p A and \p B are similar enough to be mapped to the same
- /// value.
- bool isClose(const IRInstructionData &A, const IRInstructionData &B);
- struct IRInstructionDataTraits : DenseMapInfo<IRInstructionData *> {
- static inline IRInstructionData *getEmptyKey() { return nullptr; }
- static inline IRInstructionData *getTombstoneKey() {
- return reinterpret_cast<IRInstructionData *>(-1);
- }
- static unsigned getHashValue(const IRInstructionData *E) {
- using llvm::hash_value;
- assert(E && "IRInstructionData is a nullptr?");
- return hash_value(*E);
- }
- static bool isEqual(const IRInstructionData *LHS,
- const IRInstructionData *RHS) {
- if (RHS == getEmptyKey() || RHS == getTombstoneKey() ||
- LHS == getEmptyKey() || LHS == getTombstoneKey())
- return LHS == RHS;
- assert(LHS && RHS && "nullptr should have been caught by getEmptyKey?");
- return isClose(*LHS, *RHS);
- }
- };
- /// Helper struct for converting the Instructions in a Module into a vector of
- /// unsigned integers. This vector of unsigned integers can be thought of as a
- /// "numeric string". This numeric string can then be queried by, for example,
- /// data structures that find repeated substrings.
- ///
- /// This hashing is done per BasicBlock in the module. To hash Instructions
- /// based off of their operations, each Instruction is wrapped in an
- /// IRInstructionData struct. The unsigned integer for an IRInstructionData
- /// depends on:
- /// - The hash provided by the IRInstructionData.
- /// - Which member of InstrType the IRInstructionData is classified as.
- // See InstrType for more details on the possible classifications, and how they
- // manifest in the numeric string.
- ///
- /// The numeric string for an individual BasicBlock is terminated by an unique
- /// unsigned integer. This prevents data structures which rely on repetition
- /// from matching across BasicBlocks. (For example, the SuffixTree.)
- /// As a concrete example, if we have the following two BasicBlocks:
- /// \code
- /// bb0:
- /// %add1 = add i32 %a, %b
- /// %add2 = add i32 %c, %d
- /// %add3 = add i64 %e, %f
- /// bb1:
- /// %sub = sub i32 %c, %d
- /// \endcode
- /// We may hash the Instructions like this (via IRInstructionData):
- /// \code
- /// bb0:
- /// %add1 = add i32 %a, %b ; Hash: 1
- /// %add2 = add i32 %c, %d; Hash: 1
- /// %add3 = add i64 %e, %f; Hash: 2
- /// bb1:
- /// %sub = sub i32 %c, %d; Hash: 3
- /// %add4 = add i32 %c, %d ; Hash: 1
- /// \endcode
- /// And produce a "numeric string representation" like so:
- /// 1, 1, 2, unique_integer_1, 3, 1, unique_integer_2
- ///
- /// TODO: This is very similar to the MachineOutliner, and should be
- /// consolidated into the same interface.
- struct IRInstructionMapper {
- /// The starting illegal instruction number to map to.
- ///
- /// Set to -3 for compatibility with DenseMapInfo<unsigned>.
- unsigned IllegalInstrNumber = static_cast<unsigned>(-3);
- /// The next available integer to assign to a legal Instruction to.
- unsigned LegalInstrNumber = 0;
- /// Correspondence from IRInstructionData to unsigned integers.
- DenseMap<IRInstructionData *, unsigned, IRInstructionDataTraits>
- InstructionIntegerMap;
- /// A mapping for a basic block in a module to its assigned number/location
- /// in the module.
- DenseMap<BasicBlock *, unsigned> BasicBlockToInteger;
- /// Set if we added an illegal number in the previous step.
- /// Since each illegal number is unique, we only need one of them between
- /// each range of legal numbers. This lets us make sure we don't add more
- /// than one illegal number per range.
- bool AddedIllegalLastTime = false;
- /// Marks whether we found a illegal instruction in the previous step.
- bool CanCombineWithPrevInstr = false;
- /// Marks whether we have found a set of instructions that is long enough
- /// to be considered for similarity.
- bool HaveLegalRange = false;
- /// Marks whether we should use exact function names, as well as types to
- /// find similarity between calls.
- bool EnableMatchCallsByName = false;
- /// This allocator pointer is in charge of holding on to the IRInstructionData
- /// so it is not deallocated until whatever external tool is using it is done
- /// with the information.
- SpecificBumpPtrAllocator<IRInstructionData> *InstDataAllocator = nullptr;
- /// This allocator pointer is in charge of creating the IRInstructionDataList
- /// so it is not deallocated until whatever external tool is using it is done
- /// with the information.
- SpecificBumpPtrAllocator<IRInstructionDataList> *IDLAllocator = nullptr;
- /// Get an allocated IRInstructionData struct using the InstDataAllocator.
- ///
- /// \param I - The Instruction to wrap with IRInstructionData.
- /// \param Legality - A boolean value that is true if the instruction is to
- /// be considered for similarity, and false if not.
- /// \param IDL - The InstructionDataList that the IRInstructionData is
- /// inserted into.
- /// \returns An allocated IRInstructionData struct.
- IRInstructionData *allocateIRInstructionData(Instruction &I, bool Legality,
- IRInstructionDataList &IDL);
- /// Get an empty allocated IRInstructionData struct using the
- /// InstDataAllocator.
- ///
- /// \param IDL - The InstructionDataList that the IRInstructionData is
- /// inserted into.
- /// \returns An allocated IRInstructionData struct.
- IRInstructionData *allocateIRInstructionData(IRInstructionDataList &IDL);
- /// Get an allocated IRInstructionDataList object using the IDLAllocator.
- ///
- /// \returns An allocated IRInstructionDataList object.
- IRInstructionDataList *allocateIRInstructionDataList();
- IRInstructionDataList *IDL = nullptr;
- /// Assigns values to all the basic blocks in function \p F starting from
- /// integer \p BBNumber.
- ///
- /// \param F - The function containing the basic blocks to assign numbers to.
- /// \param BBNumber - The number to start from.
- void initializeForBBs(Function &F, unsigned &BBNumber) {
- for (BasicBlock &BB : F)
- BasicBlockToInteger.insert(std::make_pair(&BB, BBNumber++));
- }
- /// Assigns values to all the basic blocks in Module \p M.
- /// \param M - The module containing the basic blocks to assign numbers to.
- void initializeForBBs(Module &M) {
- unsigned BBNumber = 0;
- for (Function &F : M)
- initializeForBBs(F, BBNumber);
- }
- /// Maps the Instructions in a BasicBlock \p BB to legal or illegal integers
- /// determined by \p InstrType. Two Instructions are mapped to the same value
- /// if they are close as defined by the InstructionData class above.
- ///
- /// \param [in] BB - The BasicBlock to be mapped to integers.
- /// \param [in,out] InstrList - Vector of IRInstructionData to append to.
- /// \param [in,out] IntegerMapping - Vector of unsigned integers to append to.
- void convertToUnsignedVec(BasicBlock &BB,
- std::vector<IRInstructionData *> &InstrList,
- std::vector<unsigned> &IntegerMapping);
- /// Maps an Instruction to a legal integer.
- ///
- /// \param [in] It - The Instruction to be mapped to an integer.
- /// \param [in,out] IntegerMappingForBB - Vector of unsigned integers to
- /// append to.
- /// \param [in,out] InstrListForBB - Vector of InstructionData to append to.
- /// \returns The integer \p It was mapped to.
- unsigned mapToLegalUnsigned(BasicBlock::iterator &It,
- std::vector<unsigned> &IntegerMappingForBB,
- std::vector<IRInstructionData *> &InstrListForBB);
- /// Maps an Instruction to an illegal integer.
- ///
- /// \param [in] It - The \p Instruction to be mapped to an integer.
- /// \param [in,out] IntegerMappingForBB - Vector of unsigned integers to
- /// append to.
- /// \param [in,out] InstrListForBB - Vector of IRInstructionData to append to.
- /// \param End - true if creating a dummy IRInstructionData at the end of a
- /// basic block.
- /// \returns The integer \p It was mapped to.
- unsigned mapToIllegalUnsigned(
- BasicBlock::iterator &It, std::vector<unsigned> &IntegerMappingForBB,
- std::vector<IRInstructionData *> &InstrListForBB, bool End = false);
- IRInstructionMapper(SpecificBumpPtrAllocator<IRInstructionData> *IDA,
- SpecificBumpPtrAllocator<IRInstructionDataList> *IDLA)
- : InstDataAllocator(IDA), IDLAllocator(IDLA) {
- // Make sure that the implementation of DenseMapInfo<unsigned> hasn't
- // changed.
- assert(DenseMapInfo<unsigned>::getEmptyKey() == static_cast<unsigned>(-1) &&
- "DenseMapInfo<unsigned>'s empty key isn't -1!");
- assert(DenseMapInfo<unsigned>::getTombstoneKey() ==
- static_cast<unsigned>(-2) &&
- "DenseMapInfo<unsigned>'s tombstone key isn't -2!");
- IDL = new (IDLAllocator->Allocate())
- IRInstructionDataList();
- }
- /// Custom InstVisitor to classify different instructions for whether it can
- /// be analyzed for similarity.
- struct InstructionClassification
- : public InstVisitor<InstructionClassification, InstrType> {
- InstructionClassification() = default;
- // TODO: Determine a scheme to resolve when the label is similar enough.
- InstrType visitBranchInst(BranchInst &BI) {
- if (EnableBranches)
- return Legal;
- return Illegal;
- }
- InstrType visitPHINode(PHINode &PN) {
- if (EnableBranches)
- return Legal;
- return Illegal;
- }
- // TODO: Handle allocas.
- InstrType visitAllocaInst(AllocaInst &AI) { return Illegal; }
- // We exclude variable argument instructions since variable arguments
- // requires extra checking of the argument list.
- InstrType visitVAArgInst(VAArgInst &VI) { return Illegal; }
- // We exclude all exception handling cases since they are so context
- // dependent.
- InstrType visitLandingPadInst(LandingPadInst &LPI) { return Illegal; }
- InstrType visitFuncletPadInst(FuncletPadInst &FPI) { return Illegal; }
- // DebugInfo should be included in the regions, but should not be
- // analyzed for similarity as it has no bearing on the outcome of the
- // program.
- InstrType visitDbgInfoIntrinsic(DbgInfoIntrinsic &DII) { return Invisible; }
- InstrType visitIntrinsicInst(IntrinsicInst &II) {
- // These are disabled due to complications in the CodeExtractor when
- // outlining these instructions. For instance, It is unclear what we
- // should do when moving only the start or end lifetime instruction into
- // an outlined function. Also, assume-like intrinsics could be removed
- // from the region, removing arguments, causing discrepencies in the
- // number of inputs between different regions.
- if (II.isAssumeLikeIntrinsic())
- return Illegal;
- return EnableIntrinsics ? Legal : Illegal;
- }
- // We only allow call instructions where the function has a name and
- // is not an indirect call.
- InstrType visitCallInst(CallInst &CI) {
- Function *F = CI.getCalledFunction();
- bool IsIndirectCall = CI.isIndirectCall();
- if (IsIndirectCall && !EnableIndirectCalls)
- return Illegal;
- if (!F && !IsIndirectCall)
- return Illegal;
- // Functions marked with the swifttailcc and tailcc calling conventions
- // require special handling when outlining musttail functions. The
- // calling convention must be passed down to the outlined function as
- // well. Further, there is special handling for musttail calls as well,
- // requiring a return call directly after. For now, the outliner does not
- // support this, so we do not handle matching this case either.
- if ((CI.getCallingConv() == CallingConv::SwiftTail ||
- CI.getCallingConv() == CallingConv::Tail) &&
- !EnableMustTailCalls)
- return Illegal;
- if (CI.isMustTailCall() && !EnableMustTailCalls)
- return Illegal;
- return Legal;
- }
- // TODO: We do not current handle similarity that changes the control flow.
- InstrType visitInvokeInst(InvokeInst &II) { return Illegal; }
- // TODO: We do not current handle similarity that changes the control flow.
- InstrType visitCallBrInst(CallBrInst &CBI) { return Illegal; }
- // TODO: Handle interblock similarity.
- InstrType visitTerminator(Instruction &I) { return Illegal; }
- InstrType visitInstruction(Instruction &I) { return Legal; }
- // The flag variable that lets the classifier know whether we should
- // allow branches to be checked for similarity.
- bool EnableBranches = false;
- // The flag variable that lets the classifier know whether we should
- // allow indirect calls to be considered legal instructions.
- bool EnableIndirectCalls = false;
- // Flag that lets the classifier know whether we should allow intrinsics to
- // be checked for similarity.
- bool EnableIntrinsics = false;
-
- // Flag that lets the classifier know whether we should allow tail calls to
- // be checked for similarity.
- bool EnableMustTailCalls = false;
- };
- /// Maps an Instruction to a member of InstrType.
- InstructionClassification InstClassifier;
- };
- /// This is a class that wraps a range of IRInstructionData from one point to
- /// another in the vector of IRInstructionData, which is a region of the
- /// program. It is also responsible for defining the structure within this
- /// region of instructions.
- ///
- /// The structure of a region is defined through a value numbering system
- /// assigned to each unique value in a region at the creation of the
- /// IRSimilarityCandidate.
- ///
- /// For example, for each Instruction we add a mapping for each new
- /// value seen in that Instruction.
- /// IR: Mapping Added:
- /// %add1 = add i32 %a, c1 %add1 -> 3, %a -> 1, c1 -> 2
- /// %add2 = add i32 %a, %1 %add2 -> 4
- /// %add3 = add i32 c2, c1 %add3 -> 6, c2 -> 5
- ///
- /// We can compare IRSimilarityCandidates against one another.
- /// The \ref isSimilar function compares each IRInstructionData against one
- /// another and if we have the same sequences of IRInstructionData that would
- /// create the same hash, we have similar IRSimilarityCandidates.
- ///
- /// We can also compare the structure of IRSimilarityCandidates. If we can
- /// create a mapping of registers in the region contained by one
- /// IRSimilarityCandidate to the region contained by different
- /// IRSimilarityCandidate, they can be considered structurally similar.
- ///
- /// IRSimilarityCandidate1: IRSimilarityCandidate2:
- /// %add1 = add i32 %a, %b %add1 = add i32 %d, %e
- /// %add2 = add i32 %a, %c %add2 = add i32 %d, %f
- /// %add3 = add i32 c1, c2 %add3 = add i32 c3, c4
- ///
- /// Can have the following mapping from candidate to candidate of:
- /// %a -> %d, %b -> %e, %c -> %f, c1 -> c3, c2 -> c4
- /// and can be considered similar.
- ///
- /// IRSimilarityCandidate1: IRSimilarityCandidate2:
- /// %add1 = add i32 %a, %b %add1 = add i32 %d, c4
- /// %add2 = add i32 %a, %c %add2 = add i32 %d, %f
- /// %add3 = add i32 c1, c2 %add3 = add i32 c3, c4
- ///
- /// We cannot create the same mapping since the use of c4 is not used in the
- /// same way as %b or c2.
- class IRSimilarityCandidate {
- private:
- /// The start index of this IRSimilarityCandidate in the instruction list.
- unsigned StartIdx = 0;
- /// The number of instructions in this IRSimilarityCandidate.
- unsigned Len = 0;
- /// The first instruction in this IRSimilarityCandidate.
- IRInstructionData *FirstInst = nullptr;
- /// The last instruction in this IRSimilarityCandidate.
- IRInstructionData *LastInst = nullptr;
- /// Global Value Numbering structures
- /// @{
- /// Stores the mapping of the value to the number assigned to it in the
- /// IRSimilarityCandidate.
- DenseMap<Value *, unsigned> ValueToNumber;
- /// Stores the mapping of the number to the value assigned this number.
- DenseMap<unsigned, Value *> NumberToValue;
- /// Stores the mapping of a value's number to canonical numbering in the
- /// candidate's respective similarity group.
- DenseMap<unsigned, unsigned> NumberToCanonNum;
- /// Stores the mapping of canonical number in the candidate's respective
- /// similarity group to a value number.
- DenseMap<unsigned, unsigned> CanonNumToNumber;
- /// @}
- public:
- /// \param StartIdx - The starting location of the region.
- /// \param Len - The length of the region.
- /// \param FirstInstIt - The starting IRInstructionData of the region.
- /// \param LastInstIt - The ending IRInstructionData of the region.
- IRSimilarityCandidate(unsigned StartIdx, unsigned Len,
- IRInstructionData *FirstInstIt,
- IRInstructionData *LastInstIt);
- /// \param A - The first IRInstructionCandidate to compare.
- /// \param B - The second IRInstructionCandidate to compare.
- /// \returns True when every IRInstructionData in \p A is similar to every
- /// IRInstructionData in \p B.
- static bool isSimilar(const IRSimilarityCandidate &A,
- const IRSimilarityCandidate &B);
- /// \param [in] A - The first IRInstructionCandidate to compare.
- /// \param [in] B - The second IRInstructionCandidate to compare.
- /// \returns True when every IRInstructionData in \p A is structurally similar
- /// to \p B.
- static bool compareStructure(const IRSimilarityCandidate &A,
- const IRSimilarityCandidate &B);
- /// \param [in] A - The first IRInstructionCandidate to compare.
- /// \param [in] B - The second IRInstructionCandidate to compare.
- /// \param [in,out] ValueNumberMappingA - A mapping of value numbers from
- /// candidate \p A to candidate \B.
- /// \param [in,out] ValueNumberMappingB - A mapping of value numbers from
- /// candidate \p B to candidate \A.
- /// \returns True when every IRInstructionData in \p A is structurally similar
- /// to \p B.
- static bool
- compareStructure(const IRSimilarityCandidate &A,
- const IRSimilarityCandidate &B,
- DenseMap<unsigned, DenseSet<unsigned>> &ValueNumberMappingA,
- DenseMap<unsigned, DenseSet<unsigned>> &ValueNumberMappingB);
- struct OperandMapping {
- /// The IRSimilarityCandidate that holds the instruction the OperVals were
- /// pulled from.
- const IRSimilarityCandidate &IRSC;
- /// The operand values to be analyzed.
- ArrayRef<Value *> &OperVals;
- /// The current mapping of global value numbers from one IRSimilarityCandidate
- /// to another IRSimilarityCandidate.
- DenseMap<unsigned, DenseSet<unsigned>> &ValueNumberMapping;
- };
- /// A helper struct to hold the candidate, for a branch instruction, the
- /// relative location of a label, and the label itself. This is mostly to
- /// group the values together before passing them as a bundle to a function.
- struct RelativeLocMapping {
- /// The IRSimilarityCandidate that holds the instruction the relative
- /// location was pulled from.
- const IRSimilarityCandidate &IRSC;
- /// The relative location to be analyzed.
- int RelativeLocation;
- /// The corresponding value.
- Value *OperVal;
- };
- /// Compare the operands in \p A and \p B and check that the current mapping
- /// of global value numbers from \p A to \p B and \p B to \A is consistent.
- ///
- /// \param A - The first IRInstructionCandidate, operand values, and current
- /// operand mappings to compare.
- /// \param B - The second IRInstructionCandidate, operand values, and current
- /// operand mappings to compare.
- /// \returns true if the IRSimilarityCandidates operands are compatible.
- static bool compareNonCommutativeOperandMapping(OperandMapping A,
- OperandMapping B);
- /// Compare the operands in \p A and \p B and check that the current mapping
- /// of global value numbers from \p A to \p B and \p B to \A is consistent
- /// given that the operands are commutative.
- ///
- /// \param A - The first IRInstructionCandidate, operand values, and current
- /// operand mappings to compare.
- /// \param B - The second IRInstructionCandidate, operand values, and current
- /// operand mappings to compare.
- /// \returns true if the IRSimilarityCandidates operands are compatible.
- static bool compareCommutativeOperandMapping(OperandMapping A,
- OperandMapping B);
- /// Compare the relative locations in \p A and \p B and check that the
- /// distances match if both locations are contained in the region, and that
- /// the branches both point outside the region if they do not.
- /// Example Region:
- /// \code
- /// entry:
- /// br i1 %0, label %block_1, label %block_3
- /// block_0:
- /// br i1 %0, label %block_1, label %block_2
- /// block_1:
- /// br i1 %0, label %block_2, label %block_3
- /// block_2:
- /// br i1 %1, label %block_1, label %block_4
- /// block_3:
- /// br i1 %2, label %block_2, label %block_5
- /// \endcode
- /// If we compare the branches in block_0 and block_1 the relative values are
- /// 1 and 2 for both, so we consider this a match.
- ///
- /// If we compare the branches in entry and block_0 the relative values are
- /// 2 and 3, and 1 and 2 respectively. Since these are not the same we do not
- /// consider them a match.
- ///
- /// If we compare the branches in block_1 and block_2 the relative values are
- /// 1 and 2, and -1 and None respectively. As a result we do not consider
- /// these to be the same
- ///
- /// If we compare the branches in block_2 and block_3 the relative values are
- /// -1 and None for both. We do consider these to be a match.
- ///
- /// \param A - The first IRInstructionCandidate, relative location value,
- /// and incoming block.
- /// \param B - The second IRInstructionCandidate, relative location value,
- /// and incoming block.
- /// \returns true if the relative locations match.
- static bool checkRelativeLocations(RelativeLocMapping A,
- RelativeLocMapping B);
- /// Create a mapping from the value numbering to a different separate set of
- /// numbers. This will serve as a guide for relating one candidate to another.
- /// The canonical number gives use the ability identify which global value
- /// number in one candidate relates to the global value number in the other.
- ///
- /// \param [in, out] CurrCand - The IRSimilarityCandidate to create a
- /// canonical numbering for.
- static void createCanonicalMappingFor(IRSimilarityCandidate &CurrCand);
- /// Create a mapping for the value numbering of the calling
- /// IRSimilarityCandidate, to a different separate set of numbers, based on
- /// the canonical ordering in \p SourceCand. These are defined based on the
- /// found mappings in \p ToSourceMapping and \p FromSourceMapping. Both of
- /// these relationships should have the same information, just in opposite
- /// directions.
- ///
- /// \param [in, out] SourceCand - The IRSimilarityCandidate to create a
- /// canonical numbering from.
- /// \param ToSourceMapping - The mapping of value numbers from this candidate
- /// to \p SourceCand.
- /// \param FromSourceMapping - The mapping of value numbers from \p SoureCand
- /// to this candidate.
- void createCanonicalRelationFrom(
- IRSimilarityCandidate &SourceCand,
- DenseMap<unsigned, DenseSet<unsigned>> &ToSourceMapping,
- DenseMap<unsigned, DenseSet<unsigned>> &FromSourceMapping);
- /// \param [in,out] BBSet - The set to track the basic blocks.
- void getBasicBlocks(DenseSet<BasicBlock *> &BBSet) const {
- for (IRInstructionData &ID : *this) {
- BasicBlock *BB = ID.Inst->getParent();
- BBSet.insert(BB);
- }
- }
- /// \param [in,out] BBSet - The set to track the basic blocks.
- /// \param [in,out] BBList - A list in order of use to track the basic blocks.
- void getBasicBlocks(DenseSet<BasicBlock *> &BBSet,
- SmallVector<BasicBlock *> &BBList) const {
- for (IRInstructionData &ID : *this) {
- BasicBlock *BB = ID.Inst->getParent();
- if (BBSet.insert(BB).second)
- BBList.push_back(BB);
- }
- }
- /// Compare the start and end indices of the two IRSimilarityCandidates for
- /// whether they overlap. If the start instruction of one
- /// IRSimilarityCandidate is less than the end instruction of the other, and
- /// the start instruction of one is greater than the start instruction of the
- /// other, they overlap.
- ///
- /// \returns true if the IRSimilarityCandidates do not have overlapping
- /// instructions.
- static bool overlap(const IRSimilarityCandidate &A,
- const IRSimilarityCandidate &B);
- /// \returns the number of instructions in this Candidate.
- unsigned getLength() const { return Len; }
- /// \returns the start index of this IRSimilarityCandidate.
- unsigned getStartIdx() const { return StartIdx; }
- /// \returns the end index of this IRSimilarityCandidate.
- unsigned getEndIdx() const { return StartIdx + Len - 1; }
- /// \returns The first IRInstructionData.
- IRInstructionData *front() const { return FirstInst; }
- /// \returns The last IRInstructionData.
- IRInstructionData *back() const { return LastInst; }
- /// \returns The first Instruction.
- Instruction *frontInstruction() { return FirstInst->Inst; }
- /// \returns The last Instruction
- Instruction *backInstruction() { return LastInst->Inst; }
- /// \returns The BasicBlock the IRSimilarityCandidate starts in.
- BasicBlock *getStartBB() { return FirstInst->Inst->getParent(); }
- /// \returns The BasicBlock the IRSimilarityCandidate ends in.
- BasicBlock *getEndBB() { return LastInst->Inst->getParent(); }
- /// \returns The Function that the IRSimilarityCandidate is located in.
- Function *getFunction() { return getStartBB()->getParent(); }
- /// Finds the positive number associated with \p V if it has been mapped.
- /// \param [in] V - the Value to find.
- /// \returns The positive number corresponding to the value.
- /// \returns std::nullopt if not present.
- std::optional<unsigned> getGVN(Value *V) {
- assert(V != nullptr && "Value is a nullptr?");
- DenseMap<Value *, unsigned>::iterator VNIt = ValueToNumber.find(V);
- if (VNIt == ValueToNumber.end())
- return std::nullopt;
- return VNIt->second;
- }
- /// Finds the Value associate with \p Num if it exists.
- /// \param [in] Num - the number to find.
- /// \returns The Value associated with the number.
- /// \returns std::nullopt if not present.
- std::optional<Value *> fromGVN(unsigned Num) {
- DenseMap<unsigned, Value *>::iterator VNIt = NumberToValue.find(Num);
- if (VNIt == NumberToValue.end())
- return std::nullopt;
- assert(VNIt->second != nullptr && "Found value is a nullptr!");
- return VNIt->second;
- }
- /// Find the canonical number from the global value number \p N stored in the
- /// candidate.
- ///
- /// \param N - The global value number to find the canonical number for.
- /// \returns An optional containing the value, and std::nullopt if it could
- /// not be found.
- std::optional<unsigned> getCanonicalNum(unsigned N) {
- DenseMap<unsigned, unsigned>::iterator NCIt = NumberToCanonNum.find(N);
- if (NCIt == NumberToCanonNum.end())
- return std::nullopt;
- return NCIt->second;
- }
- /// Find the global value number from the canonical number \p N stored in the
- /// candidate.
- ///
- /// \param N - The canonical number to find the global vlaue number for.
- /// \returns An optional containing the value, and std::nullopt if it could
- /// not be found.
- std::optional<unsigned> fromCanonicalNum(unsigned N) {
- DenseMap<unsigned, unsigned>::iterator CNIt = CanonNumToNumber.find(N);
- if (CNIt == CanonNumToNumber.end())
- return std::nullopt;
- return CNIt->second;
- }
- /// \param RHS -The IRSimilarityCandidate to compare against
- /// \returns true if the IRSimilarityCandidate is occurs after the
- /// IRSimilarityCandidate in the program.
- bool operator<(const IRSimilarityCandidate &RHS) const {
- return getStartIdx() > RHS.getStartIdx();
- }
- using iterator = IRInstructionDataList::iterator;
- iterator begin() const { return iterator(front()); }
- iterator end() const { return std::next(iterator(back())); }
- };
- typedef DenseMap<IRSimilarityCandidate *,
- DenseMap<unsigned, DenseSet<unsigned>>>
- CandidateGVNMapping;
- typedef std::vector<IRSimilarityCandidate> SimilarityGroup;
- typedef std::vector<SimilarityGroup> SimilarityGroupList;
- /// This class puts all the pieces of the IRInstructionData,
- /// IRInstructionMapper, IRSimilarityCandidate together.
- ///
- /// It first feeds the Module or vector of Modules into the IRInstructionMapper,
- /// and puts all the mapped instructions into a single long list of
- /// IRInstructionData.
- ///
- /// The list of unsigned integers is given to the Suffix Tree or similar data
- /// structure to find repeated subsequences. We construct an
- /// IRSimilarityCandidate for each instance of the subsequence. We compare them
- /// against one another since These repeated subsequences can have different
- /// structure. For each different kind of structure found, we create a
- /// similarity group.
- ///
- /// If we had four IRSimilarityCandidates A, B, C, and D where A, B and D are
- /// structurally similar to one another, while C is different we would have two
- /// SimilarityGroups:
- ///
- /// SimilarityGroup 1: SimilarityGroup 2
- /// A, B, D C
- ///
- /// A list of the different similarity groups is then returned after
- /// analyzing the module.
- class IRSimilarityIdentifier {
- public:
- IRSimilarityIdentifier(bool MatchBranches = true,
- bool MatchIndirectCalls = true,
- bool MatchCallsWithName = false,
- bool MatchIntrinsics = true,
- bool MatchMustTailCalls = true)
- : Mapper(&InstDataAllocator, &InstDataListAllocator),
- EnableBranches(MatchBranches), EnableIndirectCalls(MatchIndirectCalls),
- EnableMatchingCallsByName(MatchCallsWithName),
- EnableIntrinsics(MatchIntrinsics),
- EnableMustTailCalls(MatchMustTailCalls) {}
- private:
- /// Map the instructions in the module to unsigned integers, using mapping
- /// already present in the Mapper if possible.
- ///
- /// \param [in] M Module - To map to integers.
- /// \param [in,out] InstrList - The vector to append IRInstructionData to.
- /// \param [in,out] IntegerMapping - The vector to append integers to.
- void populateMapper(Module &M, std::vector<IRInstructionData *> &InstrList,
- std::vector<unsigned> &IntegerMapping);
- /// Map the instructions in the modules vector to unsigned integers, using
- /// mapping already present in the mapper if possible.
- ///
- /// \param [in] Modules - The list of modules to use to populate the mapper
- /// \param [in,out] InstrList - The vector to append IRInstructionData to.
- /// \param [in,out] IntegerMapping - The vector to append integers to.
- void populateMapper(ArrayRef<std::unique_ptr<Module>> &Modules,
- std::vector<IRInstructionData *> &InstrList,
- std::vector<unsigned> &IntegerMapping);
- /// Find the similarity candidates in \p InstrList and corresponding
- /// \p UnsignedVec
- ///
- /// \param [in,out] InstrList - The vector to append IRInstructionData to.
- /// \param [in,out] IntegerMapping - The vector to append integers to.
- /// candidates found in the program.
- void findCandidates(std::vector<IRInstructionData *> &InstrList,
- std::vector<unsigned> &IntegerMapping);
- public:
- // Find the IRSimilarityCandidates in the \p Modules and group by structural
- // similarity in a SimilarityGroup, each group is returned in a
- // SimilarityGroupList.
- //
- // \param [in] Modules - the modules to analyze.
- // \returns The groups of similarity ranges found in the modules.
- SimilarityGroupList &
- findSimilarity(ArrayRef<std::unique_ptr<Module>> Modules);
- // Find the IRSimilarityCandidates in the given Module grouped by structural
- // similarity in a SimilarityGroup, contained inside a SimilarityGroupList.
- //
- // \param [in] M - the module to analyze.
- // \returns The groups of similarity ranges found in the module.
- SimilarityGroupList &findSimilarity(Module &M);
- // Clears \ref SimilarityCandidates if it is already filled by a previous run.
- void resetSimilarityCandidates() {
- // If we've already analyzed a Module or set of Modules, so we must clear
- // the SimilarityCandidates to make sure we do not have only old values
- // hanging around.
- if (SimilarityCandidates)
- SimilarityCandidates->clear();
- else
- SimilarityCandidates = SimilarityGroupList();
- }
- // \returns The groups of similarity ranges found in the most recently passed
- // set of modules.
- std::optional<SimilarityGroupList> &getSimilarity() {
- return SimilarityCandidates;
- }
- private:
- /// The allocator for IRInstructionData.
- SpecificBumpPtrAllocator<IRInstructionData> InstDataAllocator;
- /// The allocator for IRInstructionDataLists.
- SpecificBumpPtrAllocator<IRInstructionDataList> InstDataListAllocator;
- /// Map Instructions to unsigned integers and wraps the Instruction in an
- /// instance of IRInstructionData.
- IRInstructionMapper Mapper;
- /// The flag variable that marks whether we should check branches for
- /// similarity, or only look within basic blocks.
- bool EnableBranches = true;
- /// The flag variable that marks whether we allow indirect calls to be checked
- /// for similarity, or exclude them as a legal instruction.
- bool EnableIndirectCalls = true;
- /// The flag variable that marks whether we allow calls to be marked as
- /// similar if they do not have the same name, only the same calling
- /// convention, attributes and type signature.
- bool EnableMatchingCallsByName = true;
- /// The flag variable that marks whether we should check intrinsics for
- /// similarity.
- bool EnableIntrinsics = true;
- // The flag variable that marks whether we should allow tailcalls
- // to be checked for similarity.
- bool EnableMustTailCalls = false;
- /// The SimilarityGroups found with the most recent run of \ref
- /// findSimilarity. std::nullopt if there is no recent run.
- std::optional<SimilarityGroupList> SimilarityCandidates;
- };
- } // end namespace IRSimilarity
- /// An analysis pass based on legacy pass manager that runs and returns
- /// IRSimilarityIdentifier run on the Module.
- class IRSimilarityIdentifierWrapperPass : public ModulePass {
- std::unique_ptr<IRSimilarity::IRSimilarityIdentifier> IRSI;
- public:
- static char ID;
- IRSimilarityIdentifierWrapperPass();
- IRSimilarity::IRSimilarityIdentifier &getIRSI() { return *IRSI; }
- const IRSimilarity::IRSimilarityIdentifier &getIRSI() const { return *IRSI; }
- bool doInitialization(Module &M) override;
- bool doFinalization(Module &M) override;
- bool runOnModule(Module &M) override;
- void getAnalysisUsage(AnalysisUsage &AU) const override {
- AU.setPreservesAll();
- }
- };
- /// An analysis pass that runs and returns the IRSimilarityIdentifier run on the
- /// Module.
- class IRSimilarityAnalysis : public AnalysisInfoMixin<IRSimilarityAnalysis> {
- public:
- typedef IRSimilarity::IRSimilarityIdentifier Result;
- Result run(Module &M, ModuleAnalysisManager &);
- private:
- friend AnalysisInfoMixin<IRSimilarityAnalysis>;
- static AnalysisKey Key;
- };
- /// Printer pass that uses \c IRSimilarityAnalysis.
- class IRSimilarityAnalysisPrinterPass
- : public PassInfoMixin<IRSimilarityAnalysisPrinterPass> {
- raw_ostream &OS;
- public:
- explicit IRSimilarityAnalysisPrinterPass(raw_ostream &OS) : OS(OS) {}
- PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
- };
- } // end namespace llvm
- #endif // LLVM_ANALYSIS_IRSIMILARITYIDENTIFIER_H
- #ifdef __GNUC__
- #pragma GCC diagnostic pop
- #endif
|