//=== unittests/CodeGen/IRMatchers.h - Match on the LLVM IR -----*- 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 // //===----------------------------------------------------------------------===// /// \file /// This file provides a simple mechanism for performing search operations over /// IR including metadata and types. It allows writing complex search patterns /// using understandable syntax. For instance, the code: /// /// \code /// const BasicBlock *BB = ... /// const Instruction *I = match(BB, /// MInstruction(Instruction::Store, /// MConstInt(4, 8), /// MMTuple( /// MMTuple( /// MMString("omnipotent char"), /// MMTuple( /// MMString("Simple C/C++ TBAA")), /// MConstInt(0, 64)), /// MSameAs(0), /// MConstInt(0)))); /// \endcode /// /// searches the basic block BB for the 'store' instruction, first argument of /// which is 'i8 4', and the attached metadata has an item described by the /// given tree. //===----------------------------------------------------------------------===// #ifndef CLANG_UNITTESTS_CODEGEN_IRMATCHERS_H #define CLANG_UNITTESTS_CODEGEN_IRMATCHERS_H #include "llvm/ADT/PointerUnion.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Value.h" namespace llvm { /// Keeps information about pending match queries. /// /// This class stores state of all unfinished match actions. It allows to /// use queries like "this operand is the same as n-th operand", which are /// hard to implement otherwise. /// class MatcherContext { public: /// Describes pending match query. /// /// The query is represented by the current entity being investigated (type, /// value or metadata). If the entity is a member of a list (like arguments), /// the query also keeps the entity number in that list. /// class Query { PointerUnion Entity; unsigned OperandNo; public: Query(const Value *V, unsigned N) : Entity(V), OperandNo(N) {} Query(const Metadata *M, unsigned N) : Entity(M), OperandNo(N) {} Query(const Type *T, unsigned N) : Entity(T), OperandNo(N) {} template const T *get() const { return Entity.dyn_cast(); } unsigned getOperandNo() const { return OperandNo; } }; template void push(const T *V, unsigned N = ~0) { MatchStack.push_back(Query(V, N)); } void pop() { MatchStack.pop_back(); } template const T *top() const { return MatchStack.back().get(); } size_t size() const { return MatchStack.size(); } unsigned getOperandNo() const { return MatchStack.back().getOperandNo(); } /// Returns match query at the given offset from the top of queries. /// /// Offset 0 corresponds to the topmost query. /// const Query &getQuery(unsigned Offset) const { assert(MatchStack.size() > Offset); return MatchStack[MatchStack.size() - 1 - Offset]; } private: SmallVector MatchStack; }; /// Base of all matcher classes. /// class Matcher { public: virtual ~Matcher() {} /// Returns true if the entity on the top of the specified context satisfies /// the matcher condition. /// virtual bool match(MatcherContext &MC) = 0; }; /// Base class of matchers that test particular entity. /// template class EntityMatcher : public Matcher { public: bool match(MatcherContext &MC) override { if (auto V = MC.top()) return matchEntity(*V, MC); return false; } virtual bool matchEntity(const T &M, MatcherContext &C) = 0; }; /// Matcher that matches any entity of the specified kind. /// template class AnyMatcher : public EntityMatcher { public: bool matchEntity(const T &M, MatcherContext &C) override { return true; } }; /// Matcher that tests if the current entity satisfies the specified /// condition. /// template class CondMatcher : public EntityMatcher { std::function Condition; public: CondMatcher(std::function C) : Condition(C) {} bool matchEntity(const T &V, MatcherContext &C) override { return Condition(V); } }; /// Matcher that save pointer to the entity that satisfies condition of the // specified matcher. /// template class SavingMatcher : public EntityMatcher { const T *&Var; std::shared_ptr Next; public: SavingMatcher(const T *&V, std::shared_ptr N) : Var(V), Next(N) {} bool matchEntity(const T &V, MatcherContext &C) override { bool Result = Next->match(C); if (Result) Var = &V; return Result; } }; /// Matcher that checks that the entity is identical to another entity in the /// same container. /// class SameAsMatcher : public Matcher { unsigned OpNo; public: SameAsMatcher(unsigned N) : OpNo(N) {} bool match(MatcherContext &C) override { if (C.getOperandNo() != ~0U) { // Handle all known containers here. const MatcherContext::Query &StackRec = C.getQuery(1); if (const Metadata *MR = StackRec.get()) { if (const auto *MT = dyn_cast(MR)) { if (OpNo < MT->getNumOperands()) return C.top() == MT->getOperand(OpNo).get(); return false; } llvm_unreachable("Unknown metadata container"); } if (const Value *VR = StackRec.get()) { if (const auto *Insn = dyn_cast(VR)) { if (OpNo < Insn->getNumOperands()) return C.top() == Insn->getOperand(OpNo); return false; } llvm_unreachable("Unknown value container"); } llvm_unreachable("Unknown type container"); } return false; } }; /// Matcher that tests if the entity is a constant integer. /// class ConstantIntMatcher : public Matcher { uint64_t IntValue; unsigned Width; public: ConstantIntMatcher(uint64_t V, unsigned W = 0) : IntValue(V), Width(W) {} bool match(MatcherContext &Ctx) override { if (const Value *V = Ctx.top()) { if (const auto *CI = dyn_cast(V)) return (Width == 0 || CI->getBitWidth() == Width) && CI->getLimitedValue() == IntValue; } if (const Metadata *M = Ctx.top()) { if (const auto *MT = dyn_cast(M)) if (const auto *C = dyn_cast(MT->getValue())) return (Width == 0 || C->getBitWidth() == Width) && C->getLimitedValue() == IntValue; } return false; } }; /// Value matcher tuned to test instructions. /// class InstructionMatcher : public EntityMatcher { SmallVector, 8> OperandMatchers; std::shared_ptr> MetaMatcher = nullptr; unsigned Code; public: InstructionMatcher(unsigned C) : Code(C) {} void push(std::shared_ptr> M) { assert(!MetaMatcher && "Only one metadata matcher may be specified"); MetaMatcher = M; } void push(std::shared_ptr V) { OperandMatchers.push_back(V); } template void push(std::shared_ptr V, Args... A) { push(V); push(A...); } virtual bool matchInstruction(const Instruction &I) { return I.getOpcode() == Code; } bool matchEntity(const Value &V, MatcherContext &C) override { if (const auto *I = dyn_cast(&V)) { if (!matchInstruction(*I)) return false; if (OperandMatchers.size() > I->getNumOperands()) return false; for (unsigned N = 0, E = OperandMatchers.size(); N != E; ++N) { C.push(I->getOperand(N), N); if (!OperandMatchers[N]->match(C)) { C.pop(); return false; } C.pop(); } if (MetaMatcher) { SmallVector, 8> MDs; I->getAllMetadata(MDs); bool Found = false; for (auto Item : MDs) { C.push(Item.second); if (MetaMatcher->match(C)) { Found = true; C.pop(); break; } C.pop(); } return Found; } return true; } return false; } }; /// Matcher that tests type of the current value using the specified /// type matcher. /// class ValueTypeMatcher : public EntityMatcher { std::shared_ptr> TyM; public: ValueTypeMatcher(std::shared_ptr> T) : TyM(T) {} ValueTypeMatcher(const Type *T) : TyM(new CondMatcher([T](const Type &Ty) -> bool { return &Ty == T; })) {} bool matchEntity(const Value &V, MatcherContext &Ctx) override { Type *Ty = V.getType(); Ctx.push(Ty); bool Res = TyM->match(Ctx); Ctx.pop(); return Res; } }; /// Matcher that matches string metadata. /// class NameMetaMatcher : public EntityMatcher { StringRef Name; public: NameMetaMatcher(StringRef N) : Name(N) {} bool matchEntity(const Metadata &M, MatcherContext &C) override { if (auto *MDS = dyn_cast(&M)) return MDS->getString().equals(Name); return false; } }; /// Matcher that matches metadata tuples. /// class MTupleMatcher : public EntityMatcher { SmallVector, 4> Operands; public: void push(std::shared_ptr M) { Operands.push_back(M); } template void push(std::shared_ptr M, Args... A) { push(M); push(A...); } bool matchEntity(const Metadata &M, MatcherContext &C) override { if (const auto *MT = dyn_cast(&M)) { if (MT->getNumOperands() != Operands.size()) return false; for (unsigned I = 0, E = MT->getNumOperands(); I != E; ++I) { const MDOperand &Op = MT->getOperand(I); C.push(Op.get(), I); if (!Operands[I]->match(C)) { C.pop(); return false; } C.pop(); } return true; } return false; } }; // Helper function used to construct matchers. inline std::shared_ptr MSameAs(unsigned N) { return std::shared_ptr(new SameAsMatcher(N)); } template std::shared_ptr MInstruction(unsigned C, T... Args) { auto Result = new InstructionMatcher(C); Result->push(Args...); return std::shared_ptr(Result); } inline std::shared_ptr MConstInt(uint64_t V, unsigned W = 0) { return std::shared_ptr(new ConstantIntMatcher(V, W)); } inline std::shared_ptr> MValType(std::shared_ptr> T) { return std::shared_ptr>(new ValueTypeMatcher(T)); } inline std::shared_ptr> MValType(const Type *T) { return std::shared_ptr>(new ValueTypeMatcher(T)); } inline std::shared_ptr> MType(std::function C) { return std::shared_ptr>(new CondMatcher(C)); } inline std::shared_ptr> MMAny() { return std::shared_ptr>(new AnyMatcher); } inline std::shared_ptr> MMSave(const Metadata *&V, std::shared_ptr> M) { return std::shared_ptr>( new SavingMatcher(V, M)); } inline std::shared_ptr> MMString(const char *Name) { return std::shared_ptr>(new NameMetaMatcher(Name)); } template std::shared_ptr> MMTuple(T... Args) { auto Res = new MTupleMatcher(); Res->push(Args...); return std::shared_ptr>(Res); } /// Looks for the instruction that satisfies condition of the specified /// matcher inside the given basic block. /// \returns Pointer to the found instruction or nullptr if such instruction /// was not found. /// inline const Instruction *match(const BasicBlock *BB, std::shared_ptr M) { MatcherContext MC; for (const auto &I : *BB) { MC.push(&I); if (M->match(MC)) return &I; MC.pop(); } assert(MC.size() == 0); return nullptr; } /// Looks for the instruction that satisfies condition of the specified /// matcher starting from the specified instruction inside the same basic block. /// /// The given instruction is not checked. /// inline const Instruction *matchNext(const Instruction *I, std::shared_ptr M) { if (!I) return nullptr; MatcherContext MC; const BasicBlock *BB = I->getParent(); if (!BB) return nullptr; for (auto P = ++BasicBlock::const_iterator(I), E = BB->end(); P != E; ++P) { MC.push(&*P); if (M->match(MC)) return &*P; MC.pop(); } assert(MC.size() == 0); return nullptr; } } #endif