blob: 1b7a3bb98bc56f433cd449fa5a52f8e0a8fbfcba [file] [log] [blame]
//===- Predicate.h - Pattern predicates -------------------------*- 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
//
//===----------------------------------------------------------------------===//
//
// This file contains definitions for "predicates" used when converting PDL into
// a matcher tree. Predicates are composed of three different parts:
//
// * Positions
// - A position refers to a specific location on the input DAG, i.e. an
// existing MLIR entity being matched. These can be attributes, operands,
// operations, results, and types. Each position also defines a relation to
// its parent. For example, the operand `[0] -> 1` has a parent operation
// position `[0]`. The attribute `[0, 1] -> "myAttr"` has parent operation
// position of `[0, 1]`. The operation `[0, 1]` has a parent operand edge
// `[0] -> 1` (i.e. it is the defining op of operand 1). The only position
// without a parent is `[0]`, which refers to the root operation.
// * Questions
// - A question refers to a query on a specific positional value. For
// example, an operation name question checks the name of an operation
// position.
// * Answers
// - An answer is the expected result of a question. For example, when
// matching an operation with the name "foo.op". The question would be an
// operation name question, with an expected answer of "foo.op".
//
//===----------------------------------------------------------------------===//
#ifndef MLIR_LIB_CONVERSION_PDLTOPDLINTERP_PREDICATE_H_
#define MLIR_LIB_CONVERSION_PDLTOPDLINTERP_PREDICATE_H_
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/OperationSupport.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/Types.h"
namespace mlir {
namespace pdl_to_pdl_interp {
namespace Predicates {
/// An enumeration of the kinds of predicates.
enum Kind : unsigned {
/// Positions, ordered by decreasing priority.
OperationPos,
OperandPos,
OperandGroupPos,
AttributePos,
ResultPos,
ResultGroupPos,
TypePos,
// Questions, ordered by dependency and decreasing priority.
IsNotNullQuestion,
OperationNameQuestion,
TypeQuestion,
AttributeQuestion,
OperandCountAtLeastQuestion,
OperandCountQuestion,
ResultCountAtLeastQuestion,
ResultCountQuestion,
EqualToQuestion,
ConstraintQuestion,
// Answers.
AttributeAnswer,
FalseAnswer,
OperationNameAnswer,
TrueAnswer,
TypeAnswer,
UnsignedAnswer,
};
} // end namespace Predicates
/// Base class for all predicates, used to allow efficient pointer comparison.
template <typename ConcreteT, typename BaseT, typename Key,
Predicates::Kind Kind>
class PredicateBase : public BaseT {
public:
using KeyTy = Key;
using Base = PredicateBase<ConcreteT, BaseT, Key, Kind>;
template <typename KeyT>
explicit PredicateBase(KeyT &&key)
: BaseT(Kind), key(std::forward<KeyT>(key)) {}
/// Get an instance of this position.
template <typename... Args>
static ConcreteT *get(StorageUniquer &uniquer, Args &&...args) {
return uniquer.get<ConcreteT>(/*initFn=*/{}, std::forward<Args>(args)...);
}
/// Construct an instance with the given storage allocator.
template <typename KeyT>
static ConcreteT *construct(StorageUniquer::StorageAllocator &alloc,
KeyT &&key) {
return new (alloc.allocate<ConcreteT>()) ConcreteT(std::forward<KeyT>(key));
}
/// Utility methods required by the storage allocator.
bool operator==(const KeyTy &key) const { return this->key == key; }
static bool classof(const BaseT *pred) { return pred->getKind() == Kind; }
/// Return the key value of this predicate.
const KeyTy &getValue() const { return key; }
protected:
KeyTy key;
};
/// Base storage for simple predicates that only unique with the kind.
template <typename ConcreteT, typename BaseT, Predicates::Kind Kind>
class PredicateBase<ConcreteT, BaseT, void, Kind> : public BaseT {
public:
using Base = PredicateBase<ConcreteT, BaseT, void, Kind>;
explicit PredicateBase() : BaseT(Kind) {}
static ConcreteT *get(StorageUniquer &uniquer) {
return uniquer.get<ConcreteT>();
}
static bool classof(const BaseT *pred) { return pred->getKind() == Kind; }
};
//===----------------------------------------------------------------------===//
// Positions
//===----------------------------------------------------------------------===//
struct OperationPosition;
/// A position describes a value on the input IR on which a predicate may be
/// applied, such as an operation or attribute. This enables re-use between
/// predicates, and assists generating bytecode and memory management.
///
/// Operation positions form the base of other positions, which are formed
/// relative to a parent operation. Operations are anchored at Operand nodes,
/// except for the root operation which is parentless.
class Position : public StorageUniquer::BaseStorage {
public:
explicit Position(Predicates::Kind kind) : kind(kind) {}
virtual ~Position();
/// Returns the depth of the first ancestor operation position.
unsigned getOperationDepth() const;
/// Returns the parent position. The root operation position has no parent.
Position *getParent() const { return parent; }
/// Returns the kind of this position.
Predicates::Kind getKind() const { return kind; }
protected:
/// Link to the parent position.
Position *parent = nullptr;
private:
/// The kind of this position.
Predicates::Kind kind;
};
//===----------------------------------------------------------------------===//
// AttributePosition
/// A position describing an attribute of an operation.
struct AttributePosition
: public PredicateBase<AttributePosition, Position,
std::pair<OperationPosition *, StringAttr>,
Predicates::AttributePos> {
explicit AttributePosition(const KeyTy &key);
/// Returns the attribute name of this position.
StringAttr getName() const { return key.second; }
};
//===----------------------------------------------------------------------===//
// OperandPosition
/// A position describing an operand of an operation.
struct OperandPosition
: public PredicateBase<OperandPosition, Position,
std::pair<OperationPosition *, unsigned>,
Predicates::OperandPos> {
explicit OperandPosition(const KeyTy &key);
/// Returns the operand number of this position.
unsigned getOperandNumber() const { return key.second; }
};
//===----------------------------------------------------------------------===//
// OperandGroupPosition
/// A position describing an operand group of an operation.
struct OperandGroupPosition
: public PredicateBase<
OperandGroupPosition, Position,
std::tuple<OperationPosition *, Optional<unsigned>, bool>,
Predicates::OperandGroupPos> {
explicit OperandGroupPosition(const KeyTy &key);
/// Returns a hash suitable for the given keytype.
static llvm::hash_code hashKey(const KeyTy &key) {
return llvm::hash_value(key);
}
/// Returns the group number of this position. If None, this group refers to
/// all operands.
Optional<unsigned> getOperandGroupNumber() const { return std::get<1>(key); }
/// Returns if the operand group has unknown size. If false, the operand group
/// has at max one element.
bool isVariadic() const { return std::get<2>(key); }
};
//===----------------------------------------------------------------------===//
// OperationPosition
/// An operation position describes an operation node in the IR. Other position
/// kinds are formed with respect to an operation position.
struct OperationPosition
: public PredicateBase<OperationPosition, Position,
std::tuple<Position *, Optional<unsigned>, unsigned>,
Predicates::OperationPos> {
static constexpr unsigned kDown = std::numeric_limits<unsigned>::max();
explicit OperationPosition(const KeyTy &key) : Base(key) {
parent = std::get<0>(key);
}
/// Returns a hash suitable for the given keytype.
static llvm::hash_code hashKey(const KeyTy &key) {
return llvm::hash_value(key);
}
/// Gets the root position.
static OperationPosition *getRoot(StorageUniquer &uniquer) {
return Base::get(uniquer, nullptr, kDown, 0);
}
/// Gets an downward operation position with the given parent.
static OperationPosition *get(StorageUniquer &uniquer, Position *parent) {
return Base::get(uniquer, parent, kDown, parent->getOperationDepth() + 1);
}
/// Gets an upward operation position with the given parent and operand.
static OperationPosition *get(StorageUniquer &uniquer, Position *parent,
Optional<unsigned> operand) {
return Base::get(uniquer, parent, operand, parent->getOperationDepth() + 1);
}
/// Returns the operand index for an upward operation position.
Optional<unsigned> getIndex() const { return std::get<1>(key); }
/// Returns if this operation position is upward, accepting an input.
bool isUpward() const { return getIndex().getValueOr(0) != kDown; }
/// Returns the depth of this position.
unsigned getDepth() const { return std::get<2>(key); }
/// Returns if this operation position corresponds to the root.
bool isRoot() const { return getDepth() == 0; }
};
//===----------------------------------------------------------------------===//
// ResultPosition
/// A position describing a result of an operation.
struct ResultPosition
: public PredicateBase<ResultPosition, Position,
std::pair<OperationPosition *, unsigned>,
Predicates::ResultPos> {
explicit ResultPosition(const KeyTy &key) : Base(key) { parent = key.first; }
/// Returns the result number of this position.
unsigned getResultNumber() const { return key.second; }
};
//===----------------------------------------------------------------------===//
// ResultGroupPosition
/// A position describing a result group of an operation.
struct ResultGroupPosition
: public PredicateBase<
ResultGroupPosition, Position,
std::tuple<OperationPosition *, Optional<unsigned>, bool>,
Predicates::ResultGroupPos> {
explicit ResultGroupPosition(const KeyTy &key) : Base(key) {
parent = std::get<0>(key);
}
/// Returns a hash suitable for the given keytype.
static llvm::hash_code hashKey(const KeyTy &key) {
return llvm::hash_value(key);
}
/// Returns the group number of this position. If None, this group refers to
/// all results.
Optional<unsigned> getResultGroupNumber() const { return std::get<1>(key); }
/// Returns if the result group has unknown size. If false, the result group
/// has at max one element.
bool isVariadic() const { return std::get<2>(key); }
};
//===----------------------------------------------------------------------===//
// TypePosition
/// A position describing the result type of an entity, i.e. an Attribute,
/// Operand, Result, etc.
struct TypePosition : public PredicateBase<TypePosition, Position, Position *,
Predicates::TypePos> {
explicit TypePosition(const KeyTy &key) : Base(key) {
assert((isa<AttributePosition, OperandPosition, OperandGroupPosition,
ResultPosition, ResultGroupPosition>(key)) &&
"expected parent to be an attribute, operand, or result");
parent = key;
}
};
//===----------------------------------------------------------------------===//
// Qualifiers
//===----------------------------------------------------------------------===//
/// An ordinal predicate consists of a "Question" and a set of acceptable
/// "Answers" (later converted to ordinal values). A predicate will query some
/// property of a positional value and decide what to do based on the result.
///
/// This makes top-level predicate representations ordinal (SwitchOp). Later,
/// predicates that end up with only one acceptable answer (including all
/// boolean kinds) will be converted to boolean predicates (PredicateOp) in the
/// matcher.
///
/// For simplicity, both are represented as "qualifiers", with a base kind and
/// perhaps additional properties. For example, all OperationName predicates ask
/// the same question, but GenericConstraint predicates may ask different ones.
class Qualifier : public StorageUniquer::BaseStorage {
public:
explicit Qualifier(Predicates::Kind kind) : kind(kind) {}
/// Returns the kind of this qualifier.
Predicates::Kind getKind() const { return kind; }
private:
/// The kind of this position.
Predicates::Kind kind;
};
//===----------------------------------------------------------------------===//
// Answers
/// An Answer representing an `Attribute` value.
struct AttributeAnswer
: public PredicateBase<AttributeAnswer, Qualifier, Attribute,
Predicates::AttributeAnswer> {
using Base::Base;
};
/// An Answer representing an `OperationName` value.
struct OperationNameAnswer
: public PredicateBase<OperationNameAnswer, Qualifier, OperationName,
Predicates::OperationNameAnswer> {
using Base::Base;
};
/// An Answer representing a boolean `true` value.
struct TrueAnswer
: PredicateBase<TrueAnswer, Qualifier, void, Predicates::TrueAnswer> {
using Base::Base;
};
/// An Answer representing a boolean 'false' value.
struct FalseAnswer
: PredicateBase<FalseAnswer, Qualifier, void, Predicates::FalseAnswer> {
using Base::Base;
};
/// An Answer representing a `Type` value. The value is stored as either a
/// TypeAttr, or an ArrayAttr of TypeAttr.
struct TypeAnswer : public PredicateBase<TypeAnswer, Qualifier, Attribute,
Predicates::TypeAnswer> {
using Base::Base;
};
/// An Answer representing an unsigned value.
struct UnsignedAnswer
: public PredicateBase<UnsignedAnswer, Qualifier, unsigned,
Predicates::UnsignedAnswer> {
using Base::Base;
};
//===----------------------------------------------------------------------===//
// Questions
/// Compare an `Attribute` to a constant value.
struct AttributeQuestion
: public PredicateBase<AttributeQuestion, Qualifier, void,
Predicates::AttributeQuestion> {};
/// Apply a parameterized constraint to multiple position values.
struct ConstraintQuestion
: public PredicateBase<
ConstraintQuestion, Qualifier,
std::tuple<StringRef, ArrayRef<Position *>, Attribute>,
Predicates::ConstraintQuestion> {
using Base::Base;
/// Construct an instance with the given storage allocator.
static ConstraintQuestion *construct(StorageUniquer::StorageAllocator &alloc,
KeyTy key) {
return Base::construct(alloc, KeyTy{alloc.copyInto(std::get<0>(key)),
alloc.copyInto(std::get<1>(key)),
std::get<2>(key)});
}
};
/// Compare the equality of two values.
struct EqualToQuestion
: public PredicateBase<EqualToQuestion, Qualifier, Position *,
Predicates::EqualToQuestion> {
using Base::Base;
};
/// Compare a positional value with null, i.e. check if it exists.
struct IsNotNullQuestion
: public PredicateBase<IsNotNullQuestion, Qualifier, void,
Predicates::IsNotNullQuestion> {};
/// Compare the number of operands of an operation with a known value.
struct OperandCountQuestion
: public PredicateBase<OperandCountQuestion, Qualifier, void,
Predicates::OperandCountQuestion> {};
struct OperandCountAtLeastQuestion
: public PredicateBase<OperandCountAtLeastQuestion, Qualifier, void,
Predicates::OperandCountAtLeastQuestion> {};
/// Compare the name of an operation with a known value.
struct OperationNameQuestion
: public PredicateBase<OperationNameQuestion, Qualifier, void,
Predicates::OperationNameQuestion> {};
/// Compare the number of results of an operation with a known value.
struct ResultCountQuestion
: public PredicateBase<ResultCountQuestion, Qualifier, void,
Predicates::ResultCountQuestion> {};
struct ResultCountAtLeastQuestion
: public PredicateBase<ResultCountAtLeastQuestion, Qualifier, void,
Predicates::ResultCountAtLeastQuestion> {};
/// Compare the type of an attribute or value with a known type.
struct TypeQuestion : public PredicateBase<TypeQuestion, Qualifier, void,
Predicates::TypeQuestion> {};
//===----------------------------------------------------------------------===//
// PredicateUniquer
//===----------------------------------------------------------------------===//
/// This class provides a storage uniquer that is used to allocate predicate
/// instances.
class PredicateUniquer : public StorageUniquer {
public:
PredicateUniquer() {
// Register the types of Positions with the uniquer.
registerParametricStorageType<AttributePosition>();
registerParametricStorageType<OperandPosition>();
registerParametricStorageType<OperandGroupPosition>();
registerParametricStorageType<OperationPosition>();
registerParametricStorageType<ResultPosition>();
registerParametricStorageType<ResultGroupPosition>();
registerParametricStorageType<TypePosition>();
// Register the types of Questions with the uniquer.
registerParametricStorageType<AttributeAnswer>();
registerParametricStorageType<OperationNameAnswer>();
registerParametricStorageType<TypeAnswer>();
registerParametricStorageType<UnsignedAnswer>();
registerSingletonStorageType<FalseAnswer>();
registerSingletonStorageType<TrueAnswer>();
// Register the types of Answers with the uniquer.
registerParametricStorageType<ConstraintQuestion>();
registerParametricStorageType<EqualToQuestion>();
registerSingletonStorageType<AttributeQuestion>();
registerSingletonStorageType<IsNotNullQuestion>();
registerSingletonStorageType<OperandCountQuestion>();
registerSingletonStorageType<OperandCountAtLeastQuestion>();
registerSingletonStorageType<OperationNameQuestion>();
registerSingletonStorageType<ResultCountQuestion>();
registerSingletonStorageType<ResultCountAtLeastQuestion>();
registerSingletonStorageType<TypeQuestion>();
}
};
//===----------------------------------------------------------------------===//
// PredicateBuilder
//===----------------------------------------------------------------------===//
/// This class provides utilities for constructing predicates.
class PredicateBuilder {
public:
PredicateBuilder(PredicateUniquer &uniquer, MLIRContext *ctx)
: uniquer(uniquer), ctx(ctx) {}
//===--------------------------------------------------------------------===//
// Positions
//===--------------------------------------------------------------------===//
/// Returns the root operation position.
Position *getRoot() { return OperationPosition::getRoot(uniquer); }
/// Returns the parent position defining the value held by the given operand.
OperationPosition *getOperandDefiningOp(Position *p) {
assert((isa<OperandPosition, OperandGroupPosition>(p)) &&
"expected operand position");
return OperationPosition::get(uniquer, p);
}
/// Returns the position of operation using the value at the given index.
OperationPosition *getUsersOp(Position *p, Optional<unsigned> operand) {
assert((isa<OperandPosition, OperandGroupPosition, ResultPosition,
ResultGroupPosition>(p)) &&
"expected result position");
return OperationPosition::get(uniquer, p, operand);
}
/// Returns an attribute position for an attribute of the given operation.
Position *getAttribute(OperationPosition *p, StringRef name) {
return AttributePosition::get(uniquer, p, StringAttr::get(ctx, name));
}
/// Returns an operand position for an operand of the given operation.
Position *getOperand(OperationPosition *p, unsigned operand) {
return OperandPosition::get(uniquer, p, operand);
}
/// Returns a position for a group of operands of the given operation.
Position *getOperandGroup(OperationPosition *p, Optional<unsigned> group,
bool isVariadic) {
return OperandGroupPosition::get(uniquer, p, group, isVariadic);
}
Position *getAllOperands(OperationPosition *p) {
return getOperandGroup(p, /*group=*/llvm::None, /*isVariadic=*/true);
}
/// Returns a result position for a result of the given operation.
Position *getResult(OperationPosition *p, unsigned result) {
return ResultPosition::get(uniquer, p, result);
}
/// Returns a position for a group of results of the given operation.
Position *getResultGroup(OperationPosition *p, Optional<unsigned> group,
bool isVariadic) {
return ResultGroupPosition::get(uniquer, p, group, isVariadic);
}
Position *getAllResults(OperationPosition *p) {
return getResultGroup(p, /*group=*/llvm::None, /*isVariadic=*/true);
}
/// Returns a type position for the given entity.
Position *getType(Position *p) { return TypePosition::get(uniquer, p); }
//===--------------------------------------------------------------------===//
// Qualifiers
//===--------------------------------------------------------------------===//
/// An ordinal predicate consists of a "Question" and a set of acceptable
/// "Answers" (later converted to ordinal values). A predicate will query some
/// property of a positional value and decide what to do based on the result.
using Predicate = std::pair<Qualifier *, Qualifier *>;
/// Create a predicate comparing an attribute to a known value.
Predicate getAttributeConstraint(Attribute attr) {
return {AttributeQuestion::get(uniquer),
AttributeAnswer::get(uniquer, attr)};
}
/// Create a predicate checking if two values are equal.
Predicate getEqualTo(Position *pos) {
return {EqualToQuestion::get(uniquer, pos), TrueAnswer::get(uniquer)};
}
/// Create a predicate checking if two values are not equal.
Predicate getNotEqualTo(Position *pos) {
return {EqualToQuestion::get(uniquer, pos), FalseAnswer::get(uniquer)};
}
/// Create a predicate that applies a generic constraint.
Predicate getConstraint(StringRef name, ArrayRef<Position *> pos,
Attribute params) {
return {
ConstraintQuestion::get(uniquer, std::make_tuple(name, pos, params)),
TrueAnswer::get(uniquer)};
}
/// Create a predicate comparing a value with null.
Predicate getIsNotNull() {
return {IsNotNullQuestion::get(uniquer), TrueAnswer::get(uniquer)};
}
/// Create a predicate comparing the number of operands of an operation to a
/// known value.
Predicate getOperandCount(unsigned count) {
return {OperandCountQuestion::get(uniquer),
UnsignedAnswer::get(uniquer, count)};
}
Predicate getOperandCountAtLeast(unsigned count) {
return {OperandCountAtLeastQuestion::get(uniquer),
UnsignedAnswer::get(uniquer, count)};
}
/// Create a predicate comparing the name of an operation to a known value.
Predicate getOperationName(StringRef name) {
return {OperationNameQuestion::get(uniquer),
OperationNameAnswer::get(uniquer, OperationName(name, ctx))};
}
/// Create a predicate comparing the number of results of an operation to a
/// known value.
Predicate getResultCount(unsigned count) {
return {ResultCountQuestion::get(uniquer),
UnsignedAnswer::get(uniquer, count)};
}
Predicate getResultCountAtLeast(unsigned count) {
return {ResultCountAtLeastQuestion::get(uniquer),
UnsignedAnswer::get(uniquer, count)};
}
/// Create a predicate comparing the type of an attribute or value to a known
/// type. The value is stored as either a TypeAttr, or an ArrayAttr of
/// TypeAttr.
Predicate getTypeConstraint(Attribute type) {
return {TypeQuestion::get(uniquer), TypeAnswer::get(uniquer, type)};
}
private:
/// The uniquer used when allocating predicate nodes.
PredicateUniquer &uniquer;
/// The current MLIR context.
MLIRContext *ctx;
};
} // end namespace pdl_to_pdl_interp
} // end namespace mlir
#endif // MLIR_CONVERSION_PDLTOPDLINTERP_PREDICATE_H_