diff --git a/llvm/utils/TableGen/X86FoldTablesEmitter.cpp b/llvm/utils/TableGen/X86FoldTablesEmitter.cpp index 807b787e29428d..748f124ff557dc 100644 --- a/llvm/utils/TableGen/X86FoldTablesEmitter.cpp +++ b/llvm/utils/TableGen/X86FoldTablesEmitter.cpp @@ -127,6 +127,15 @@ class X86FoldTablesEmitter { OS << "0 },\n"; } + + bool operator<(const X86FoldTableEntry &RHS) const { + bool LHSpseudo = RegInst->TheDef->getValueAsBit("isPseudo"); + bool RHSpseudo = RHS.RegInst->TheDef->getValueAsBit("isPseudo"); + if (LHSpseudo != RHSpseudo) + return LHSpseudo; + + return RegInst->TheDef->getName() < RHS.RegInst->TheDef->getName(); + } }; typedef std::vector FoldTable; @@ -647,6 +656,14 @@ void X86FoldTablesEmitter::run(formatted_raw_ostream &OS) { &(Target.getInstruction(MemInstIter)), Entry.Strategy); } + // Sort the tables before printing. + llvm::sort(Table2Addr); + llvm::sort(Table0); + llvm::sort(Table1); + llvm::sort(Table2); + llvm::sort(Table3); + llvm::sort(Table4); + // Print all tables. printTable(Table2Addr, "Table2Addr", OS); printTable(Table0, "Table0", OS); diff --git a/mlir/docs/OpDefinitions.md b/mlir/docs/OpDefinitions.md index 7c5058e04a9bd2..96c257cef08cf3 100644 --- a/mlir/docs/OpDefinitions.md +++ b/mlir/docs/OpDefinitions.md @@ -1,11 +1,11 @@ # Operation Definition Specification (ODS) In addition to specializing the `mlir::Op` C++ template, MLIR also supports -defining operations in a table-driven manner. This is achieved via -[TableGen][TableGen], which is both a generic language and its tooling to +defining operations and data types in a table-driven manner. This is achieved +via [TableGen][TableGen], which is both a generic language and its tooling to maintain records of domain-specific information. Facts regarding an operation -are specified concisely into a TableGen record, which will be expanded into an -equivalent `mlir::Op` C++ template specialization at compiler build time. +are specified concisely into a TableGen record, which will be expanded into +an equivalent `mlir::Op` C++ template specialization at compiler build time. This manual explains in detail all the available mechanisms for defining operations in such a table-driven manner. It aims to be a specification instead @@ -1412,6 +1412,173 @@ llvm::Optional symbolizeMyBitEnum(uint32_t value) { } ``` +## Type Definitions + +MLIR defines the TypeDef class hierarchy to enable generation of data types +from their specifications. A type is defined by specializing the TypeDef +class with concrete contents for all the fields it requires. For example, an +integer type could be defined as: + +```tablegen +// All of the types will extend this class. +class Test_Type : TypeDef { } + +// An alternate int type. +def IntegerType : Test_Type<"TestInteger"> { + let mnemonic = "int"; + + let summary = "An integer type with special semantics"; + + let description = [{ + An alternate integer type. This type differentiates itself from the + standard integer type by not having a SignednessSemantics parameter, just + a width. + }]; + + let parameters = (ins "unsigned":$width); + + // We define the printer inline. + let printer = [{ + $_printer << "int<" << getImpl()->width << ">"; + }]; + + // The parser is defined here also. + let parser = [{ + if (parser.parseLess()) + return Type(); + int width; + if ($_parser.parseInteger(width)) + return Type(); + if ($_parser.parseGreater()) + return Type(); + return get(ctxt, width); + }]; +``` + +### Type name + +The name of the C++ class which gets generated defaults to +`Type` (e.g. `TestIntegerType` in the above example). This +can be overridden via the the `cppClassName` field. The field `mnemonic` is +to specify the asm name for parsing. It is optional and not specifying it +will imply that no parser or printer methods are attached to this class. + +### Type documentation + +The `summary` and `description` fields exist and are to be used the same way +as in Operations. Namely, the summary should be a one-liner and `description` +should be a longer explanation. + +### Type parameters + +The `parameters` field is a list of the types parameters. If no parameters +are specified (the default), this type is considered a singleton type. +Parameters are in the `"c++Type":$paramName` format. +To use C++ types as parameters which need allocation in the storage +constructor, there are two options: + +- Set `hasCustomStorageConstructor` to generate the TypeStorage class with +a constructor which is just declared -- no definition -- so you can write it +yourself. +- Use the `TypeParameter` tablegen class instead of the "c++Type" string. + +### TypeParameter tablegen class + +This is used to further specify attributes about each of the types +parameters. It includes documentation (`description` and `syntax`), the C++ +type to use, and a custom allocator to use in the storage constructor method. + +```tablegen +// DO NOT DO THIS! +let parameters = (ins + "ArrayRef":$dims); +``` + +The default storage constructor blindly copies fields by value. It does not +know anything about the types. In this case, the ArrayRef requires +allocation with `dims = allocator.copyInto(dims)`. + +You can specify the necessary constuctor by specializing the `TypeParameter` +tblgen class: + +```tablegen +class ArrayRefIntParam : + TypeParameter<"::llvm::ArrayRef", "Array of ints"> { + let allocator = [{$_dst = $_allocator.copyInto($_self);}]; +} + +... + +let parameters = (ins + ArrayRefIntParam:$dims); +``` + +The `allocator` code block has the following substitutions: +- `$_allocator` is the TypeStorageAllocator in which to allocate objects. +- `$_dst` is the variable in which to place the allocated data. + +MLIR includes several specialized classes for common situations: +- `StringRefParameter` for StringRefs. +- `ArrayRefParameter` for ArrayRefs of value +types +- `SelfAllocationParameter` for C++ classes which contain +a method called `allocateInto(StorageAllocator &allocator)` to allocate +itself into `allocator`. +- `ArrayRefOfSelfAllocationParameter` for arrays +of objects which self-allocate as per the last specialization. + +If we were to use one of these included specializations: + +```tablegen +let parameters = (ins + ArrayRefParameter<"int", "The dimensions">:$dims +); +``` + +### Parsing and printing + +If a mnemonic is specified, the `printer` and `parser` code fields are active. +The rules for both are: +- If null, generate just the declaration. +- If non-null and non-empty, use the code in the definition. The `$_printer` +or `$_parser` substitutions are valid and should be used. +- It is an error to have an empty code block. + +For each dialect, two "dispatch" functions will be created: one for parsing +and one for printing. You should add calls to these in your +`Dialect::printType` and `Dialect::parseType` methods. They are created in +the dialect's namespace and their function signatures are: +```c++ +Type generatedTypeParser(MLIRContext* ctxt, DialectAsmParser& parser, + StringRef mnemonic); +LogicalResult generatedTypePrinter(Type type, DialectAsmPrinter& printer); +``` + +The mnemonic, parser, and printer fields are optional. If they're not +defined, the generated code will not include any parsing or printing code and +omit the type from the dispatch functions above. In this case, the dialect +author is responsible for parsing/printing the types in `Dialect::printType` +and `Dialect::parseType`. + +### Other fields + +- If the `genStorageClass` field is set to 1 (the default) a storage class is +generated with member variables corresponding to each of the specified +`parameters`. +- If the `genAccessors` field is 1 (the default) accessor methods will be +generated on the Type class (e.g. `int getWidth() const` in the example +above). +- If the `genVerifyInvariantsDecl` field is set, a declaration for a method +`static LogicalResult verifyConstructionInvariants(Location, parameters...)` +is added to the class as well as a `getChecked(Location, parameters...)` +method which gets the result of `verifyConstructionInvariants` before calling +`get`. +- The `storageClass` field can be used to set the name of the storage class. +- The `storageNamespace` field is used to set the namespace where the storage +class should sit. Defaults to "detail". +- The `extraClassDeclaration` field is used to include extra code in the +class declaration. + ## Debugging Tips ### Run `mlir-tblgen` to see the generated content diff --git a/mlir/test/lib/Dialect/Test/TestTypeDefs.td b/mlir/test/lib/Dialect/Test/TestTypeDefs.td index cfab6985e87af7..d755d91bb5d649 100644 --- a/mlir/test/lib/Dialect/Test/TestTypeDefs.td +++ b/mlir/test/lib/Dialect/Test/TestTypeDefs.td @@ -75,7 +75,8 @@ def IntegerType : Test_Type<"TestInteger"> { int width; if ($_parser.parseInteger(width)) return Type(); if ($_parser.parseGreater()) return Type(); - return get(ctxt, signedness, width); + Location loc = $_parser.getEncodedSourceLoc($_parser.getNameLoc()); + return getChecked(loc, signedness, width); }]; // Any extra code one wants in the type's class declaration. diff --git a/mlir/test/mlir-tblgen/typedefs.td b/mlir/test/mlir-tblgen/typedefs.td index 5ba3fbcc71157e..4fc7f3282bd778 100644 --- a/mlir/test/mlir-tblgen/typedefs.td +++ b/mlir/test/mlir-tblgen/typedefs.td @@ -51,7 +51,7 @@ def B_CompoundTypeA : TestType<"CompoundA"> { // DECL-LABEL: class CompoundAType: public ::mlir::Type // DECL: static ::mlir::LogicalResult verifyConstructionInvariants(Location loc, int widthOfSomething, ::mlir::test::SimpleTypeA exampleTdType, SomeCppStruct exampleCppType, ::llvm::ArrayRef dims); -// DECL: static CompoundAType getChecked(Location loc, int widthOfSomething, ::mlir::test::SimpleTypeA exampleTdType, SomeCppStruct exampleCppType, ::llvm::ArrayRef dims); +// DECL: static ::mlir::Type getChecked(Location loc, int widthOfSomething, ::mlir::test::SimpleTypeA exampleTdType, SomeCppStruct exampleCppType, ::llvm::ArrayRef dims); // DECL: static ::llvm::StringRef getMnemonic() { return "cmpnd_a"; } // DECL: static ::mlir::Type parse(::mlir::MLIRContext* ctxt, ::mlir::DialectAsmParser& parser); // DECL: void print(::mlir::DialectAsmPrinter& printer) const; diff --git a/mlir/tools/mlir-tblgen/TypeDefGen.cpp b/mlir/tools/mlir-tblgen/TypeDefGen.cpp index 6bf6e04ef2cf97..4473f629f3f141 100644 --- a/mlir/tools/mlir-tblgen/TypeDefGen.cpp +++ b/mlir/tools/mlir-tblgen/TypeDefGen.cpp @@ -78,25 +78,25 @@ class TypeParamCommaFormatter : public llvm::detail::format_adapter { /// [...]". TypeNamePairs, - /// Emit ", parameter1Type parameter1Name, parameter2Type parameter2Name, - /// [...]". - TypeNamePairsPrependComma, - /// Emit "parameter1(parameter1), parameter2(parameter2), [...]". - TypeNameInitializer + TypeNameInitializer, + + /// Emit "param1Name, param2Name, [...]". + JustParams, }; - TypeParamCommaFormatter(EmitFormat emitFormat, ArrayRef params) - : emitFormat(emitFormat), params(params) {} + TypeParamCommaFormatter(EmitFormat emitFormat, ArrayRef params, + bool prependComma = true) + : emitFormat(emitFormat), params(params), prependComma(prependComma) {} /// llvm::formatv will call this function when using an instance as a /// replacement value. void format(raw_ostream &os, StringRef options) override { - if (params.size() && emitFormat == EmitFormat::TypeNamePairsPrependComma) + if (params.size() && prependComma) os << ", "; + switch (emitFormat) { case EmitFormat::TypeNamePairs: - case EmitFormat::TypeNamePairsPrependComma: interleaveComma(params, os, [&](const TypeParameter &p) { emitTypeNamePair(p, os); }); break; @@ -105,6 +105,10 @@ class TypeParamCommaFormatter : public llvm::detail::format_adapter { emitTypeNameInitializer(p, os); }); break; + case EmitFormat::JustParams: + interleaveComma(params, os, + [&](const TypeParameter &p) { os << p.getName(); }); + break; } } @@ -120,6 +124,7 @@ class TypeParamCommaFormatter : public llvm::detail::format_adapter { EmitFormat emitFormat; ArrayRef params; + bool prependComma; }; } // end anonymous namespace @@ -168,10 +173,9 @@ static const char *const typeDefParsePrint = R"( /// The code block for the verifyConstructionInvariants and getChecked. /// /// {0}: List of parameters, parameters style. -/// {1}: C++ type class name. static const char *const typeDefDeclVerifyStr = R"( static ::mlir::LogicalResult verifyConstructionInvariants(Location loc{0}); - static {1} getChecked(Location loc{0}); + static ::mlir::Type getChecked(Location loc{0}); )"; /// Generate the declaration for the given typeDef class. @@ -194,14 +198,13 @@ static void emitTypeDefDecl(const TypeDef &typeDef, raw_ostream &os) { os << *extraDecl << "\n"; TypeParamCommaFormatter emitTypeNamePairsAfterComma( - TypeParamCommaFormatter::EmitFormat::TypeNamePairsPrependComma, params); + TypeParamCommaFormatter::EmitFormat::TypeNamePairs, params); os << llvm::formatv(" static {0} get(::mlir::MLIRContext* ctxt{1});\n", typeDef.getCppClassName(), emitTypeNamePairsAfterComma); // Emit the verify invariants declaration. if (typeDef.genVerifyInvariantsDecl()) - os << llvm::formatv(typeDefDeclVerifyStr, emitTypeNamePairsAfterComma, - typeDef.getCppClassName()); + os << llvm::formatv(typeDefDeclVerifyStr, emitTypeNamePairsAfterComma); // Emit the mnenomic, if specified. if (auto mnenomic = typeDef.getMnemonic()) { @@ -317,6 +320,17 @@ static const char *const typeDefStorageClassConstructorReturn = R"( } )"; +/// The code block for the getChecked definition. +/// +/// {0}: List of parameters, parameters style. +/// {1}: C++ type class name. +/// {2}: Comma separated list of parameter names. +static const char *const typeDefDefGetCheckeStr = R"( + ::mlir::Type {1}::getChecked(Location loc{0}) {{ + return Base::getChecked(loc{2}); + } +)"; + /// Use tgfmt to emit custom allocation code for each parameter, if necessary. static void emitParameterAllocationCode(TypeDef &typeDef, raw_ostream &os) { SmallVector parameters; @@ -355,27 +369,28 @@ static void emitStorageClass(TypeDef typeDef, raw_ostream &os) { auto parameterTypeList = join(parameterTypes, ", "); // 1) Emit most of the storage class up until the hashKey body. - os << formatv( - typeDefStorageClassBegin, typeDef.getStorageNamespace(), - typeDef.getStorageClassName(), - TypeParamCommaFormatter( - TypeParamCommaFormatter::EmitFormat::TypeNamePairs, parameters), - TypeParamCommaFormatter( - TypeParamCommaFormatter::EmitFormat::TypeNameInitializer, parameters), - parameterList, parameterTypeList); + os << formatv(typeDefStorageClassBegin, typeDef.getStorageNamespace(), + typeDef.getStorageClassName(), + TypeParamCommaFormatter( + TypeParamCommaFormatter::EmitFormat::TypeNamePairs, + parameters, /*prependComma=*/false), + TypeParamCommaFormatter( + TypeParamCommaFormatter::EmitFormat::TypeNameInitializer, + parameters, /*prependComma=*/false), + parameterList, parameterTypeList); // 2) Emit the haskKey method. os << " static ::llvm::hash_code hashKey(const KeyTy &key) {\n"; // Extract each parameter from the key. for (size_t i = 0, e = parameters.size(); i < e; ++i) - os << formatv(" const auto &{0} = std::get<{1}>(key);\n", - parameters[i].getName(), i); + os << llvm::formatv(" const auto &{0} = std::get<{1}>(key);\n", + parameters[i].getName(), i); // Then combine them all. This requires all the parameters types to have a // hash_value defined. - os << " return ::llvm::hash_combine("; - interleaveComma(parameterNames, os); - os << ");\n"; - os << " }\n"; + os << llvm::formatv( + " return ::llvm::hash_combine({0});\n }\n", + TypeParamCommaFormatter(TypeParamCommaFormatter::EmitFormat::JustParams, + parameters, /* prependComma */ false)); // 3) Emit the construct method. if (typeDef.hasStorageCustomConstructor()) @@ -462,14 +477,12 @@ static void emitTypeDefDef(TypeDef typeDef, raw_ostream &os) { os << llvm::formatv( "{0} {0}::get(::mlir::MLIRContext* ctxt{1}) {{\n" - " return Base::get(ctxt", + " return Base::get(ctxt{2});\n}\n", typeDef.getCppClassName(), TypeParamCommaFormatter( - TypeParamCommaFormatter::EmitFormat::TypeNamePairsPrependComma, - parameters)); - for (TypeParameter ¶m : parameters) - os << ", " << param.getName(); - os << ");\n}\n"; + TypeParamCommaFormatter::EmitFormat::TypeNamePairs, parameters), + TypeParamCommaFormatter(TypeParamCommaFormatter::EmitFormat::JustParams, + parameters)); // Emit the parameter accessors. if (typeDef.genAccessors()) @@ -481,6 +494,17 @@ static void emitTypeDefDef(TypeDef typeDef, raw_ostream &os) { typeDef.getCppClassName()); } + // Generate getChecked() method. + if (typeDef.genVerifyInvariantsDecl()) { + os << llvm::formatv( + typeDefDefGetCheckeStr, + TypeParamCommaFormatter( + TypeParamCommaFormatter::EmitFormat::TypeNamePairs, parameters), + typeDef.getCppClassName(), + TypeParamCommaFormatter(TypeParamCommaFormatter::EmitFormat::JustParams, + parameters)); + } + // If mnemonic is specified maybe print definitions for the parser and printer // code, if they're specified. if (typeDef.getMnemonic())