Index: mlir/test/IR/properties.mlir =================================================================== --- mlir/test/IR/properties.mlir +++ 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] Index: mlir/test/lib/Dialect/Test/TestDialect.cpp =================================================================== --- mlir/test/lib/Dialect/Test/TestDialect.cpp +++ mlir/test/lib/Dialect/Test/TestDialect.cpp @@ -1933,6 +1933,21 @@ 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" Index: mlir/test/lib/Dialect/Test/TestOps.td =================================================================== --- mlir/test/lib/Dialect/Test/TestOps.td +++ mlir/test/lib/Dialect/Test/TestOps.td @@ -3344,6 +3344,13 @@ ); } +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. Index: mlir/test/mlir-tblgen/op-format-invalid.td =================================================================== --- mlir/test/mlir-tblgen/op-format-invalid.td +++ 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 //===----------------------------------------------------------------------===// Index: mlir/tools/mlir-tblgen/FormatGen.h =================================================================== --- mlir/tools/mlir-tblgen/FormatGen.h +++ mlir/tools/mlir-tblgen/FormatGen.h @@ -235,7 +235,7 @@ 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; } Index: mlir/tools/mlir-tblgen/OpFormatGen.cpp =================================================================== --- mlir/tools/mlir-tblgen/OpFormatGen.cpp +++ mlir/tools/mlir-tblgen/OpFormatGen.cpp @@ -94,6 +94,9 @@ /// This class represents a variable that refers to a successor. using SuccessorVariable = OpVariableElement; + +using PropertyVariable = + OpVariableElement; } // namespace //===----------------------------------------------------------------------===// @@ -939,6 +942,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 +1868,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 +2469,7 @@ llvm::DenseSet seenOperands; llvm::DenseSet seenRegions; llvm::DenseSet seenSuccessors; + llvm::DenseSet seenProperties; }; } // namespace @@ -2941,6 +2954,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) { @@ -3070,7 +3095,7 @@ for (FormatElement *argument : arguments) { if (!isa(argument)) { + SuccessorVariable, PropertyVariable>(argument)) { // TODO: FormatElement should have location info attached. return emitError(loc, "only variables and types may be used as " "parameters to a custom directive");