llvm-project/clang/unittests/CodeGen/IRMatchers.h

452 lines
13 KiB
C++

//=== 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<const Value *, const Metadata *, const Type *> 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<typename T>
const T *get() const {
return Entity.dyn_cast<const T *>();
}
unsigned getOperandNo() const { return OperandNo; }
};
template<typename T>
void push(const T *V, unsigned N = ~0) {
MatchStack.push_back(Query(V, N));
}
void pop() { MatchStack.pop_back(); }
template<typename T>
const T *top() const { return MatchStack.back().get<T>(); }
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<Query, 8> 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<typename T>
class EntityMatcher : public Matcher {
public:
bool match(MatcherContext &MC) override {
if (auto V = MC.top<T>())
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<typename T>
class AnyMatcher : public EntityMatcher<T> {
public:
bool matchEntity(const T &M, MatcherContext &C) override { return true; }
};
/// Matcher that tests if the current entity satisfies the specified
/// condition.
///
template<typename T>
class CondMatcher : public EntityMatcher<T> {
std::function<bool(const T &)> Condition;
public:
CondMatcher(std::function<bool(const T &)> 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<typename T>
class SavingMatcher : public EntityMatcher<T> {
const T *&Var;
std::shared_ptr<Matcher> Next;
public:
SavingMatcher(const T *&V, std::shared_ptr<Matcher> 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<Metadata>()) {
if (const auto *MT = dyn_cast<MDTuple>(MR)) {
if (OpNo < MT->getNumOperands())
return C.top<Metadata>() == MT->getOperand(OpNo).get();
return false;
}
llvm_unreachable("Unknown metadata container");
}
if (const Value *VR = StackRec.get<Value>()) {
if (const auto *Insn = dyn_cast<Instruction>(VR)) {
if (OpNo < Insn->getNumOperands())
return C.top<Value>() == 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<Value>()) {
if (const auto *CI = dyn_cast<ConstantInt>(V))
return (Width == 0 || CI->getBitWidth() == Width) &&
CI->getLimitedValue() == IntValue;
}
if (const Metadata *M = Ctx.top<Metadata>()) {
if (const auto *MT = dyn_cast<ValueAsMetadata>(M))
if (const auto *C = dyn_cast<ConstantInt>(MT->getValue()))
return (Width == 0 || C->getBitWidth() == Width) &&
C->getLimitedValue() == IntValue;
}
return false;
}
};
/// Value matcher tuned to test instructions.
///
class InstructionMatcher : public EntityMatcher<Value> {
SmallVector<std::shared_ptr<Matcher>, 8> OperandMatchers;
std::shared_ptr<EntityMatcher<Metadata>> MetaMatcher = nullptr;
unsigned Code;
public:
InstructionMatcher(unsigned C) : Code(C) {}
void push(std::shared_ptr<EntityMatcher<Metadata>> M) {
assert(!MetaMatcher && "Only one metadata matcher may be specified");
MetaMatcher = M;
}
void push(std::shared_ptr<Matcher> V) { OperandMatchers.push_back(V); }
template<typename... Args>
void push(std::shared_ptr<Matcher> 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<Instruction>(&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<std::pair<unsigned, MDNode *>, 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<Value> {
std::shared_ptr<EntityMatcher<Type>> TyM;
public:
ValueTypeMatcher(std::shared_ptr<EntityMatcher<Type>> T) : TyM(T) {}
ValueTypeMatcher(const Type *T)
: TyM(new CondMatcher<Type>([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<Metadata> {
StringRef Name;
public:
NameMetaMatcher(StringRef N) : Name(N) {}
bool matchEntity(const Metadata &M, MatcherContext &C) override {
if (auto *MDS = dyn_cast<MDString>(&M))
return MDS->getString().equals(Name);
return false;
}
};
/// Matcher that matches metadata tuples.
///
class MTupleMatcher : public EntityMatcher<Metadata> {
SmallVector<std::shared_ptr<Matcher>, 4> Operands;
public:
void push(std::shared_ptr<Matcher> M) { Operands.push_back(M); }
template<typename... Args>
void push(std::shared_ptr<Matcher> M, Args... A) {
push(M);
push(A...);
}
bool matchEntity(const Metadata &M, MatcherContext &C) override {
if (const auto *MT = dyn_cast<MDTuple>(&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<Matcher> MSameAs(unsigned N) {
return std::shared_ptr<Matcher>(new SameAsMatcher(N));
}
template<typename... T>
std::shared_ptr<InstructionMatcher> MInstruction(unsigned C, T... Args) {
auto Result = new InstructionMatcher(C);
Result->push(Args...);
return std::shared_ptr<InstructionMatcher>(Result);
}
inline std::shared_ptr<Matcher> MConstInt(uint64_t V, unsigned W = 0) {
return std::shared_ptr<Matcher>(new ConstantIntMatcher(V, W));
}
inline std::shared_ptr<EntityMatcher<Value>>
MValType(std::shared_ptr<EntityMatcher<Type>> T) {
return std::shared_ptr<EntityMatcher<Value>>(new ValueTypeMatcher(T));
}
inline std::shared_ptr<EntityMatcher<Value>> MValType(const Type *T) {
return std::shared_ptr<EntityMatcher<Value>>(new ValueTypeMatcher(T));
}
inline std::shared_ptr<EntityMatcher<Type>>
MType(std::function<bool(const Type &)> C) {
return std::shared_ptr<EntityMatcher<Type>>(new CondMatcher<Type>(C));
}
inline std::shared_ptr<EntityMatcher<Metadata>> MMAny() {
return std::shared_ptr<EntityMatcher<Metadata>>(new AnyMatcher<Metadata>);
}
inline std::shared_ptr<EntityMatcher<Metadata>>
MMSave(const Metadata *&V, std::shared_ptr<EntityMatcher<Metadata>> M) {
return std::shared_ptr<EntityMatcher<Metadata>>(
new SavingMatcher<Metadata>(V, M));
}
inline std::shared_ptr<EntityMatcher<Metadata>> MMString(const char *Name) {
return std::shared_ptr<EntityMatcher<Metadata>>(new NameMetaMatcher(Name));
}
template<typename... T>
std::shared_ptr<EntityMatcher<Metadata>> MMTuple(T... Args) {
auto Res = new MTupleMatcher();
Res->push(Args...);
return std::shared_ptr<EntityMatcher<Metadata>>(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<Matcher> 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<Matcher> 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