428 lines · cpp
1//===- DialectGen.cpp - MLIR dialect definitions generator ----------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// DialectGen uses the description of dialects to generate C++ definitions.10//11//===----------------------------------------------------------------------===//12 13#include "CppGenUtilities.h"14#include "DialectGenUtilities.h"15#include "mlir/TableGen/Class.h"16#include "mlir/TableGen/CodeGenHelpers.h"17#include "mlir/TableGen/Format.h"18#include "mlir/TableGen/GenInfo.h"19#include "mlir/TableGen/Interfaces.h"20#include "mlir/TableGen/Operator.h"21#include "mlir/TableGen/Trait.h"22#include "llvm/ADT/Sequence.h"23#include "llvm/ADT/StringExtras.h"24#include "llvm/Support/CommandLine.h"25#include "llvm/Support/Signals.h"26#include "llvm/TableGen/Error.h"27#include "llvm/TableGen/Record.h"28#include "llvm/TableGen/TableGenBackend.h"29 30#define DEBUG_TYPE "mlir-tblgen-opdefgen"31 32using namespace mlir;33using namespace mlir::tblgen;34using llvm::Record;35using llvm::RecordKeeper;36 37static llvm::cl::OptionCategory dialectGenCat("Options for -gen-dialect-*");38static llvm::cl::opt<std::string>39 selectedDialect("dialect", llvm::cl::desc("The dialect to gen for"),40 llvm::cl::cat(dialectGenCat), llvm::cl::CommaSeparated);41 42/// Utility iterator used for filtering records for a specific dialect.43namespace {44using DialectFilterIterator =45 llvm::filter_iterator<ArrayRef<Record *>::iterator,46 std::function<bool(const Record *)>>;47} // namespace48 49static void populateDiscardableAttributes(50 Dialect &dialect, const llvm::DagInit *discardableAttrDag,51 SmallVector<std::pair<std::string, std::string>> &discardableAttributes) {52 for (int i : llvm::seq<int>(0, discardableAttrDag->getNumArgs())) {53 const llvm::Init *arg = discardableAttrDag->getArg(i);54 55 StringRef givenName = discardableAttrDag->getArgNameStr(i);56 if (givenName.empty())57 PrintFatalError(dialect.getDef()->getLoc(),58 "discardable attributes must be named");59 discardableAttributes.push_back(60 {givenName.str(), arg->getAsUnquotedString()});61 }62}63 64/// Given a set of records for a T, filter the ones that correspond to65/// the given dialect.66template <typename T>67static iterator_range<DialectFilterIterator>68filterForDialect(ArrayRef<Record *> records, Dialect &dialect) {69 auto filterFn = [&](const Record *record) {70 return T(record).getDialect() == dialect;71 };72 return {DialectFilterIterator(records.begin(), records.end(), filterFn),73 DialectFilterIterator(records.end(), records.end(), filterFn)};74}75 76std::optional<Dialect>77tblgen::findDialectToGenerate(ArrayRef<Dialect> dialects) {78 if (dialects.empty()) {79 llvm::errs() << "no dialect was found\n";80 return std::nullopt;81 }82 83 // Select the dialect to gen for.84 if (dialects.size() == 1 && selectedDialect.getNumOccurrences() == 0)85 return dialects.front();86 87 if (selectedDialect.getNumOccurrences() == 0) {88 llvm::errs() << "when more than 1 dialect is present, one must be selected "89 "via '-dialect'\n";90 return std::nullopt;91 }92 93 const auto *dialectIt = llvm::find_if(dialects, [](const Dialect &dialect) {94 return dialect.getName() == selectedDialect;95 });96 if (dialectIt == dialects.end()) {97 llvm::errs() << "selected dialect with '-dialect' does not exist\n";98 return std::nullopt;99 }100 return *dialectIt;101}102 103//===----------------------------------------------------------------------===//104// GEN: Dialect declarations105//===----------------------------------------------------------------------===//106 107/// The code block for the start of a dialect class declaration.108///109/// {0}: The name of the dialect class.110/// {1}: The dialect namespace.111/// {2}: The dialect parent class.112static const char *const dialectDeclBeginStr = R"(113class {0} : public ::mlir::{2} {114 explicit {0}(::mlir::MLIRContext *context);115 116 void initialize();117 friend class ::mlir::MLIRContext;118public:119 ~{0}() override;120 static constexpr ::llvm::StringLiteral getDialectNamespace() {121 return ::llvm::StringLiteral("{1}");122 }123)";124 125/// Registration for a single dependent dialect: to be inserted in the ctor126/// above for each dependent dialect.127const char *const dialectRegistrationTemplate =128 "getContext()->loadDialect<{0}>();";129 130/// The code block for the attribute parser/printer hooks.131static const char *const attrParserDecl = R"(132 /// Parse an attribute registered to this dialect.133 ::mlir::Attribute parseAttribute(::mlir::DialectAsmParser &parser,134 ::mlir::Type type) const override;135 136 /// Print an attribute registered to this dialect.137 void printAttribute(::mlir::Attribute attr,138 ::mlir::DialectAsmPrinter &os) const override;139)";140 141/// The code block for the type parser/printer hooks.142static const char *const typeParserDecl = R"(143 /// Parse a type registered to this dialect.144 ::mlir::Type parseType(::mlir::DialectAsmParser &parser) const override;145 146 /// Print a type registered to this dialect.147 void printType(::mlir::Type type,148 ::mlir::DialectAsmPrinter &os) const override;149)";150 151/// The code block for the canonicalization pattern registration hook.152static const char *const canonicalizerDecl = R"(153 /// Register canonicalization patterns.154 void getCanonicalizationPatterns(155 ::mlir::RewritePatternSet &results) const override;156)";157 158/// The code block for the constant materializer hook.159static const char *const constantMaterializerDecl = R"(160 /// Materialize a single constant operation from a given attribute value with161 /// the desired resultant type.162 ::mlir::Operation *materializeConstant(::mlir::OpBuilder &builder,163 ::mlir::Attribute value,164 ::mlir::Type type,165 ::mlir::Location loc) override;166)";167 168/// The code block for the operation attribute verifier hook.169static const char *const opAttrVerifierDecl = R"(170 /// Provides a hook for verifying dialect attributes attached to the given171 /// op.172 ::llvm::LogicalResult verifyOperationAttribute(173 ::mlir::Operation *op, ::mlir::NamedAttribute attribute) override;174)";175 176/// The code block for the region argument attribute verifier hook.177static const char *const regionArgAttrVerifierDecl = R"(178 /// Provides a hook for verifying dialect attributes attached to the given179 /// op's region argument.180 ::llvm::LogicalResult verifyRegionArgAttribute(181 ::mlir::Operation *op, unsigned regionIndex, unsigned argIndex,182 ::mlir::NamedAttribute attribute) override;183)";184 185/// The code block for the region result attribute verifier hook.186static const char *const regionResultAttrVerifierDecl = R"(187 /// Provides a hook for verifying dialect attributes attached to the given188 /// op's region result.189 ::llvm::LogicalResult verifyRegionResultAttribute(190 ::mlir::Operation *op, unsigned regionIndex, unsigned resultIndex,191 ::mlir::NamedAttribute attribute) override;192)";193 194/// The code block for the op interface fallback hook.195static const char *const operationInterfaceFallbackDecl = R"(196 /// Provides a hook for op interface.197 void *getRegisteredInterfaceForOp(mlir::TypeID interfaceID,198 mlir::OperationName opName) override;199)";200 201/// The code block for the discardable attribute helper.202static const char *const discardableAttrHelperDecl = R"(203 /// Helper to manage the discardable attribute `{1}`.204 class {0}AttrHelper {{205 ::mlir::StringAttr name;206 public:207 static constexpr ::llvm::StringLiteral getNameStr() {{208 return "{4}.{1}";209 }210 constexpr ::mlir::StringAttr getName() {{211 return name;212 }213 214 {0}AttrHelper(::mlir::MLIRContext *ctx)215 : name(::mlir::StringAttr::get(ctx, getNameStr())) {{}216 217 {2} getAttr(::mlir::Operation *op) {{218 return op->getAttrOfType<{2}>(name);219 }220 void setAttr(::mlir::Operation *op, {2} val) {{221 op->setAttr(name, val);222 }223 bool isAttrPresent(::mlir::Operation *op) {{224 return op->hasAttrOfType<{2}>(name);225 }226 void removeAttr(::mlir::Operation *op) {{227 assert(op->hasAttrOfType<{2}>(name));228 op->removeAttr(name);229 }230 };231 {0}AttrHelper get{0}AttrHelper() {232 return {3}AttrName;233 }234 private:235 {0}AttrHelper {3}AttrName;236 public:237)";238 239/// Generate the declaration for the given dialect class.240static void emitDialectDecl(Dialect &dialect, raw_ostream &os) {241 // Emit all nested namespaces.242 {243 DialectNamespaceEmitter nsEmitter(os, dialect);244 245 // Emit the start of the decl.246 std::string cppName = dialect.getCppClassName();247 StringRef superClassName =248 dialect.isExtensible() ? "ExtensibleDialect" : "Dialect";249 250 tblgen::emitSummaryAndDescComments(os, dialect.getSummary(),251 dialect.getDescription(),252 /*terminateCmment=*/false);253 os << llvm::formatv(dialectDeclBeginStr, cppName, dialect.getName(),254 superClassName);255 256 // If the dialect requested the default attribute printer and parser, emit257 // the declarations for the hooks.258 if (dialect.useDefaultAttributePrinterParser())259 os << attrParserDecl;260 // If the dialect requested the default type printer and parser, emit the261 // delcarations for the hooks.262 if (dialect.useDefaultTypePrinterParser())263 os << typeParserDecl;264 265 // Add the decls for the various features of the dialect.266 if (dialect.hasCanonicalizer())267 os << canonicalizerDecl;268 if (dialect.hasConstantMaterializer())269 os << constantMaterializerDecl;270 if (dialect.hasOperationAttrVerify())271 os << opAttrVerifierDecl;272 if (dialect.hasRegionArgAttrVerify())273 os << regionArgAttrVerifierDecl;274 if (dialect.hasRegionResultAttrVerify())275 os << regionResultAttrVerifierDecl;276 if (dialect.hasOperationInterfaceFallback())277 os << operationInterfaceFallbackDecl;278 279 const llvm::DagInit *discardableAttrDag =280 dialect.getDiscardableAttributes();281 SmallVector<std::pair<std::string, std::string>> discardableAttributes;282 populateDiscardableAttributes(dialect, discardableAttrDag,283 discardableAttributes);284 285 for (const auto &attrPair : discardableAttributes) {286 std::string camelNameUpper = llvm::convertToCamelFromSnakeCase(287 attrPair.first, /*capitalizeFirst=*/true);288 std::string camelName = llvm::convertToCamelFromSnakeCase(289 attrPair.first, /*capitalizeFirst=*/false);290 os << llvm::formatv(discardableAttrHelperDecl, camelNameUpper,291 attrPair.first, attrPair.second, camelName,292 dialect.getName());293 }294 295 if (std::optional<StringRef> extraDecl = dialect.getExtraClassDeclaration())296 os << *extraDecl;297 298 // End the dialect decl.299 os << "};\n";300 }301 if (!dialect.getCppNamespace().empty())302 os << "MLIR_DECLARE_EXPLICIT_TYPE_ID(" << dialect.getCppNamespace()303 << "::" << dialect.getCppClassName() << ")\n";304}305 306static bool emitDialectDecls(const RecordKeeper &records, raw_ostream &os) {307 emitSourceFileHeader("Dialect Declarations", os, records);308 309 auto dialectDefs = records.getAllDerivedDefinitions("Dialect");310 if (dialectDefs.empty())311 return false;312 313 SmallVector<Dialect> dialects(dialectDefs.begin(), dialectDefs.end());314 std::optional<Dialect> dialect = findDialectToGenerate(dialects);315 if (!dialect)316 return true;317 emitDialectDecl(*dialect, os);318 return false;319}320 321//===----------------------------------------------------------------------===//322// GEN: Dialect definitions323//===----------------------------------------------------------------------===//324 325/// The code block to generate a dialect constructor definition.326///327/// {0}: The name of the dialect class.328/// {1}: Initialization code that is emitted in the ctor body before calling329/// initialize(), such as dependent dialect registration.330/// {2}: The dialect parent class.331/// {3}: Extra members to initialize332static const char *const dialectConstructorStr = R"(333{0}::{0}(::mlir::MLIRContext *context)334 : ::mlir::{2}(getDialectNamespace(), context, ::mlir::TypeID::get<{0}>())335 {3}336 {{337 {1}338 initialize();339}340)";341 342/// The code block to generate a default destructor definition.343///344/// {0}: The name of the dialect class.345static const char *const dialectDestructorStr = R"(346{0}::~{0}() = default;347 348)";349 350static void emitDialectDef(Dialect &dialect, const RecordKeeper &records,351 raw_ostream &os) {352 std::string cppClassName = dialect.getCppClassName();353 354 // Emit the TypeID explicit specializations to have a single symbol def.355 if (!dialect.getCppNamespace().empty())356 os << "MLIR_DEFINE_EXPLICIT_TYPE_ID(" << dialect.getCppNamespace()357 << "::" << cppClassName << ")\n";358 359 // Emit all nested namespaces.360 DialectNamespaceEmitter nsEmitter(os, dialect);361 362 /// Build the list of dependent dialects.363 std::string dependentDialectRegistrations;364 {365 llvm::raw_string_ostream dialectsOs(dependentDialectRegistrations);366 llvm::interleave(367 dialect.getDependentDialects(), dialectsOs,368 [&](StringRef dependentDialect) {369 dialectsOs << llvm::formatv(dialectRegistrationTemplate,370 dependentDialect);371 },372 "\n ");373 }374 375 // Emit the constructor and destructor.376 StringRef superClassName =377 dialect.isExtensible() ? "ExtensibleDialect" : "Dialect";378 379 const llvm::DagInit *discardableAttrDag = dialect.getDiscardableAttributes();380 SmallVector<std::pair<std::string, std::string>> discardableAttributes;381 populateDiscardableAttributes(dialect, discardableAttrDag,382 discardableAttributes);383 std::string discardableAttributesInit;384 for (const auto &attrPair : discardableAttributes) {385 std::string camelName = llvm::convertToCamelFromSnakeCase(386 attrPair.first, /*capitalizeFirst=*/false);387 llvm::raw_string_ostream os(discardableAttributesInit);388 os << ", " << camelName << "AttrName(context)";389 }390 391 os << llvm::formatv(dialectConstructorStr, cppClassName,392 dependentDialectRegistrations, superClassName,393 discardableAttributesInit);394 if (!dialect.hasNonDefaultDestructor())395 os << llvm::formatv(dialectDestructorStr, cppClassName);396}397 398static bool emitDialectDefs(const RecordKeeper &records, raw_ostream &os) {399 emitSourceFileHeader("Dialect Definitions", os, records);400 401 auto dialectDefs = records.getAllDerivedDefinitions("Dialect");402 if (dialectDefs.empty())403 return false;404 405 SmallVector<Dialect> dialects(dialectDefs.begin(), dialectDefs.end());406 std::optional<Dialect> dialect = findDialectToGenerate(dialects);407 if (!dialect)408 return true;409 emitDialectDef(*dialect, records, os);410 return false;411}412 413//===----------------------------------------------------------------------===//414// GEN: Dialect registration hooks415//===----------------------------------------------------------------------===//416 417static mlir::GenRegistration418 genDialectDecls("gen-dialect-decls", "Generate dialect declarations",419 [](const RecordKeeper &records, raw_ostream &os) {420 return emitDialectDecls(records, os);421 });422 423static mlir::GenRegistration424 genDialectDefs("gen-dialect-defs", "Generate dialect definitions",425 [](const RecordKeeper &records, raw_ostream &os) {426 return emitDialectDefs(records, os);427 });428