diff --git a/mlir/include/mlir/Dialect/IRDL/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/IRDL/IR/CMakeLists.txt --- a/mlir/include/mlir/Dialect/IRDL/IR/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/IRDL/IR/CMakeLists.txt @@ -20,3 +20,12 @@ mlir_tablegen(IRDLTypesGen.cpp.inc -gen-typedef-defs) add_public_tablegen_target(MLIRIRDLTypesIncGen) add_dependencies(mlir-generic-headers MLIRIRDLTypesIncGen) + +# Add IRDL attributes +set(LLVM_TARGET_DEFINITIONS IRDLAttributes.td) +mlir_tablegen(IRDLEnums.h.inc -gen-enum-decls) +mlir_tablegen(IRDLEnums.cpp.inc -gen-enum-defs) +mlir_tablegen(IRDLAttributes.h.inc -gen-attrdef-decls) +mlir_tablegen(IRDLAttributes.cpp.inc -gen-attrdef-defs) +add_public_tablegen_target(MLIRIRDLAttributesIncGen) +add_dependencies(mlir-generic-headers MLIRIRDLAttributesIncGen) diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDL.h b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.h --- a/mlir/include/mlir/Dialect/IRDL/IR/IRDL.h +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.h @@ -39,6 +39,11 @@ #define GET_TYPEDEF_CLASSES #include "mlir/Dialect/IRDL/IR/IRDLTypesGen.h.inc" +#include "mlir/Dialect/IRDL/IR/IRDLEnums.h.inc" + +#define GET_ATTRDEF_CLASSES +#include "mlir/Dialect/IRDL/IR/IRDLAttributes.h.inc" + #define GET_OP_CLASSES #include "mlir/Dialect/IRDL/IR/IRDLOps.h.inc" diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td --- a/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td @@ -69,6 +69,7 @@ attribute. The rationale of this is to simplify the dialect. }]; + let useDefaultAttributePrinterParser = 1; let useDefaultTypePrinterParser = 1; let usePropertiesForAttributes = 1; diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLAttributes.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLAttributes.td new file mode 100644 --- /dev/null +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLAttributes.td @@ -0,0 +1,52 @@ +//===- IRDLAttributes.td - IR Definition Language Dialect --*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file declares the attributes used in IRDL. +// +//===----------------------------------------------------------------------===// + +#ifndef MLIR_DIALECT_IRDL_IR_IRDLATTRIBUTES +#define MLIR_DIALECT_IRDL_IR_IRDLATTRIBUTES + +include "IRDL.td" +include "mlir/IR/AttrTypeBase.td" +include "mlir/IR/EnumAttr.td" + +def Variadicity : I32EnumAttr< + "Variadicity", "variadicity kind", + [ + I32EnumAttrCase<"single", 0>, + I32EnumAttrCase<"optional", 1>, + I32EnumAttrCase<"variadic", 2>, + ]> { + let cppNamespace = "::mlir::irdl"; + let genSpecializedAttr = 0; +} + +def VariadicityAttr : EnumAttr { + let summary = + "A variadicity kind. Can be either 'single', 'optional', or 'variadic'"; + let description = [{ + A `irdl.variadicity` attribute specifies that the associated operand or + result definition is either a single definition (the default), an + optional definition, or a variadic definition. + + For instance: + ```mlir + irdl.operands (%arg1, single %arg2, optional %arg3, variadic %arg4) + ``` + + In this example, both %arg1 and %arg2 are single operands, %arg3 is an + optional operand, and %arg4 is a variadic operand. + }]; +} + +def VariadicityArrayAttr : ArrayOfAttr {} + +#endif // MLIR_DIALECT_IRDL_IR_IRDLATTRIBUTES diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td --- a/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td +++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDLOps.td @@ -14,6 +14,7 @@ #define MLIR_DIALECT_IRDL_IR_IRDLOPS include "IRDL.td" +include "IRDLAttributes.td" include "IRDLTypes.td" include "IRDLInterfaces.td" include "mlir/Interfaces/SideEffectInterfaces.td" @@ -223,10 +224,25 @@ The `mul` operation will expect two operands of type `cmath.complex`, that have the same type, and return a result of the same type. + + The operands can also be marked as variadic or optional: + ```mlir + irdl.operands(%0, single %1, optional %2, variadic %3) + ``` + + Here, %0 and %1 are required single operands, %2 is an optional operand, + and %3 is a variadic operand. + + When more than one operand is marked as optional or variadic, the operation + will expect a 'operand_segment_sizes' attribute that defines the number of + operands in each segment. }]; - let arguments = (ins Variadic:$args); - let assemblyFormat = " `(` $args `)` attr-dict "; + let arguments = (ins Variadic:$args, + VariadicityArrayAttr:$variadicity); + let assemblyFormat = + "`` custom($args, $variadicity) attr-dict"; + let hasVerifier = true; } def IRDL_ResultsOp : IRDL_Op<"results", [HasParent<"OperationOp">]> { @@ -254,10 +270,25 @@ The operation will expect one operand of the `cmath.complex` type, and two results that have the underlying type of the `cmath.complex`. + + The results can also be marked as variadic or optional: + ```mlir + irdl.results(%0, single %1, optional %2, variadic %3) + ``` + + Here, %0 and %1 are required single results, %2 is an optional result, + and %3 is a variadic result. + + When more than one result is marked as optional or variadic, the operation + will expect a 'result_segment_sizes' attribute that defines the number of + results in each segment. }]; - let arguments = (ins Variadic:$args); - let assemblyFormat = " `(` $args `)` attr-dict "; + let arguments = (ins Variadic:$args, + VariadicityArrayAttr:$variadicity); + let assemblyFormat = + " `` custom($args, $variadicity) attr-dict"; + let hasVerifier = true; } def IRDL_AttributesOp : IRDL_Op<"attributes", [HasParent<"OperationOp">]> { diff --git a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp --- a/mlir/lib/Dialect/IRDL/IR/IRDL.cpp +++ b/mlir/lib/Dialect/IRDL/IR/IRDL.cpp @@ -39,6 +39,10 @@ #define GET_TYPEDEF_LIST #include "mlir/Dialect/IRDL/IR/IRDLTypesGen.cpp.inc" >(); + addAttributes< +#define GET_ATTRDEF_LIST +#include "mlir/Dialect/IRDL/IR/IRDLAttributes.cpp.inc" + >(); } //===----------------------------------------------------------------------===// @@ -71,9 +75,35 @@ return success(); } +LogicalResult OperandsOp::verify() { + size_t numVariadicities = getVariadicity().size(); + size_t numOperands = getNumOperands(); + + if (numOperands != numVariadicities) + return emitOpError() + << "the number of operands and their variadicities must be " + "the same, but got " + << numOperands << " and " << numVariadicities << " respectively"; + + return success(); +} + +LogicalResult ResultsOp::verify() { + size_t numVariadicities = getVariadicity().size(); + size_t numOperands = this->getNumOperands(); + + if (numOperands != numVariadicities) + return emitOpError() + << "the number of operands and their variadicities must be " + "the same, but got " + << numOperands << " and " << numVariadicities << " respectively"; + + return success(); +} + LogicalResult AttributesOp::verify() { - const size_t namesSize = getAttributeValueNames().size(); - const size_t valuesSize = getAttributeValues().size(); + size_t namesSize = getAttributeValueNames().size(); + size_t valuesSize = getAttributeValues().size(); if (namesSize != valuesSize) return emitOpError() @@ -84,6 +114,83 @@ return success(); } +/// Parse a value with its variadicity first. By default, the variadicity is +/// single. +/// +/// value-with-variadicity ::= ("single" | "optional" | "variadic")? ssa-value +static ParseResult +parseValueWithVariadicity(OpAsmParser &p, + OpAsmParser::UnresolvedOperand &operand, + VariadicityAttr &variadicityAttr) { + MLIRContext *ctx = p.getBuilder().getContext(); + + // Parse the variadicity, if present + if (p.parseOptionalKeyword("single").succeeded()) { + variadicityAttr = VariadicityAttr::get(ctx, Variadicity::single); + } else if (p.parseOptionalKeyword("optional").succeeded()) { + variadicityAttr = VariadicityAttr::get(ctx, Variadicity::optional); + } else if (p.parseOptionalKeyword("variadic").succeeded()) { + variadicityAttr = VariadicityAttr::get(ctx, Variadicity::variadic); + } else { + variadicityAttr = VariadicityAttr::get(ctx, Variadicity::single); + } + + // Parse the value + if (p.parseOperand(operand)) + return failure(); + return success(); +} + +/// Parse a list of values with their variadicities first. By default, the +/// variadicity is single. +/// +/// values-with-variadicity ::= +/// `(` (value-with-variadicity (`,` value-with-variadicity)*)? `)` +/// value-with-variadicity ::= ("single" | "optional" | "variadic")? ssa-value +static ParseResult parseValuesWithVariadicity( + OpAsmParser &p, SmallVectorImpl &operands, + VariadicityArrayAttr &variadicityAttr) { + Builder &builder = p.getBuilder(); + MLIRContext *ctx = builder.getContext(); + SmallVector variadicities; + + // Parse a single value with its variadicity + auto parseOne = [&] { + OpAsmParser::UnresolvedOperand operand; + VariadicityAttr variadicity; + if (parseValueWithVariadicity(p, operand, variadicity)) + return failure(); + operands.push_back(operand); + variadicities.push_back(variadicity); + return success(); + }; + + if (p.parseCommaSeparatedList(OpAsmParser::Delimiter::Paren, parseOne)) + return failure(); + variadicityAttr = VariadicityArrayAttr::get(ctx, variadicities); + return success(); +} + +/// Print a list of values with their variadicities first. By default, the +/// variadicity is single. +/// +/// values-with-variadicity ::= +/// `(` (value-with-variadicity (`,` value-with-variadicity)*)? `)` +/// value-with-variadicity ::= ("single" | "optional" | "variadic")? ssa-value +static void printValuesWithVariadicity(OpAsmPrinter &p, Operation *op, + OperandRange operands, + VariadicityArrayAttr variadicityAttr) { + p << "("; + interleaveComma(llvm::seq(0, operands.size()), p, [&](int i) { + Variadicity variadicity = variadicityAttr[i].getValue(); + if (variadicity != Variadicity::single) { + p << stringifyVariadicity(variadicity) << " "; + } + p << operands[i]; + }); + p << ")"; +} + static ParseResult parseAttributesOp(OpAsmParser &p, SmallVectorImpl &attrOperands, @@ -119,5 +226,10 @@ #define GET_TYPEDEF_CLASSES #include "mlir/Dialect/IRDL/IR/IRDLTypesGen.cpp.inc" +#include "mlir/Dialect/IRDL/IR/IRDLEnums.cpp.inc" + +#define GET_ATTRDEF_CLASSES +#include "mlir/Dialect/IRDL/IR/IRDLAttributes.cpp.inc" + #define GET_OP_CLASSES #include "mlir/Dialect/IRDL/IR/IRDLOps.cpp.inc" diff --git a/mlir/test/Dialect/IRDL/variadics-error.irdl.mlir b/mlir/test/Dialect/IRDL/variadics-error.irdl.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/IRDL/variadics-error.irdl.mlir @@ -0,0 +1,43 @@ +// RUN: mlir-opt %s -verify-diagnostics -split-input-file + +irdl.dialect @errors { + irdl.operation @operands { + %0 = irdl.is i32 + + // expected-error@+1 {{'irdl.operands' op the number of operands and their variadicities must be the same, but got 2 and 1 respectively}} + "irdl.operands"(%0, %0) <{variadicity = #irdl}> : (!irdl.attribute, !irdl.attribute) -> () + } +} + +// ----- + +irdl.dialect @errors { + irdl.operation @operands2 { + %0 = irdl.is i32 + + // expected-error@+1 {{'irdl.operands' op the number of operands and their variadicities must be the same, but got 1 and 2 respectively}} + "irdl.operands"(%0) <{variadicity = #irdl}> : (!irdl.attribute) -> () + } +} + +// ----- + +irdl.dialect @errors { + irdl.operation @results { + %0 = irdl.is i32 + + // expected-error@+1 {{'irdl.results' op the number of operands and their variadicities must be the same, but got 2 and 1 respectively}} + "irdl.results"(%0, %0) <{variadicity = #irdl}> : (!irdl.attribute, !irdl.attribute) -> () + } +} + +// ----- + +irdl.dialect @errors { + irdl.operation @results2 { + %0 = irdl.is i32 + + // expected-error@+1 {{'irdl.results' op the number of operands and their variadicities must be the same, but got 1 and 2 respectively}} + "irdl.results"(%0) <{variadicity = #irdl}> : (!irdl.attribute) -> () + } +} diff --git a/mlir/test/Dialect/IRDL/variadics.irdl.mlir b/mlir/test/Dialect/IRDL/variadics.irdl.mlir new file mode 100644 --- /dev/null +++ b/mlir/test/Dialect/IRDL/variadics.irdl.mlir @@ -0,0 +1,102 @@ +// RUN: mlir-opt %s | mlir-opt | FileCheck %s + +// CHECK: irdl.dialect @testvar { +irdl.dialect @testvar { + + // CHECK-LABEL: irdl.operation @single_operand { + // CHECK-NEXT: %[[v0:[^ ]*]] = irdl.is i32 + // CHECK-NEXT: irdl.operands(%[[v0]]) + // CHECK-NEXT: } + irdl.operation @single_operand { + %0 = irdl.is i32 + irdl.operands(single %0) + } + + // CHECK-LABEL: irdl.operation @var_operand { + // CHECK-NEXT: %[[v0:[^ ]*]] = irdl.is i16 + // CHECK-NEXT: %[[v1:[^ ]*]] = irdl.is i32 + // CHECK-NEXT: %[[v2:[^ ]*]] = irdl.is i64 + // CHECK-NEXT: irdl.operands(%[[v0]], variadic %[[v1]], %[[v2]]) + // CHECK-NEXT: } + irdl.operation @var_operand { + %0 = irdl.is i16 + %1 = irdl.is i32 + %2 = irdl.is i64 + irdl.operands(%0, variadic %1, %2) + } + + // CHECK-LABEL: irdl.operation @opt_operand { + // CHECK-NEXT: %[[v0:[^ ]*]] = irdl.is i16 + // CHECK-NEXT: %[[v1:[^ ]*]] = irdl.is i32 + // CHECK-NEXT: %[[v2:[^ ]*]] = irdl.is i64 + // CHECK-NEXT: irdl.operands(%[[v0]], variadic %[[v1]], %[[v2]]) + // CHECK-NEXT: } + irdl.operation @opt_operand { + %0 = irdl.is i16 + %1 = irdl.is i32 + %2 = irdl.is i64 + irdl.operands(%0, variadic %1, %2) + } + + // CHECK-LABEL: irdl.operation @var_and_opt_operand { + // CHECK-NEXT: %[[v0:[^ ]*]] = irdl.is i16 + // CHECK-NEXT: %[[v1:[^ ]*]] = irdl.is i32 + // CHECK-NEXT: %[[v2:[^ ]*]] = irdl.is i64 + // CHECK-NEXT: irdl.operands(variadic %[[v0]], optional %[[v1]], %[[v2]]) + // CHECK-NEXT: } + irdl.operation @var_and_opt_operand { + %0 = irdl.is i16 + %1 = irdl.is i32 + %2 = irdl.is i64 + irdl.operands(variadic %0, optional %1, %2) + } + + + // CHECK-LABEL: irdl.operation @single_result { + // CHECK-NEXT: %[[v0:[^ ]*]] = irdl.is i32 + // CHECK-NEXT: irdl.results(%[[v0]]) + // CHECK-NEXT: } + irdl.operation @single_result { + %0 = irdl.is i32 + irdl.results(single %0) + } + + // CHECK-LABEL: irdl.operation @var_result { + // CHECK-NEXT: %[[v0:[^ ]*]] = irdl.is i16 + // CHECK-NEXT: %[[v1:[^ ]*]] = irdl.is i32 + // CHECK-NEXT: %[[v2:[^ ]*]] = irdl.is i64 + // CHECK-NEXT: irdl.results(%[[v0]], variadic %[[v1]], %[[v2]]) + // CHECK-NEXT: } + irdl.operation @var_result { + %0 = irdl.is i16 + %1 = irdl.is i32 + %2 = irdl.is i64 + irdl.results(%0, variadic %1, %2) + } + + // CHECK-LABEL: irdl.operation @opt_result { + // CHECK-NEXT: %[[v0:[^ ]*]] = irdl.is i16 + // CHECK-NEXT: %[[v1:[^ ]*]] = irdl.is i32 + // CHECK-NEXT: %[[v2:[^ ]*]] = irdl.is i64 + // CHECK-NEXT: irdl.results(%[[v0]], variadic %[[v1]], %[[v2]]) + // CHECK-NEXT: } + irdl.operation @opt_result { + %0 = irdl.is i16 + %1 = irdl.is i32 + %2 = irdl.is i64 + irdl.results(%0, variadic %1, %2) + } + + // CHECK-LABEL: irdl.operation @var_and_opt_result { + // CHECK-NEXT: %[[v0:[^ ]*]] = irdl.is i16 + // CHECK-NEXT: %[[v1:[^ ]*]] = irdl.is i32 + // CHECK-NEXT: %[[v2:[^ ]*]] = irdl.is i64 + // CHECK-NEXT: irdl.results(variadic %[[v0]], optional %[[v1]], %[[v2]]) + // CHECK-NEXT: } + irdl.operation @var_and_opt_result { + %0 = irdl.is i16 + %1 = irdl.is i32 + %2 = irdl.is i64 + irdl.results(variadic %0, optional %1, %2) + } +}