315 lines
13 KiB
C++
315 lines
13 KiB
C++
//===- DialectRegistry.h - Dialect Registration and Extension ---*- 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 defines functionality for registring and extending dialects.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef MLIR_IR_DIALECTREGISTRY_H
|
|
#define MLIR_IR_DIALECTREGISTRY_H
|
|
|
|
#include "mlir/IR/MLIRContext.h"
|
|
#include "mlir/Support/TypeID.h"
|
|
#include "llvm/ADT/ArrayRef.h"
|
|
#include "llvm/ADT/MapVector.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/Support/LogicalResult.h"
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <tuple>
|
|
|
|
namespace mlir {
|
|
class Dialect;
|
|
|
|
using DialectAllocatorFunction = std::function<Dialect *(MLIRContext *)>;
|
|
using DialectAllocatorFunctionRef = function_ref<Dialect *(MLIRContext *)>;
|
|
using DynamicDialectPopulationFunction =
|
|
std::function<void(MLIRContext *, DynamicDialect *)>;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// DialectExtension
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// This class represents an opaque dialect extension. It contains a set of
|
|
/// required dialects and an application function. The required dialects control
|
|
/// when the extension is applied, i.e. the extension is applied when all
|
|
/// required dialects are loaded. The application function can be used to attach
|
|
/// additional functionality to attributes, dialects, operations, types, etc.,
|
|
/// and may also load additional necessary dialects.
|
|
class DialectExtensionBase {
|
|
public:
|
|
virtual ~DialectExtensionBase();
|
|
|
|
/// Return the dialects that our required by this extension to be loaded
|
|
/// before applying. If empty then the extension is invoked for every loaded
|
|
/// dialect independently.
|
|
ArrayRef<StringRef> getRequiredDialects() const { return dialectNames; }
|
|
|
|
/// Apply this extension to the given context and the required dialects.
|
|
virtual void apply(MLIRContext *context,
|
|
MutableArrayRef<Dialect *> dialects) const = 0;
|
|
|
|
/// Return a copy of this extension.
|
|
virtual std::unique_ptr<DialectExtensionBase> clone() const = 0;
|
|
|
|
protected:
|
|
/// Initialize the extension with a set of required dialects.
|
|
/// If the list is empty, the extension is invoked for every loaded dialect
|
|
/// independently.
|
|
DialectExtensionBase(ArrayRef<StringRef> dialectNames)
|
|
: dialectNames(dialectNames) {}
|
|
|
|
private:
|
|
/// The names of the dialects affected by this extension.
|
|
SmallVector<StringRef> dialectNames;
|
|
};
|
|
|
|
/// This class represents a dialect extension anchored on the given set of
|
|
/// dialects. When all of the specified dialects have been loaded, the
|
|
/// application function of this extension will be executed.
|
|
template <typename DerivedT, typename... DialectsT>
|
|
class DialectExtension : public DialectExtensionBase {
|
|
public:
|
|
/// Applies this extension to the given context and set of required dialects.
|
|
virtual void apply(MLIRContext *context, DialectsT *...dialects) const = 0;
|
|
|
|
/// Return a copy of this extension.
|
|
std::unique_ptr<DialectExtensionBase> clone() const final {
|
|
return std::make_unique<DerivedT>(static_cast<const DerivedT &>(*this));
|
|
}
|
|
|
|
protected:
|
|
DialectExtension()
|
|
: DialectExtensionBase(
|
|
ArrayRef<StringRef>({DialectsT::getDialectNamespace()...})) {}
|
|
|
|
/// Override the base apply method to allow providing the exact dialect types.
|
|
void apply(MLIRContext *context,
|
|
MutableArrayRef<Dialect *> dialects) const final {
|
|
unsigned dialectIdx = 0;
|
|
auto derivedDialects = std::tuple<DialectsT *...>{
|
|
static_cast<DialectsT *>(dialects[dialectIdx++])...};
|
|
std::apply([&](DialectsT *...dialect) { apply(context, dialect...); },
|
|
derivedDialects);
|
|
}
|
|
};
|
|
|
|
namespace dialect_extension_detail {
|
|
|
|
/// Checks if the given interface, which is attempting to be used, is a
|
|
/// promised interface of this dialect that has yet to be implemented. If so,
|
|
/// emits a fatal error.
|
|
void handleUseOfUndefinedPromisedInterface(Dialect &dialect,
|
|
TypeID interfaceRequestorID,
|
|
TypeID interfaceID,
|
|
StringRef interfaceName);
|
|
|
|
/// Checks if the given interface, which is attempting to be attached, is a
|
|
/// promised interface of this dialect that has yet to be implemented. If so,
|
|
/// the promised interface is marked as resolved.
|
|
void handleAdditionOfUndefinedPromisedInterface(Dialect &dialect,
|
|
TypeID interfaceRequestorID,
|
|
TypeID interfaceID);
|
|
|
|
/// Checks if a promise has been made for the interface/requestor pair.
|
|
bool hasPromisedInterface(Dialect &dialect, TypeID interfaceRequestorID,
|
|
TypeID interfaceID);
|
|
|
|
/// Checks if a promise has been made for the interface/requestor pair.
|
|
template <typename ConcreteT, typename InterfaceT>
|
|
bool hasPromisedInterface(Dialect &dialect) {
|
|
return hasPromisedInterface(dialect, TypeID::get<ConcreteT>(),
|
|
InterfaceT::getInterfaceID());
|
|
}
|
|
|
|
} // namespace dialect_extension_detail
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// DialectRegistry
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// The DialectRegistry maps a dialect namespace to a constructor for the
|
|
/// matching dialect. This allows for decoupling the list of dialects
|
|
/// "available" from the dialects loaded in the Context. The parser in
|
|
/// particular will lazily load dialects in the Context as operations are
|
|
/// encountered.
|
|
///
|
|
/// In addition to allocator-backed registrations, the registry can also carry
|
|
/// a set of dialect *names* that some caller has asked to be preloaded into
|
|
/// the context (see `addDialectToPreload(StringRef)`). The registry itself
|
|
/// does not load those dialects — it merely records the request; the
|
|
/// allocator is expected to live in the MLIRContext's own registry, and
|
|
/// actually loading them is the caller's responsibility via
|
|
/// `preloadSelectDialects(MLIRContext *)`.
|
|
class DialectRegistry {
|
|
using MapTy =
|
|
std::map<std::string, std::pair<TypeID, DialectAllocatorFunction>,
|
|
std::less<>>;
|
|
|
|
public:
|
|
explicit DialectRegistry();
|
|
DialectRegistry(const DialectRegistry &) = delete;
|
|
DialectRegistry &operator=(const DialectRegistry &other) = delete;
|
|
DialectRegistry(DialectRegistry &&) = default;
|
|
DialectRegistry &operator=(DialectRegistry &&other) = default;
|
|
|
|
template <typename ConcreteDialect>
|
|
void insert() {
|
|
insert(TypeID::get<ConcreteDialect>(),
|
|
ConcreteDialect::getDialectNamespace(),
|
|
static_cast<DialectAllocatorFunction>(([](MLIRContext *ctx) {
|
|
// Just allocate the dialect, the context
|
|
// takes ownership of it.
|
|
return ctx->getOrLoadDialect<ConcreteDialect>();
|
|
})));
|
|
}
|
|
|
|
template <typename ConcreteDialect, typename OtherDialect,
|
|
typename... MoreDialects>
|
|
void insert() {
|
|
insert<ConcreteDialect>();
|
|
insert<OtherDialect, MoreDialects...>();
|
|
}
|
|
|
|
/// Add a new dialect constructor to the registry. The constructor must be
|
|
/// calling MLIRContext::getOrLoadDialect in order for the context to take
|
|
/// ownership of the dialect and for delayed interface registration to happen.
|
|
void insert(TypeID typeID, StringRef name,
|
|
const DialectAllocatorFunction &ctor);
|
|
|
|
/// Request that the dialect with the given name be preloaded into the
|
|
/// MLIRContext, without providing an allocator. Useful when a caller knows a
|
|
/// dialect is required but expects its allocator to be available in the
|
|
/// MLIRContext's own registry at load time (e.g. a pass learning dialect
|
|
/// names from string-valued options).
|
|
void addDialectToPreload(StringRef name);
|
|
|
|
/// Add a new dynamic dialect constructor in the registry. The constructor
|
|
/// provides as argument the created dynamic dialect, and is expected to
|
|
/// register the dialect types, attributes, and ops, using the
|
|
/// methods defined in ExtensibleDialect such as registerDynamicOperation.
|
|
void insertDynamic(StringRef name,
|
|
const DynamicDialectPopulationFunction &ctor);
|
|
|
|
/// Return an allocation function for constructing the dialect identified
|
|
/// by its namespace, or nullptr if the namespace is not in this registry.
|
|
DialectAllocatorFunctionRef getDialectAllocator(StringRef name) const;
|
|
|
|
// Register all dialects available in the current registry with the registry
|
|
// in the provided context.
|
|
void appendTo(DialectRegistry &destination) const {
|
|
for (const auto &nameAndRegistrationIt : registry)
|
|
destination.insert(nameAndRegistrationIt.second.first,
|
|
nameAndRegistrationIt.first,
|
|
nameAndRegistrationIt.second.second);
|
|
for (const std::string &name : dialectsToPreload)
|
|
destination.addDialectToPreload(StringRef(name));
|
|
// Merge the extensions.
|
|
for (const auto &extension : extensions)
|
|
destination.extensions.try_emplace(extension.first,
|
|
extension.second->clone());
|
|
}
|
|
|
|
/// Return the names of dialects registered in this registry with an
|
|
/// allocator function. Does not include preload-only entries added via
|
|
/// `addDialectToPreload(StringRef)` — use `getDialectsToPreload()` for those.
|
|
SmallVector<StringRef> getRegisteredDialectNames() const {
|
|
SmallVector<StringRef> names;
|
|
names.reserve(registry.size());
|
|
for (const auto &item : registry)
|
|
names.push_back(item.first);
|
|
return names;
|
|
}
|
|
|
|
/// Return the names of dialects that should be preloaded into the context
|
|
/// but whose allocator is expected to be resolved from the context's own
|
|
/// registry (added via `addDialectToPreload(StringRef)`).
|
|
ArrayRef<std::string> getDialectsToPreload() const {
|
|
return dialectsToPreload;
|
|
}
|
|
|
|
/// Load into `ctx` every dialect previously added via
|
|
/// `addDialectToPreload(StringRef)`. The allocator is resolved from the
|
|
/// context's own registry. On failure, if `emitError` is provided, it is
|
|
/// invoked to produce a diagnostic naming the offending dialect; otherwise
|
|
/// the failure is silent.
|
|
LogicalResult preloadSelectDialects(
|
|
MLIRContext *ctx,
|
|
function_ref<InFlightDiagnostic()> emitError = {}) const;
|
|
|
|
/// Apply any held extensions that require the given dialect. Users are not
|
|
/// expected to call this directly.
|
|
void applyExtensions(Dialect *dialect) const;
|
|
|
|
/// Apply any applicable extensions to the given context. Users are not
|
|
/// expected to call this directly.
|
|
void applyExtensions(MLIRContext *ctx) const;
|
|
|
|
/// Add the given extension to the registry.
|
|
bool addExtension(TypeID extensionID,
|
|
std::unique_ptr<DialectExtensionBase> extension) {
|
|
return extensions.try_emplace(extensionID, std::move(extension)).second;
|
|
}
|
|
|
|
/// Add the given extensions to the registry.
|
|
template <typename... ExtensionsT>
|
|
void addExtensions() {
|
|
(addExtension(TypeID::get<ExtensionsT>(), std::make_unique<ExtensionsT>()),
|
|
...);
|
|
}
|
|
|
|
/// Add an extension function that requires the given dialects.
|
|
/// Note: This bare functor overload is provided in addition to the
|
|
/// std::function variant to enable dialect type deduction, e.g.:
|
|
/// registry.addExtension(+[](MLIRContext *ctx, MyDialect *dialect) {
|
|
/// ... })
|
|
///
|
|
/// is equivalent to:
|
|
/// registry.addExtension<MyDialect>(
|
|
/// [](MLIRContext *ctx, MyDialect *dialect){ ... }
|
|
/// )
|
|
template <typename... DialectsT>
|
|
bool addExtension(void (*extensionFn)(MLIRContext *, DialectsT *...)) {
|
|
using ExtensionFnT = void (*)(MLIRContext *, DialectsT *...);
|
|
|
|
struct Extension : public DialectExtension<Extension, DialectsT...> {
|
|
Extension(const Extension &) = default;
|
|
Extension(ExtensionFnT extensionFn)
|
|
: DialectExtension<Extension, DialectsT...>(),
|
|
extensionFn(extensionFn) {}
|
|
~Extension() override = default;
|
|
|
|
void apply(MLIRContext *context, DialectsT *...dialects) const final {
|
|
extensionFn(context, dialects...);
|
|
}
|
|
ExtensionFnT extensionFn;
|
|
};
|
|
return addExtension(TypeID::getFromOpaquePointer(
|
|
reinterpret_cast<const void *>(extensionFn)),
|
|
std::make_unique<Extension>(extensionFn));
|
|
}
|
|
|
|
/// Returns true if the current registry is a subset of 'rhs', i.e. if 'rhs'
|
|
/// contains all of the components of this registry.
|
|
bool isSubsetOf(const DialectRegistry &rhs) const;
|
|
|
|
private:
|
|
MapTy registry;
|
|
/// Names of dialects that should be preloaded into the MLIRContext but for
|
|
/// which no allocator has been registered here. The allocator is expected
|
|
/// to be resolved from the MLIRContext's own registry when the dialect is
|
|
/// loaded (e.g. via MLIRContext::getOrLoadDialect).
|
|
SmallVector<std::string> dialectsToPreload;
|
|
llvm::MapVector<TypeID, std::unique_ptr<DialectExtensionBase>> extensions;
|
|
};
|
|
|
|
} // namespace mlir
|
|
|
|
#endif // MLIR_IR_DIALECTREGISTRY_H
|