diff --git a/llvm/include/llvm/ADT/Hashing.h b/llvm/include/llvm/ADT/Hashing.h --- a/llvm/include/llvm/ADT/Hashing.h +++ b/llvm/include/llvm/ADT/Hashing.h @@ -104,6 +104,9 @@ template std::enable_if_t::value, hash_code> hash_value(T value); +hash_code hash_value(float value); +hash_code hash_value(double value); + /// Compute a hash_code for a pointer's address. /// /// N.B.: This hashes the *address*. Not the value and not the type. diff --git a/llvm/lib/Support/Hashing.cpp b/llvm/lib/Support/Hashing.cpp --- a/llvm/lib/Support/Hashing.cpp +++ b/llvm/lib/Support/Hashing.cpp @@ -26,3 +26,10 @@ void llvm::set_fixed_execution_hash_seed(uint64_t fixed_value) { hashing::detail::fixed_seed_override = fixed_value; } + +hash_code llvm::hash_value(float value) { + return hash_value(*(uint32_t *)&value); +} +hash_code llvm::hash_value(double value) { + return hash_value(*(uint64_t *)&value); +} diff --git a/mlir/cmake/modules/AddMLIR.cmake b/mlir/cmake/modules/AddMLIR.cmake --- a/mlir/cmake/modules/AddMLIR.cmake +++ b/mlir/cmake/modules/AddMLIR.cmake @@ -9,6 +9,8 @@ set(LLVM_TARGET_DEFINITIONS ${dialect}.td) mlir_tablegen(${dialect}.h.inc -gen-op-decls) mlir_tablegen(${dialect}.cpp.inc -gen-op-defs) + mlir_tablegen(${dialect}Types.h.inc -gen-typedef-decls) + mlir_tablegen(${dialect}Types.cpp.inc -gen-typedef-defs) mlir_tablegen(${dialect}Dialect.h.inc -gen-dialect-decls -dialect=${dialect_namespace}) add_public_tablegen_target(MLIR${dialect}IncGen) add_dependencies(mlir-headers MLIR${dialect}IncGen) diff --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td --- a/mlir/include/mlir/IR/OpBase.td +++ b/mlir/include/mlir/IR/OpBase.td @@ -2321,4 +2321,80 @@ // so to replace the matched DAG with an existing SSA value. def replaceWithValue; + +//===----------------------------------------------------------------------===// +// Data type generation +//===----------------------------------------------------------------------===// + +// Define a new type belonging to a dialect and called 'name' +class TypeDef { + Dialect owningDialect = dialect; + string cppClassName = name # "Type"; + + // Name of storage class to generate or use + string storageClass = name # "TypeStorage"; + // Namespace (withing dialect c++ namespace) in which the storage class resides + string storageNamespace = "detail"; + // Should we generate the storage class? (Or use an existing one?) + bit genStorageClass = 1; + // Should we generate the storage class constructor? + bit hasStorageCustomConstructor = 0; + + // This is the list of fields in the storage class (and list of parameters + // in the creation functions). If empty, don't use or generate a storage class + dag members = (ins); + + // Use the lowercased name as the keyword for parsing/printing. Specify only + // if you want tblgen to automatically generate the printer/parser for this + // type. + string mnemonic = ?; + + // If null, generate just the declarations. + // If an empty code block, generate print/parser methods only if 'mnemonic' is specified + // If a non-empty code block, just use that code as the definition code. + code printer = [{}]; + code parser = [{}]; + + // If set, generate accessors for each Type member + bit genAccessors = 1; + // Generate the verifyConstructionInvariants declaration and getChecked method + bit genVerifyInvariantsDecl = 0; + // Extra code to include in the class declaration + code extraDecls = [{}]; +} + +// 'Members' should be subclasses of this or simple strings (which is a +// shorthand for TypeMember<"C++Type">) +class TypeMember { + code allocator = ?; + string cppType = type; +} + +// For StringRefs, which require allocation +class StringRefMember : TypeMember<"StringRef"> { + let allocator = [{$_dst = $_allocator.copyInto($_self);}]; +} + +// For standard ArrayRefs, which require allocation +class ArrayRefMember : TypeMember<"ArrayRef<" # arrayOf # ">"> { + let allocator = [{$_dst = $_allocator.copyInto($_self);}]; +} + +// For classes which require allocation and have their own allocateInto method +class SelfAllocationMember : TypeMember { + let allocator = [{$_dst = $_self.allocateInto($_allocator);}]; +} + +// For ArrayRefs which contain things which allocate themselves +class ArrayRefOfSelfAllocationMember : TypeMember<"ArrayRef<" # arrayOf # ">"> { + let allocator = [{ + SmallVector<}] # arrayOf # [{, 4> tmpFields($_self.size()); + for (size_t i=0; i<$_self.size(); i++) { + tmpFields[i] = $_self[i].allocateInto($_allocator); + } + $_dst = $_allocator.copyInto(ArrayRef<}] # arrayOf # [{>(tmpFields)); + }]; +} + + #endif // OP_BASE diff --git a/mlir/include/mlir/TableGen/TypeDef.h b/mlir/include/mlir/TableGen/TypeDef.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/TableGen/TypeDef.h @@ -0,0 +1,131 @@ +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// TypeDef wrapper to simplify using TableGen Record defining a MLIR dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_TABLEGEN_TYPEDEF_H +#define MLIR_TABLEGEN_TYPEDEF_H + +#include "mlir/Support/LLVM.h" +#include "mlir/TableGen/Dialect.h" +#include "llvm/TableGen/Record.h" +#include +#include + +namespace llvm { +class Record; +class DagInit; +} // end namespace llvm + +namespace mlir { +namespace tblgen { + +class TypeMember; + +// Wrapper class that contains a MLIR dialect's information defined in TableGen +// and provides helper methods for accessing them. +class TypeDef { +public: + explicit TypeDef(const llvm::Record *def) : def(def) {} + + // Get the dialect for which this type belongs + Dialect getDialect() const; + + // Returns the name of this TypeDef record + StringRef getName() const; + + // Returns the name of the C++ class to generate + StringRef getCppClassName() const; + + // Returns the name of the storage class for this type + StringRef getStorageClassName() const; + + // Returns the C++ namespace for this types storage class + StringRef getStorageNamespace() const; + + // Returns true if we should generate the storage class + bool genStorageClass() const; + + // I don't remember what this is for or how it'd work... + bool hasStorageCustomConstructor() const; + + // Return the list of fields for the storage class and constructors + void getMembers(SmallVectorImpl &) const; + unsigned getNumMembers() const; + + // Iterate though members, applying a map function before adding to list + template + void getMembersAs(SmallVectorImpl &members, + std::function map) const; + + // Return the keyword/mnemonic to use in the printer/parser methods if we are + // supposed to auto-generate them + llvm::Optional getMnemonic() const; + + // Returns the code to use as the types printer method. If empty, generate + // just the declaration. If null and mnemonic is non-null, generate the + // declaration and definition. + llvm::Optional getPrinterCode() const; + + // Returns the code to use as the types parser method. If empty, generate + // just the declaration. If null and mnemonic is non-null, generate the + // declaration and definition. + llvm::Optional getParserCode() const; + + // Should we generate accessors based on the types members? + bool genAccessors() const; + + // Return true if we need to generate the verifyConstructionInvariants + // declaration and getChecked method + bool genVerifyInvariantsDecl() const; + + // Returns the dialects extra class declaration code. + llvm::Optional getExtraDecls() const; + + // Returns whether two dialects are equal by checking the equality of the + // underlying record. + bool operator==(const TypeDef &other) const; + + // Compares two dialects by comparing the names of the dialects. + bool operator<(const TypeDef &other) const; + + // Returns whether the dialect is defined. + operator bool() const { return def != nullptr; } + +private: + const llvm::Record *def; +}; + +class TypeMember { +public: + explicit TypeMember(const llvm::DagInit *def, unsigned num) + : def(def), num(num) {} + + StringRef getName() const; + llvm::Optional getAllocator() const; + StringRef getCppType() const; + +private: + const llvm::DagInit *def; + const unsigned num; +}; + +template +void TypeDef::getMembersAs(SmallVectorImpl &members, + std::function map) const { + auto membersDag = def->getValueAsDag("members"); + if (membersDag != nullptr) + for (unsigned i = 0; i < membersDag->getNumArgs(); i++) + members.push_back(map(TypeMember(membersDag, i))); +} + +} // end namespace tblgen +} // end namespace mlir + +#endif // MLIR_TABLEGEN_TYPEDEF_H diff --git a/mlir/include/mlir/TableGen/TypeDefGenHelpers.h b/mlir/include/mlir/TableGen/TypeDefGenHelpers.h new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/TableGen/TypeDefGenHelpers.h @@ -0,0 +1,239 @@ +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Accessory functions / templates to assist autogenerated code. The print/parse +// struct templates define standard serializations which can be overridden with +// custom printers/parsers. These structs can be used for temporary stack +// storage also. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_TABLEGEN_PARSER_HELPERS_H +#define MLIR_TABLEGEN_PARSER_HELPERS_H + +#include "mlir/IR/Attributes.h" +#include "mlir/IR/DialectImplementation.h" +#include + +namespace mlir { +namespace tblgen { +namespace parser_helpers { + +//===----------------------------------------------------------------------===// +// +// Template enables identify various types for which we have specializations +// +//===----------------------------------------------------------------------===// + +template +using void_t = void; + +template +using remove_constref = + typename std::remove_const::type>::type; + +template +using enable_if_type = typename std::enable_if< + std::is_same, TestType>::value>::type; + +template +using is_not_type = + std::is_same, TestType>::type, + typename std::false_type::type>; + +template +using get_indexable_type = remove_constref()[0])>; + +template +using enable_if_arrayref = + enable_if_type>>; + +//===----------------------------------------------------------------------===// +// +// These structs handle Type members' parsing for common types +// +//===----------------------------------------------------------------------===// + +template +struct Parse { + ParseResult go(MLIRContext *ctxt, // The context, should it be needed + DialectAsmParser &parser, // The parser + StringRef memberName, // Type member name, for error printing + // (if necessary) + T &result); // Put the parsed value here +}; + +// Int specialization +template +using enable_if_integral_type = + typename std::enable_if::value && + is_not_type::value>::type; +template +struct Parse> { + ParseResult go(MLIRContext *ctxt, DialectAsmParser &parser, + StringRef memberName, T &result) { + return parser.parseInteger(result); + } +}; + +// Bool specialization -- 'true' / 'false' instead of 0/1 +template +struct Parse> { + ParseResult go(MLIRContext *ctxt, DialectAsmParser &parser, + StringRef memberName, bool &result) { + StringRef boolStr; + if (parser.parseKeyword(&boolStr)) + return mlir::failure(); + if (!boolStr.compare_lower("false")) { + result = false; + return mlir::success(); + } + if (!boolStr.compare_lower("true")) { + result = true; + return mlir::success(); + } + llvm::errs() << "Parser expected true/false, not '" << boolStr << "'\n"; + return mlir::failure(); + } +}; + +// Float specialization +template +using enable_if_float_type = + typename std::enable_if::value>::type; +template +struct Parse> { + ParseResult go(MLIRContext *ctxt, DialectAsmParser &parser, + StringRef memberName, T &result) { + double d; + if (parser.parseFloat(d)) + return mlir::failure(); + result = d; + return mlir::success(); + } +}; + +// mlir::Type specialization +template +using enable_if_mlir_type = + typename std::enable_if::value>::type; +template +struct Parse> { + ParseResult go(MLIRContext *ctxt, DialectAsmParser &parser, + StringRef memberName, T &result) { + Type type; + auto loc = parser.getCurrentLocation(); + if (parser.parseType(type)) + return mlir::failure(); + if ((result = type.dyn_cast_or_null()) == nullptr) { + parser.emitError(loc, "expected type '" + memberName + "'"); + return mlir::failure(); + } + return mlir::success(); + } +}; + +// StringRef specialization +template +struct Parse> { + ParseResult go(MLIRContext *ctxt, DialectAsmParser &parser, + StringRef memberName, StringRef &result) { + StringAttr a; + if (parser.parseAttribute(a)) + return mlir::failure(); + result = a.getValue(); + return mlir::success(); + } +}; + +// ArrayRef specialization +template +struct Parse> { + using inner_t = get_indexable_type; + Parse innerParser; + llvm::SmallVector members; + + ParseResult go(MLIRContext *ctxt, DialectAsmParser &parser, + StringRef memberName, ArrayRef &result) { + if (parser.parseLSquare()) + return mlir::failure(); + if (failed(parser.parseOptionalRSquare())) { + do { + inner_t member; // = std::declval(); + innerParser.go(ctxt, parser, memberName, member); + members.push_back(member); + } while (succeeded(parser.parseOptionalComma())); + if (parser.parseRSquare()) + return mlir::failure(); + } + result = ArrayRef(members); + return mlir::success(); + } +}; + +//===----------------------------------------------------------------------===// +// +// These structs handle Type members' printing for common types +// +//===----------------------------------------------------------------------===// + +template +struct Print { + static void go(DialectAsmPrinter &printer, const T &obj); +}; + +// Several C++ types can just be piped into the printer +template +using enable_if_trivial_print = + typename std::enable_if::value || + (std::is_integral::value && + is_not_type::value) || + std::is_floating_point::value>::type; +template +struct Print>> { + static void go(DialectAsmPrinter &printer, const T &obj) { printer << obj; } +}; + +// StringRef has to be quoted to match the parse specialization above +template +struct Print> { + static void go(DialectAsmPrinter &printer, const T &obj) { + printer << "\"" << obj << "\""; + } +}; + +// bool specialization +template +struct Print> { + static void go(DialectAsmPrinter &printer, const bool &obj) { + if (obj) + printer << "true"; + else + printer << "false"; + } +}; + +// ArrayRef specialization +template +struct Print> { + static void go(DialectAsmPrinter &printer, + const ArrayRef> &obj) { + printer << "["; + for (size_t i = 0; i < obj.size(); i++) { + Print>::go(printer, obj[i]); + if (i < obj.size() - 1) + printer << ", "; + } + printer << "]"; + } +}; + +} // end namespace parser_helpers +} // end namespace tblgen +} // end namespace mlir + +#endif // MLIR_TABLEGEN_PARSER_HELPERS_H diff --git a/mlir/lib/TableGen/CMakeLists.txt b/mlir/lib/TableGen/CMakeLists.txt --- a/mlir/lib/TableGen/CMakeLists.txt +++ b/mlir/lib/TableGen/CMakeLists.txt @@ -25,6 +25,7 @@ SideEffects.cpp Successor.cpp Type.cpp + TypeDef.cpp DISABLE_LLVM_LINK_LLVM_DYLIB diff --git a/mlir/lib/TableGen/TypeDef.cpp b/mlir/lib/TableGen/TypeDef.cpp new file mode 100644 --- /dev/null +++ b/mlir/lib/TableGen/TypeDef.cpp @@ -0,0 +1,139 @@ +//===- TypeDef.cpp - TypeDef wrapper class --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// TypeDef wrapper to simplify using TableGen Record defining a MLIR dialect. +// +//===----------------------------------------------------------------------===// + +#include "mlir/TableGen/TypeDef.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" + +using namespace mlir; +using namespace mlir::tblgen; + +Dialect TypeDef::getDialect() const { + return Dialect( + dyn_cast(def->getValue("owningDialect")->getValue()) + ->getDef()); +} + +StringRef TypeDef::getName() const { return def->getName(); } +StringRef TypeDef::getCppClassName() const { + return def->getValueAsString("cppClassName"); +} + +StringRef TypeDef::getStorageClassName() const { + return def->getValueAsString("storageClass"); +} +StringRef TypeDef::getStorageNamespace() const { + return def->getValueAsString("storageNamespace"); +} + +bool TypeDef::genStorageClass() const { + return def->getValueAsBit("genStorageClass"); +} +bool TypeDef::hasStorageCustomConstructor() const { + return def->getValueAsBit("hasStorageCustomConstructor"); +} +void TypeDef::getMembers(SmallVectorImpl &members) const { + auto *membersDag = def->getValueAsDag("members"); + if (membersDag != nullptr) + for (unsigned i = 0; i < membersDag->getNumArgs(); i++) + members.push_back(TypeMember(membersDag, i)); +} +unsigned TypeDef::getNumMembers() const { + auto *membersDag = def->getValueAsDag("members"); + if (membersDag == nullptr) + return 0; + return membersDag->getNumArgs(); +} +llvm::Optional TypeDef::getMnemonic() const { + const auto *code = def->getValue("mnemonic"); + if (llvm::StringInit *ci = dyn_cast(code->getValue())) + return ci->getValue(); + if (isa(code->getValue())) + return llvm::Optional(); + + llvm::PrintFatalError( + def->getLoc(), + "Record `" + def->getName() + + "', field `printer' does not have a code initializer!"); +} +llvm::Optional TypeDef::getPrinterCode() const { + const auto *code = def->getValue("printer"); + if (llvm::CodeInit *ci = dyn_cast(code->getValue())) + return ci->getValue(); + if (isa(code->getValue())) + return llvm::Optional(); + + llvm::PrintFatalError( + def->getLoc(), + "Record `" + def->getName() + + "', field `printer' does not have a code initializer!"); +} +llvm::Optional TypeDef::getParserCode() const { + return def->getValueAsString("parser"); +} +bool TypeDef::genAccessors() const { + return def->getValueAsBit("genAccessors"); +} +bool TypeDef::genVerifyInvariantsDecl() const { + return def->getValueAsBit("genVerifyInvariantsDecl"); +} + +llvm::Optional TypeDef::getExtraDecls() const { + auto value = def->getValueAsString("extraDecls"); + return value.empty() ? llvm::Optional() : value; +} + +bool TypeDef::operator==(const TypeDef &other) const { + return def == other.def; +} + +bool TypeDef::operator<(const TypeDef &other) const { + return getName() < other.getName(); +} + +StringRef TypeMember::getName() const { + return def->getArgName(num)->getValue(); +} +llvm::Optional TypeMember::getAllocator() const { + auto *memberType = def->getArg(num); + if (auto *stringType = dyn_cast(memberType)) { + return llvm::Optional(); + } else if (auto *typeMember = dyn_cast(memberType)) { + auto *code = typeMember->getDef()->getValue("allocator"); + if (llvm::CodeInit *ci = dyn_cast(code->getValue())) + return ci->getValue(); + if (isa(code->getValue())) + return llvm::Optional(); + + llvm::PrintFatalError( + typeMember->getDef()->getLoc(), + "Record `" + def->getArgName(num)->getValue() + + "', field `printer' does not have a code initializer!"); + } else { + llvm::errs() << "Members DAG arguments must be either strings or defs " + "which inherit from TypeMember\n"; + return StringRef(); + } +} +StringRef TypeMember::getCppType() const { + auto *memberType = def->getArg(num); + if (auto *stringType = dyn_cast(memberType)) { + return stringType->getValue(); + } else if (auto *typeMember = dyn_cast(memberType)) { + return typeMember->getDef()->getValueAsString("cppType"); + } else { + llvm::errs() << "Members DAG arguments must be either strings or defs " + "which inherit from TypeMember\n"; + return StringRef(); + } +} diff --git a/mlir/test/lib/Dialect/Test/CMakeLists.txt b/mlir/test/lib/Dialect/Test/CMakeLists.txt --- a/mlir/test/lib/Dialect/Test/CMakeLists.txt +++ b/mlir/test/lib/Dialect/Test/CMakeLists.txt @@ -8,6 +8,12 @@ mlir_tablegen(TestTypeInterfaces.cpp.inc -gen-type-interface-defs) add_public_tablegen_target(MLIRTestInterfaceIncGen) +set(LLVM_TARGET_DEFINITIONS TestTypeDefs.td) +mlir_tablegen(TestTypeDefs.h.inc -gen-typedef-decls) +mlir_tablegen(TestTypeDefs.cpp.inc -gen-typedef-defs) +add_public_tablegen_target(MLIRTestDefIncGen) + + set(LLVM_TARGET_DEFINITIONS TestOps.td) mlir_tablegen(TestOps.h.inc -gen-op-decls) mlir_tablegen(TestOps.cpp.inc -gen-op-defs) @@ -23,11 +29,13 @@ add_mlir_library(MLIRTestDialect TestDialect.cpp TestPatterns.cpp + TestTypes.cpp EXCLUDE_FROM_LIBMLIR DEPENDS MLIRTestInterfaceIncGen + MLIRTestDefIncGen MLIRTestOpsIncGen LINK_LIBS PUBLIC diff --git a/mlir/test/lib/Dialect/Test/TestDialect.cpp b/mlir/test/lib/Dialect/Test/TestDialect.cpp --- a/mlir/test/lib/Dialect/Test/TestDialect.cpp +++ b/mlir/test/lib/Dialect/Test/TestDialect.cpp @@ -141,16 +141,23 @@ >(); addInterfaces(); - addTypes(); + addTypes(); allowUnknownOperations(); } -static Type parseTestType(DialectAsmParser &parser, +static Type parseTestType(MLIRContext *ctxt, DialectAsmParser &parser, llvm::SetVector &stack) { StringRef typeTag; if (failed(parser.parseKeyword(&typeTag))) return Type(); + auto genType = generatedTypeParser(ctxt, parser, typeTag); + if (genType != Type()) + return genType; + if (typeTag == "test_type") return TestType::get(parser.getBuilder().getContext()); @@ -174,7 +181,7 @@ if (failed(parser.parseComma())) return Type(); stack.insert(rec); - Type subtype = parseTestType(parser, stack); + Type subtype = parseTestType(ctxt, parser, stack); stack.pop_back(); if (!subtype || failed(parser.parseGreater()) || failed(rec.setBody(subtype))) return Type(); @@ -184,11 +191,13 @@ Type TestDialect::parseType(DialectAsmParser &parser) const { llvm::SetVector stack; - return parseTestType(parser, stack); + return parseTestType(getContext(), parser, stack); } static void printTestType(Type type, DialectAsmPrinter &printer, llvm::SetVector &stack) { + if (!generatedTypePrinter(type, printer)) + return; if (type.isa()) { printer << "test_type"; return; diff --git a/mlir/test/lib/Dialect/Test/TestTypeDefs.td b/mlir/test/lib/Dialect/Test/TestTypeDefs.td new file mode 100644 --- /dev/null +++ b/mlir/test/lib/Dialect/Test/TestTypeDefs.td @@ -0,0 +1,113 @@ +//===-- TestTypeDefs.td - Test dialect type definitions ----*- tablegen -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef TEST_TYPEDEFS +#define TEST_TYPEDEFS + +// To get the test dialect def +include "TestOps.td" + +class Test_Type : TypeDef { } + +def SimpleTypeA : Test_Type<"SimpleA"> { + let mnemonic = "smpla"; +} + +// A more complex parameterized type +def CompoundTypeA : Test_Type<"CompoundA"> { + // Override the default mnemonic + let mnemonic = "cmpnd_a"; + + // What types do we contain? + let members = ( + ins + "int":$widthOfSomething, + "SimpleAType": $exampleTdType, + "float": $f, + "double": $d, + ArrayRefMember<"int">: $arrayOfInts, + ArrayRefMember<"Type">: $arrayOfTypes, + "StringRef": $simpleString, + ArrayRefMember<"StringRef">: $arrayOfStrings + ); + + let extraDecls = [{ + struct SomeCppStruct {}; + }]; +} + +def IntegerType : Test_Type<"TestInteger"> { + let mnemonic = "int"; + let genVerifyInvariantsDecl = 1; + let members = ( + ins + "TestIntegerType::SignednessSemantics":$signedness, + "unsigned":$width + ); + + let extraDecls = [{ + /// Signedness semantics. + enum SignednessSemantics { + Signless, /// No signedness semantics + Signed, /// Signed integer + Unsigned, /// Unsigned integer + }; + + /// This extra function is necessary since it doesn't include signedness + static IntegerType getChecked(unsigned width, Location location); + + /// Return true if this is a signless integer type. + bool isSignless() const { return getSignedness() == Signless; } + /// Return true if this is a signed integer type. + bool isSigned() const { return getSignedness() == Signed; } + /// Return true if this is an unsigned integer type. + bool isUnsigned() const { return getSignedness() == Unsigned; } + }]; +} + +class FieldInfo_Type : Test_Type { +let members = ( + ins + ArrayRefOfSelfAllocationMember<"FieldInfo">: $fields +); + +let printer = [{ + printer << "struct" << "<"; + for (size_t i=0; ifields.size(); i++) { + const auto& field = getImpl()->fields[i]; + printer << "{" << field.name << "," << field.type << "}"; + if (i < getImpl()->fields.size() - 1) + printer << ","; + } + printer << ">"; +}]; + +let parser = [{ + SmallVector members; + if (parser.parseLess()) return Type(); + while (mlir::succeeded(parser.parseOptionalLBrace())) { + StringRef name; + if (parser.parseKeyword(&name)) return Type(); + if (parser.parseComma()) return Type(); + Type type; + if (parser.parseType(type)) return Type(); + if (parser.parseRBrace()) return Type(); + members.push_back(FieldInfo {name, type}); + if (parser.parseOptionalComma()) break; + } + if (parser.parseGreater()) return Type(); + return get(ctxt, members); +}]; +} + +def StructType : FieldInfo_Type<"Struct"> { + let mnemonic = "struct"; +} + + +#endif // TEST_TYPEDEFS diff --git a/mlir/test/lib/Dialect/Test/TestTypes.h b/mlir/test/lib/Dialect/Test/TestTypes.h --- a/mlir/test/lib/Dialect/Test/TestTypes.h +++ b/mlir/test/lib/Dialect/Test/TestTypes.h @@ -14,11 +14,31 @@ #ifndef MLIR_TESTTYPES_H #define MLIR_TESTTYPES_H +#include + #include "mlir/IR/Diagnostics.h" +#include "mlir/IR/Dialect.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/Operation.h" #include "mlir/IR/Types.h" namespace mlir { +struct FieldInfo { +public: + StringRef name; + Type type; + + FieldInfo allocateInto(TypeStorageAllocator &alloc) const { + return FieldInfo{alloc.copyInto(name), type}; + } +}; + +bool operator==(const FieldInfo &a, const FieldInfo &b); +llvm::hash_code hash_value(const FieldInfo &fi); + +#define GET_TYPEDEF_CLASSES +#include "TestTypeDefs.h.inc" #include "TestTypeInterfaces.h.inc" /// This class is a simple test type that uses a generated interface. diff --git a/mlir/test/lib/Dialect/Test/TestTypes.cpp b/mlir/test/lib/Dialect/Test/TestTypes.cpp new file mode 100644 --- /dev/null +++ b/mlir/test/lib/Dialect/Test/TestTypes.cpp @@ -0,0 +1,93 @@ +//===- TestTypes.cpp - MLIR Test Dialect Types ----------------*- 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 types defined by the TestDialect for testing various +// features of MLIR. +// +//===----------------------------------------------------------------------===// + +#include "TestTypes.h" +#include "mlir/IR/DialectImplementation.h" +#include "mlir/IR/Types.h" +#include "mlir/TableGen/TypeDefGenHelpers.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/TypeSwitch.h" + +namespace mlir { +namespace tblgen { +namespace parser_helpers { + +// Custom parser for SignednessSemantics +template <> +struct Parse { + static ParseResult go(MLIRContext *ctxt, DialectAsmParser &parser, + StringRef memberName, + TestIntegerType::SignednessSemantics &result) { + StringRef signStr; + auto loc = parser.getCurrentLocation(); + if (parser.parseKeyword(&signStr)) + return mlir::failure(); + if (signStr.compare_lower("u") || signStr.compare_lower("unsigned")) + result = TestIntegerType::SignednessSemantics::Unsigned; + else if (signStr.compare_lower("s") || signStr.compare_lower("signed")) + result = TestIntegerType::SignednessSemantics::Signed; + else if (signStr.compare_lower("n") || signStr.compare_lower("none")) + result = TestIntegerType::SignednessSemantics::Signless; + else { + parser.emitError(loc, "expected signed, unsigned, or none"); + return mlir::failure(); + } + return mlir::success(); + } +}; + +// Custom printer for SignednessSemantics +template <> +struct Print { + static void go(DialectAsmPrinter &printer, + const TestIntegerType::SignednessSemantics &ss) { + switch (ss) { + case TestIntegerType::SignednessSemantics::Unsigned: + printer << "unsigned"; + break; + case TestIntegerType::SignednessSemantics::Signed: + printer << "signed"; + break; + case TestIntegerType::SignednessSemantics::Signless: + printer << "none"; + break; + } + } +}; + +} // namespace parser_helpers +} // namespace tblgen + +bool operator==(const FieldInfo &a, const FieldInfo &b) { + return a.name == b.name && a.type == b.type; +} + +llvm::hash_code hash_value(const FieldInfo &fi) { + return llvm::hash_combine(fi.name, fi.type); +} + +// Example type validity checker +LogicalResult TestIntegerType::verifyConstructionInvariants( + mlir::Location loc, mlir::TestIntegerType::SignednessSemantics ss, + unsigned int width) { + + if (width > 8) + return mlir::failure(); + return mlir::success(); +} + +struct TestType; +#define GET_TYPEDEF_CLASSES +#include "TestTypeDefs.cpp.inc" + +} // end namespace mlir diff --git a/mlir/test/mlir-tblgen/testdialect-typedefs.mlir b/mlir/test/mlir-tblgen/testdialect-typedefs.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-tblgen/testdialect-typedefs.mlir @@ -0,0 +1,24 @@ +// RUN: mlir-opt %s | mlir-opt -verify-diagnostics | FileCheck %s + +////////////// +// Tests the types in the 'Test' dialect, not the ones in 'typedefs.mlir' + +// CHECK: @simpleA(%arg0: !test.smpla) +func @simpleA(%A : !test.smpla) -> () { + return +} + +// CHECK: @compoundA(%arg0: !test.cmpnd_a<1, !test.smpla, 1.000000e+00, 2.200000e+00, [5, 6], [i1, i2], "example str", ["array", "of", "strings"]>) +func @compoundA(%A : !test.cmpnd_a<1, !test.smpla, 1.0, 2.2, [5, 6], [i1, i2], "example str", ["array","of","strings"]>) -> () { + return +} + +// CHECK: @testInt(%arg0: !test.int, %arg1: !test.int, %arg2: !test.int) +func @testInt(%A : !test.int, %B : !test.int, %C : !test.int) { + return +} + +// CHECK: @structTest(%arg0: !test.struct<{field1,!test.smpla},{field2,!test.int}>) +func @structTest (%A : !test.struct< {field1, !test.smpla}, {field2, !test.int} > ) { + return +} diff --git a/mlir/test/mlir-tblgen/typedefs.td b/mlir/test/mlir-tblgen/typedefs.td new file mode 100644 --- /dev/null +++ b/mlir/test/mlir-tblgen/typedefs.td @@ -0,0 +1,128 @@ +// RUN: mlir-tblgen -gen-typedef-decls -I %S/../../include %s | FileCheck %s --check-prefix=DECL +// RUN: mlir-tblgen -gen-typedef-defs -I %S/../../include %s | FileCheck %s --check-prefix=DEF + +include "mlir/IR/OpBase.td" + +// DECL: #ifdef GET_TYPEDEF_CLASSES +// DECL: #undef GET_TYPEDEF_CLASSES + +// DECL: Type generatedTypeParser(mlir::MLIRContext* ctxt, mlir::DialectAsmParser& parser, llvm::StringRef mnenomic); +// DECL: bool generatedTypePrinter(Type type, mlir::DialectAsmPrinter& printer); + +// DEF: #ifdef GET_TYPEDEF_LIST +// DEF: #undef GET_TYPEDEF_LIST +// DEF: SimpleAType, +// DEF: CompoundAType, +// DEF: IndexType, +// DEF: SingleMemberType, +// DEF: IntegerType + +// DEF-LABEL: Type generatedTypeParser(mlir::MLIRContext* ctxt, mlir::DialectAsmParser& parser, llvm::StringRef mnemonic) +// DEF: if (mnemonic == CompoundAType::getMnemonic()) return CompoundAType::parse(ctxt, parser); +// DEF return Type(); + +def Test_Dialect: Dialect { +// DECL-NOT: TestDialect +// DEF-NOT: TestDialect + let name = "TestDialect"; +} + +class TestType : TypeDef { } + +def A_SimpleTypeA : TestType<"SimpleA"> { +// DECL: class SimpleAType: public Type +// DECL: static Type parse(mlir::MLIRContext* ctxt, mlir::DialectAsmParser& parser); +// DECL: void print(mlir::DialectAsmPrinter& printer) const; +} + +// A more complex parameterized type +def B_CompoundTypeA : TestType<"CompoundA"> { + + let mnemonic = "cmpnd_a"; + let members = ( + ins + "int":$widthOfSomething, + "SimpleTypeA": $exampleTdType, + "SomeCppStruct": $exampleCppType + ); + + let genVerifyInvariantsDecl = 1; + +// DECL-LABEL: class CompoundAType: public Type +// DECL: static LogicalResult verifyConstructionInvariants(Location loc, int widthOfSomething, SimpleTypeA exampleTdType, SomeCppStruct exampleCppType); +// DECL: static CompoundAType getChecked(Location loc, int widthOfSomething, SimpleTypeA exampleTdType, SomeCppStruct exampleCppType); +// DECL: static StringRef getMnemonic() { return "cmpnd_a"; } +// DECL: int getWidthOfSomething() const; +// DECL: SimpleTypeA getExampleTdType() const; +// DECL: SomeCppStruct getExampleCppType() const; +} + +def C_IndexType : TestType<"Index"> { + let mnemonic = "index"; + +// DECL-LABEL: class IndexType: public Type +// DECL: static Type parse(mlir::MLIRContext* ctxt, mlir::DialectAsmParser& parser); +// DECL: void print(mlir::DialectAsmPrinter& printer) const; +// DECL: static StringRef getMnemonic() { return "index"; } +} + +def D_SingleMemberType : TestType<"SingleMember"> { + let members = ( + ins + "int": $num + ); +// DECL-LABEL: struct SingleMemberTypeStorage; +// DECL-LABEL: class SingleMemberType +// DECL-NEXT: detail::SingleMemberTypeStorage +// DECL: static Type parse(mlir::MLIRContext* ctxt, mlir::DialectAsmParser& parser); +// DECL: void print(mlir::DialectAsmPrinter& printer) const; +} + +def E_IntegerType : TestType<"Integer"> { + let parser = [{}]; + let printer = [{}]; + let mnemonic = "int"; + let genVerifyInvariantsDecl = 1; + let members = ( + ins + "SignednessSemantics":$signedness, + "unsigned":$width + ); + +// DECL-LABEL: IntegerType: public Type + + let extraDecls = [{ + /// Signedness semantics. + enum SignednessSemantics { + Signless, /// No signedness semantics + Signed, /// Signed integer + Unsigned, /// Unsigned integer + }; + + /// This extra function is necessary since it doesn't include signedness + static IntegerType getChecked(unsigned width, Location location); + + /// Return true if this is a signless integer type. + bool isSignless() const { return getSignedness() == Signless; } + /// Return true if this is a signed integer type. + bool isSigned() const { return getSignedness() == Signed; } + /// Return true if this is an unsigned integer type. + bool isUnsigned() const { return getSignedness() == Unsigned; } + }]; + +// DECL: /// Signedness semantics. +// DECL-NEXT: enum SignednessSemantics { +// DECL-NEXT: Signless, /// No signedness semantics +// DECL-NEXT: Signed, /// Signed integer +// DECL-NEXT: Unsigned, /// Unsigned integer +// DECL-NEXT: }; +// DECL: /// This extra function is necessary since it doesn't include signedness +// DECL-NEXT: static IntegerType getChecked(unsigned width, Location location); + +// DECL: /// Return true if this is a signless integer type. +// DECL-NEXT: bool isSignless() const { return getSignedness() == Signless; } +// DECL-NEXT: /// Return true if this is a signed integer type. +// DECL-NEXT: bool isSigned() const { return getSignedness() == Signed; } +// DECL-NEXT: /// Return true if this is an unsigned integer type. +// DECL-NEXT: bool isUnsigned() const { return getSignedness() == Unsigned; } +} diff --git a/mlir/tools/mlir-tblgen/CMakeLists.txt b/mlir/tools/mlir-tblgen/CMakeLists.txt --- a/mlir/tools/mlir-tblgen/CMakeLists.txt +++ b/mlir/tools/mlir-tblgen/CMakeLists.txt @@ -20,6 +20,7 @@ RewriterGen.cpp SPIRVUtilsGen.cpp StructsGen.cpp + TypeDefGen.cpp ) set_target_properties(mlir-tblgen PROPERTIES FOLDER "Tablegenning") diff --git a/mlir/tools/mlir-tblgen/GenUtilities.h b/mlir/tools/mlir-tblgen/GenUtilities.h new file mode 100644 --- /dev/null +++ b/mlir/tools/mlir-tblgen/GenUtilities.h @@ -0,0 +1,40 @@ +//===- GenUtilities.h - MLIR doc gen utilities ---------------*- 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 common utilities for generating C++ from tablegen +// structures. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_TOOLS_MLIRTBLGEN_GENUTILITIES_H_ +#define MLIR_TOOLS_MLIRTBLGEN_GENUTILITIES_H_ + +#include "llvm/ADT/StringExtras.h" + +namespace mlir { +namespace tblgen { + +// Simple RAII helper for defining ifdef-undef-endif scopes. +class IfDefScope { +public: + inline IfDefScope(llvm::StringRef name, llvm::raw_ostream &os) + : name(name), os(os) { + os << "#ifdef " << name << "\n" + << "#undef " << name << "\n\n"; + } + inline ~IfDefScope() { os << "\n#endif // " << name << "\n\n"; } + +private: + llvm::StringRef name; + llvm::raw_ostream &os; +}; + +} // namespace tblgen +} // namespace mlir + +#endif // MLIR_TOOLS_MLIRTBLGEN_GENUTILITIES_H_ diff --git a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp --- a/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp +++ b/mlir/tools/mlir-tblgen/OpDefinitionsGen.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "GenUtilities.h" #include "OpFormatGen.h" #include "mlir/TableGen/Format.h" #include "mlir/TableGen/GenInfo.h" @@ -158,23 +159,6 @@ // Op emitter //===----------------------------------------------------------------------===// -namespace { -// Simple RAII helper for defining ifdef-undef-endif scopes. -class IfDefScope { -public: - IfDefScope(StringRef name, raw_ostream &os) : name(name), os(os) { - os << "#ifdef " << name << "\n" - << "#undef " << name << "\n\n"; - } - - ~IfDefScope() { os << "\n#endif // " << name << "\n\n"; } - -private: - StringRef name; - raw_ostream &os; -}; -} // end anonymous namespace - namespace { // Helper class to emit a record into the given output stream. class OpEmitter { diff --git a/mlir/tools/mlir-tblgen/TypeDefGen.cpp b/mlir/tools/mlir-tblgen/TypeDefGen.cpp new file mode 100644 --- /dev/null +++ b/mlir/tools/mlir-tblgen/TypeDefGen.cpp @@ -0,0 +1,596 @@ +//===- TypeDefGen.cpp - MLIR typeDef definitions generator ----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// TypeDefGen uses the description of typeDefs to generate C++ definitions. +// +//===----------------------------------------------------------------------===// + +#include "GenUtilities.h" +#include "mlir/Support/LogicalResult.h" +#include "mlir/TableGen/Format.h" +#include "mlir/TableGen/GenInfo.h" +#include "mlir/TableGen/Interfaces.h" +#include "mlir/TableGen/OpClass.h" +#include "mlir/TableGen/OpTrait.h" +#include "mlir/TableGen/Operator.h" +#include "mlir/TableGen/TypeDef.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Signals.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" + +#define DEBUG_TYPE "mlir-tblgen-typedefgen" + +using namespace mlir; +using namespace mlir::tblgen; + +static llvm::cl::OptionCategory typedefGenCat("Options for -gen-typedef-*"); +static llvm::cl::opt + selectedDialect("typedefs-dialect", + llvm::cl::desc("Gen types for this dialect"), + llvm::cl::cat(typedefGenCat), llvm::cl::CommaSeparated); + +/// Find all the TypeDefs for the specified dialect. If no dialect specified and +/// can only find one dialect's types, use that. +static mlir::LogicalResult +findAllTypeDefs(const llvm::RecordKeeper &recordKeeper, + SmallVectorImpl &typeDefs) { + auto recDefs = recordKeeper.getAllDerivedDefinitions("TypeDef"); + auto defs = llvm::map_range( + recDefs, [&](const llvm::Record *rec) { return TypeDef(rec); }); + if (defs.empty()) + return mlir::success(); + + StringRef dialectName; + if (selectedDialect.getNumOccurrences() == 0) { + if (defs.empty()) + return mlir::success(); + + llvm::SmallSet dialects; + for (auto typeDef : defs) { + dialects.insert(typeDef.getDialect()); + } + if (dialects.size() != 1) { + llvm::errs() << "TypeDefs belonging to more than one dialect. Must " + "select one via '--typedefs-dialect'\n"; + return mlir::failure(); + } + + dialectName = (*dialects.begin()).getName(); + } else if (selectedDialect.getNumOccurrences() == 1) { + dialectName = selectedDialect.getValue(); + } else { + llvm::errs() + << "cannot select multiple dialects for which to generate types" + "via '--typedefs-dialect'\n"; + return mlir::failure(); + } + + for (auto typeDef : defs) { + if (typeDef.getDialect().getName().equals(dialectName)) + typeDefs.push_back(typeDef); + } + return mlir::success(); +} + +/// Create a string list of members and types for function decls +/// String construction helper function: member1Type member1Name, member2Type +/// member2Name +static std::string constructMemberParameters(TypeDef &typeDef, + bool prependComma) { + SmallVector members; + if (prependComma) + members.push_back(""); + typeDef.getMembersAs(members, [](auto member) { + return (member.getCppType() + " " + member.getName()).str(); + }); + if (members.size() > 0) + return llvm::join(members, ", "); + return ""; +} + +/// Create an initializer for the storage class +/// String construction helper function: member1(member1), member2(member2), +/// [...] +static std::string constructMembersInitializers(TypeDef &typeDef) { + SmallVector members; + typeDef.getMembersAs(members, [](auto member) { + return (member.getName() + "(" + member.getName() + ")").str(); + }); + return llvm::join(members, ", "); +} + +//===----------------------------------------------------------------------===// +// GEN: TypeDef declarations +//===----------------------------------------------------------------------===// + +/// The code block for the start of a typeDef class declaration -- singleton +/// case +/// +/// {0}: The name of the typeDef class. +static const char *const typeDefDeclSingletonBeginStr = R"( + class {0}: public Type::TypeBase<{0}, Type, TypeStorage> {{ +public: + /// Inherit some necessary constructors from 'TypeBase'. + using Base::Base; + +)"; + +/// The code block for the start of a typeDef class declaration -- parametric +/// case +/// +/// {0}: The name of the typeDef class. +/// {1}: The typeDef storage class namespace. +/// {2}: The storage class name +static const char *const typeDefDeclParametricBeginStr = R"( + namespace {1} { + struct {2}; + } + class {0}: public Type::TypeBase<{0}, Type, + {1}::{2}> {{ +public: + /// Inherit some necessary constructors from 'TypeBase'. + using Base::Base; +)"; + +/// {0}: The name of the typeDef class. +/// {1}: The list of members as a list of arguments +static const char *const typeDefParsePrint = R"( + + static Type parse(mlir::MLIRContext* ctxt, mlir::DialectAsmParser& parser); + void print(mlir::DialectAsmPrinter& printer) const; + +)"; + +/// The code block for the verifyConstructionInvariants and getChecked +/// +/// {0}: List of members, parameters style +/// {1}: C++ type class name +static const char *const typeDefDeclVerifyStr = R"( + static LogicalResult verifyConstructionInvariants(Location loc{0}); + static {1} getChecked(Location loc{0}); +)"; + +/// Generate the declaration for the given typeDef class. +static void emitTypeDefDecl(TypeDef &typeDef, raw_ostream &os) { + // Emit the beginning string template: either the singleton or parametric + // template + if (typeDef.getNumMembers() == 0) + os << llvm::formatv(typeDefDeclSingletonBeginStr, typeDef.getCppClassName(), + typeDef.getStorageNamespace(), + typeDef.getStorageClassName()); + else + os << llvm::formatv( + typeDefDeclParametricBeginStr, typeDef.getCppClassName(), + typeDef.getStorageNamespace(), typeDef.getStorageClassName()); + + // Emit the extra declarations first in case there's a type definition in + // there + if (llvm::Optional extraDecl = typeDef.getExtraDecls()) + os << *extraDecl; + + std::string memberParameters = constructMemberParameters(typeDef, true); + + // parse/print + os << llvm::formatv(typeDefParsePrint, typeDef.getCppClassName(), + memberParameters); + + // verify invariants + if (typeDef.genVerifyInvariantsDecl()) + os << llvm::formatv(typeDefDeclVerifyStr, memberParameters, + typeDef.getCppClassName()); + + // mnenomic, if specified + if (auto mnenomic = typeDef.getMnemonic()) { + os << " static StringRef getMnemonic() { return \"" << mnenomic + << "\"; }\n"; + } + + if (typeDef.genAccessors()) { + SmallVector members; + typeDef.getMembers(members); + + for (auto member : members) { + SmallString<16> name = member.getName(); + name[0] = llvm::toUpper(name[0]); + os << llvm::formatv(" {0} get{1}() const;\n", member.getCppType(), + name); + } + } + + // End the typeDef decl. + os << " };\n"; +} + +/// Main entry point for decls +static bool emitTypeDefDecls(const llvm::RecordKeeper &recordKeeper, + raw_ostream &os) { + emitSourceFileHeader("TypeDef Declarations", os); + + SmallVector typeDefs; + if (mlir::failed(findAllTypeDefs(recordKeeper, typeDefs))) + return true; + + IfDefScope scope("GET_TYPEDEF_CLASSES", os); + + // well known print/parse dispatch function declarations + os << " Type generatedTypeParser(mlir::MLIRContext* ctxt, " + "mlir::DialectAsmParser& parser, llvm::StringRef mnenomic);\n"; + os << " bool generatedTypePrinter(Type type, mlir::DialectAsmPrinter& " + "printer);\n"; + os << "\n"; + + // declare all the type classes first (in case they reference each other) + for (auto typeDef : typeDefs) { + os << " class " << typeDef.getCppClassName() << ";\n"; + } + + // declare all the typedefs + for (auto typeDef : typeDefs) { + emitTypeDefDecl(typeDef, os); + } + + return false; +} + +//===----------------------------------------------------------------------===// +// GEN: TypeDef list +//===----------------------------------------------------------------------===// + +static mlir::LogicalResult emitTypeDefList(SmallVectorImpl &typeDefs, + raw_ostream &os) { + IfDefScope scope("GET_TYPEDEF_LIST", os); + for (auto *i = typeDefs.begin(); i != typeDefs.end(); i++) { + os << " " << i->getCppClassName(); + if (i < typeDefs.end() - 1) + os << ",\n"; + else + os << "\n"; + } + return mlir::success(); +} + +//===----------------------------------------------------------------------===// +// GEN: TypeDef definitions +//===----------------------------------------------------------------------===// + +/// Beginning of storage class +/// {0}: Storage class namespace +/// {1}: Storage class c++ name +/// {2}: Members parameters +/// {3}: Member initialzer string +/// {4}: Member name list; +/// {5}: Member types +static const char *const typeDefStorageClassBegin = R"( +namespace {0} {{ + struct {1} : public TypeStorage {{ + {1} ({2}) + : {3} {{ } + + /// The hash key for this storage is a pair of the integer and type params. + using KeyTy = std::tuple<{5}>; + + /// Define the comparison function for the key type. + bool operator==(const KeyTy &key) const {{ + return key == KeyTy({4}); + } + + static llvm::hash_code hashKey(const KeyTy &key) {{ +)"; + +/// The storage class' constructor template +/// {0}: storage class name +static const char *const typeDefStorageClassConstructorBegin = R"( + /// Define a construction method for creating a new instance of this storage. + static {0} *construct(TypeStorageAllocator &allocator, + const KeyTy &key) {{ +)"; + +/// The storage class' constructor return template +/// {0}: storage class name +/// {1}: list of members +static const char *const typeDefStorageClassConstructorReturn = R"( + return new (allocator.allocate<{0}>()) + {0}({1}); + } +)"; + +/// use tgfmt to emit custom allocation code for each member, if necessary +static mlir::LogicalResult emitCustomAllocationCode(TypeDef &typeDef, + raw_ostream &os) { + SmallVector members; + typeDef.getMembers(members); + auto fmtCtxt = FmtContext().addSubst("_allocator", "allocator"); + for (auto member : members) { + auto allocCode = member.getAllocator(); + if (allocCode) { + fmtCtxt.withSelf(member.getName()); + fmtCtxt.addSubst("_dst", member.getName()); + auto fmtObj = tgfmt(*allocCode, &fmtCtxt); + os << " "; + fmtObj.format(os); + os << "\n"; + } + } + return mlir::success(); +} + +static mlir::LogicalResult emitStorageClass(TypeDef typeDef, raw_ostream &os) { + SmallVector members; + typeDef.getMembers(members); + + // Initialize a bunch of variables to be used later on + auto memberNames = llvm::map_range( + members, [](TypeMember member) { return member.getName(); }); + auto memberTypes = llvm::map_range( + members, [](TypeMember member) { return member.getCppType(); }); + auto memberList = llvm::join(memberNames, ", "); + auto memberTypeList = llvm::join(memberTypes, ", "); + auto memberParameters = constructMemberParameters(typeDef, false); + auto memberInits = constructMembersInitializers(typeDef); + + // emit most of the storage class up until the hashKey body + os << llvm::formatv(typeDefStorageClassBegin, typeDef.getStorageNamespace(), + typeDef.getStorageClassName(), memberParameters, + memberInits, memberList, memberTypeList); + + // extract each member from the key (auto unboxing is a c++17 feature) + for (size_t i = 0; i < members.size(); i++) { + os << llvm::formatv(" auto {0} = std::get<{1}>(key);\n", + members[i].getName(), i); + } + // then combine them all. this requires all the members types to have a + // hash_value defined + os << " return llvm::hash_combine(\n"; + for (auto *memberIter = members.begin(); memberIter < members.end(); + memberIter++) { + os << " " << memberIter->getName(); + if (memberIter < members.end() - 1) { + os << ",\n"; + } + } + os << ");\n"; + os << " }\n"; + + // if user wants to build the storage constructor themselves, declare it here + // and then they can write the definition elsewhere + if (typeDef.hasStorageCustomConstructor()) + os << " static " << typeDef.getStorageClassName() + << " *construct(TypeStorageAllocator &allocator, const KeyTy &key);\n"; + else { + os << llvm::formatv(typeDefStorageClassConstructorBegin, + typeDef.getStorageClassName()); + // I want C++17's unboxing!!! + for (size_t i = 0; i < members.size(); i++) { + os << llvm::formatv(" auto {0} = std::get<{1}>(key);\n", + members[i].getName(), i); + } + // Reassign the member variables with allocation code, if it's specified + if (mlir::failed(emitCustomAllocationCode(typeDef, os))) + return mlir::failure(); + // return an allocated copy + os << llvm::formatv(typeDefStorageClassConstructorReturn, + typeDef.getStorageClassName(), memberList); + } + + // Emit the members' class members + for (auto member : members) { + os << " " << member.getCppType() << " " << member.getName() << ";\n"; + } + os << " };\n"; + os << "};\n"; + + return mlir::success(); +} + +/// Emit the body of an autogenerated printer +static mlir::LogicalResult emitPrinterAutogen(TypeDef typeDef, + raw_ostream &os) { + if (auto mnemonic = typeDef.getMnemonic()) { + SmallVector members; + typeDef.getMembers(members); + + os << " printer << \"" << *mnemonic << "\";\n"; + + // if non-parametric, we're done + if (members.size() > 0) { + os << " printer << \"<\";\n"; + + // emit a printer for each member separated by ','. + // printer structs for common C++ types are defined in + // TypeDefGenHelpers.h, which must be #included by the consuming code. + for (auto *memberIter = members.begin(); memberIter < members.end(); + memberIter++) { + // Each printer struct must be put on the stack then 'go' called + os << " ::mlir::tblgen::parser_helpers::Print<" + << memberIter->getCppType() << ">::go(printer, getImpl()->" + << memberIter->getName() << ");\n"; + + // emit the comma unless we're the last member + if (memberIter < members.end() - 1) { + os << " printer << \", \";\n"; + } + } + os << " printer << \">\";\n"; + } + } + return mlir::success(); +} + +/// Emit the body of an autogenerated parser +static mlir::LogicalResult emitParserAutogen(TypeDef typeDef, raw_ostream &os) { + SmallVector members; + typeDef.getMembers(members); + + // by the time we get to this function, the mnenomic has already been parsed + if (members.size() > 0) { + os << " if (parser.parseLess()) return Type();\n"; + + // emit a parser for each member separated by ','. + // parse structs for common C++ types are defined in + // TypeDefGenHelpers.h, which must be #included by the consuming code. + for (auto *memberIter = members.begin(); memberIter < members.end(); + memberIter++) { + os << " " << memberIter->getCppType() << " " << memberIter->getName() + << ";\n"; + os << llvm::formatv( + " ::mlir::tblgen::parser_helpers::Parse<{0}> {1}Parser;\n", + memberIter->getCppType(), memberIter->getName()); + os << llvm::formatv( + " if ({0}Parser.go(ctxt, parser, \"{1}\", {0})) return Type();\n", + memberIter->getName(), memberIter->getCppType()); + + // parse a comma unless we're the last member + if (memberIter < members.end() - 1) { + os << " if (parser.parseComma()) return Type();\n"; + } + } + os << " if (parser.parseGreater()) return Type();\n"; + // done with the parsing + + // all the parameters are now in variables named the same as the members + auto memberNames = llvm::map_range( + members, [](TypeMember member) { return member.getName(); }); + os << " return get(ctxt, " << llvm::join(memberNames, ", ") << ");\n"; + } else { + os << " return get(ctxt);\n"; + } + return mlir::success(); +} + +/// Print all the typedef-specific definition code +static mlir::LogicalResult emitTypeDefDef(TypeDef typeDef, raw_ostream &os) { + SmallVector members; + typeDef.getMembers(members); + + // emit the storage class, if requested and necessary + if (typeDef.genStorageClass() && typeDef.getNumMembers() > 0) + if (mlir::failed(emitStorageClass(typeDef, os))) + return mlir::failure(); + + // emit the accessors + if (typeDef.genAccessors()) { + for (auto member : members) { + SmallString<16> name = member.getName(); + name[0] = llvm::toUpper(name[0]); + os << llvm::formatv( + "{0} {3}::get{1}() const { return getImpl()->{2}; }\n", + member.getCppType(), name, member.getName(), + typeDef.getCppClassName()); + } + } + + // emit the printer code, if appropriate + auto printerCode = typeDef.getPrinterCode(); + if (printerCode && typeDef.getMnemonic()) { + // Both the mnenomic and printerCode must be defined (for parity with + // parserCode) + + os << "void " << typeDef.getCppClassName() + << "::print(mlir::DialectAsmPrinter& printer) const {\n"; + if (*printerCode == "") { + // if no code specified, autogenerate a parser + if (mlir::failed(emitPrinterAutogen(typeDef, os))) + return mlir::failure(); + } else { + os << *printerCode << "\n"; + } + os << "}\n"; + } + + // emit a parser, if appropriate + auto parserCode = typeDef.getParserCode(); + if (parserCode && typeDef.getMnemonic()) { + // The mnenomic must be defined so the dispatcher knows how to dispatch + os << "Type " << typeDef.getCppClassName() + << "::parse(mlir::MLIRContext* ctxt, mlir::DialectAsmParser& parser) " + "{\n"; + if (*parserCode == "") { + if (mlir::failed(emitParserAutogen(typeDef, os))) + return mlir::failure(); + } else + os << *parserCode << "\n"; + os << "}\n"; + } + + return mlir::success(); +} + +/// Emit the dialect printer/parser dispatch. Client code should call these +/// functions from their dialect's print/parse methods. +static mlir::LogicalResult +emitParsePrintDispatch(SmallVectorImpl &typeDefs, raw_ostream &os) { + os << "Type generatedTypeParser(mlir::MLIRContext* ctxt, " + "mlir::DialectAsmParser& parser, llvm::StringRef mnemonic) {\n"; + for (auto typeDef : typeDefs) { + if (typeDef.getMnemonic()) + os << llvm::formatv(" if (mnemonic == {0}::getMnemonic()) return " + "{0}::parse(ctxt, parser);\n", + typeDef.getCppClassName()); + } + os << " return Type();\n"; + os << "}\n\n"; + + os << "bool generatedTypePrinter(Type type, mlir::DialectAsmPrinter& " + "printer) {\n" + << " bool notfound = false;\n" + << " TypeSwitch(type)\n"; + for (auto typeDef : typeDefs) { + if (typeDef.getMnemonic()) + os << llvm::formatv(" .Case<{0}>([&](Type t) {{ " + "t.dyn_cast<{0}>().print(printer); })\n", + typeDef.getCppClassName()); + } + os << " .Default([¬found](Type) { notfound = true; });\n" + << " return notfound;\n" + << "}\n\n"; + return mlir::success(); +} + +/// Entry point for typedef definitions +static bool emitTypeDefDefs(const llvm::RecordKeeper &recordKeeper, + raw_ostream &os) { + emitSourceFileHeader("TypeDef Definitions", os); + + SmallVector typeDefs; + if (mlir::failed(findAllTypeDefs(recordKeeper, typeDefs))) + return true; + + if (mlir::failed(emitTypeDefList(typeDefs, os))) + return true; + + IfDefScope scope("GET_TYPEDEF_CLASSES", os); + if (mlir::failed(emitParsePrintDispatch(typeDefs, os))) + return true; + for (auto typeDef : typeDefs) { + if (mlir::failed(emitTypeDefDef(typeDef, os))) + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// GEN: TypeDef registration hooks +//===----------------------------------------------------------------------===// + +static mlir::GenRegistration + genTypeDefDefs("gen-typedef-defs", "Generate TypeDef definitions", + [](const llvm::RecordKeeper &records, raw_ostream &os) { + return emitTypeDefDefs(records, os); + }); + +static mlir::GenRegistration + genTypeDefDecls("gen-typedef-decls", "Generate TypeDef declarations", + [](const llvm::RecordKeeper &records, raw_ostream &os) { + return emitTypeDefDecls(records, os); + });