diff --git a/mlir/test/IR/properties.mlir b/mlir/test/IR/properties.mlir --- a/mlir/test/IR/properties.mlir +++ b/mlir/test/IR/properties.mlir @@ -18,3 +18,9 @@ // GENERIC: "test.with_wrapped_properties"() // GENERIC-SAME: <{prop = "content for properties"}> : () -> () test.with_wrapped_properties <{prop = "content for properties"}> + +// CHECK: test.using_property_in_custom +// CHECK-SAME: [1, 4, 20] +// GENERIC: "test.using_property_in_custom"() +// GENERIC-SAME: prop = array +test.using_property_in_custom [1, 4, 20] 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 @@ -1933,6 +1933,18 @@ return success(); } +static bool parseUsingPropertyInCustom(OpAsmParser &parser, int64_t value[3]) { + return parser.parseLSquare() || parser.parseInteger(value[0]) || + parser.parseComma() || parser.parseInteger(value[1]) || + parser.parseComma() || parser.parseInteger(value[2]) || + parser.parseRSquare(); +} + +static void printUsingPropertyInCustom(OpAsmPrinter &printer, Operation *op, + ArrayRef value) { + printer << '[' << value << ']'; +} + #include "TestOpEnums.cpp.inc" #include "TestOpInterfaces.cpp.inc" #include "TestTypeInterfaces.cpp.inc" diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td --- a/mlir/test/lib/Dialect/Test/TestOps.td +++ b/mlir/test/lib/Dialect/Test/TestOps.td @@ -3344,6 +3344,11 @@ ); } +def TestOpUsingPropertyInCustom : TEST_Op<"using_property_in_custom"> { + let assemblyFormat = "custom($prop) attr-dict"; + let arguments = (ins ArrayProperty<"int64_t", 3>:$prop); +} + // Op with a properties struct defined out-of-line. The struct has custom // printer/parser. diff --git a/mlir/test/mlir-tblgen/op-format-invalid.td b/mlir/test/mlir-tblgen/op-format-invalid.td --- a/mlir/test/mlir-tblgen/op-format-invalid.td +++ b/mlir/test/mlir-tblgen/op-format-invalid.td @@ -478,6 +478,11 @@ let regions = (region AnyRegion:$region); } +// CHECK: error: property 'prop' is already bound +def VariableInvalidO : TestFormat_Op<[{ + custom($prop, $prop) attr-dict +}]>, Arguments<(ins IntProperty<"int64_t">:$prop)>; + //===----------------------------------------------------------------------===// // Coverage Checks //===----------------------------------------------------------------------===// diff --git a/mlir/tools/mlir-tblgen/FormatGen.h b/mlir/tools/mlir-tblgen/FormatGen.h --- a/mlir/tools/mlir-tblgen/FormatGen.h +++ b/mlir/tools/mlir-tblgen/FormatGen.h @@ -235,7 +235,15 @@ class VariableElement : public FormatElementBase { public: /// These are the kinds of variables. - enum Kind { Attribute, Operand, Region, Result, Successor, Parameter }; + enum Kind { + Attribute, + Operand, + Region, + Result, + Successor, + Parameter, + Property + }; /// Get the kind of variable. Kind getKind() const { return kind; } diff --git a/mlir/tools/mlir-tblgen/OpFormatGen.cpp b/mlir/tools/mlir-tblgen/OpFormatGen.cpp --- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp +++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp @@ -94,6 +94,10 @@ /// This class represents a variable that refers to a successor. using SuccessorVariable = OpVariableElement; + +/// This class represents a variable that refers to a property argument. +using PropertyVariable = + OpVariableElement; } // namespace //===----------------------------------------------------------------------===// @@ -939,6 +943,9 @@ ctx.addSubst("_ctxt", "parser.getContext()"); body << tgfmt(string->getValue(), &ctx); + } else if (auto *property = dyn_cast(param)) { + body << llvm::formatv("result.getOrAddProperties().{0}", + property->getVar()->name); } else { llvm_unreachable("unknown custom directive parameter"); } @@ -1862,6 +1869,12 @@ ctx.addSubst("_ctxt", "getContext()"); body << tgfmt(string->getValue(), &ctx); + } else if (auto *property = dyn_cast(element)) { + FmtContext ctx; + ctx.addSubst("_ctxt", "getContext()"); + const NamedProperty *namedProperty = property->getVar(); + ctx.addSubst("_storage", "getProperties()." + namedProperty->name); + body << tgfmt(namedProperty->prop.getConvertFromStorageCall(), &ctx); } else { llvm_unreachable("unknown custom directive parameter"); } @@ -2457,6 +2470,7 @@ llvm::DenseSet seenOperands; llvm::DenseSet seenRegions; llvm::DenseSet seenSuccessors; + llvm::DenseSet seenProperties; }; } // namespace @@ -2941,6 +2955,18 @@ return create(attr); } + + if (const NamedProperty *property = findArg(op.getProperties(), name)) { + if (ctx != CustomDirectiveContext) + return emitError( + loc, "properties currently only supported in `custom` directive"); + + if (!seenProperties.insert(property).second) + return emitError(loc, "property '" + name + "' is already bound"); + + return create(property); + } + // Operands if (const NamedTypeConstraint *operand = findArg(op.getOperands(), name)) { if (ctx == TopLevelContext || ctx == CustomDirectiveContext) { @@ -3068,9 +3094,9 @@ LogicalResult OpFormatParser::verifyCustomDirectiveArguments( SMLoc loc, ArrayRef arguments) { for (FormatElement *argument : arguments) { - if (!isa(argument)) { + if (!isa(argument)) { // TODO: FormatElement should have location info attached. return emitError(loc, "only variables and types may be used as " "parameters to a custom directive");