[Mlir-commits] [mlir] [MLIR][ODS] Add strict property assembly format mode (PR #196269)
Mehdi Amini
llvmlistbot at llvm.org
Fri Aug 14 11:47:09 PDT 2026
https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/196269
>From a5874c414424aa626c3de493513747db9485f45a Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Thu, 7 May 2026 02:32:39 -0700
Subject: [PATCH] [MLIR][ODS] Add strict property assembly format mode
Introduce a dialect-level ODS flag for strict property handling in declarative
assembly formats. It is disabled by default for now, preserving existing parser
behavior unless a dialect opts in.
Enable the mode immediately for dialects whose declarative assembly formats
already satisfy these binding rules.
When enabled, a property-backed op format must bind every inherent attribute
and property directly or include prop-dict.
Generated parsers for opted-in dialects also reject inherent attributes that
arrive through attr-dict, preventing Operation::setAttrs from populating
properties through that path.
Add mlir-tblgen coverage and document default and strict dialect behavior.
Assisted-by: Codex
---
mlir/docs/DefiningDialects/Operations.md | 7 +++
mlir/docs/DefiningDialects/_index.md | 20 ++++++
.../mlir/Dialect/Affine/IR/AffineOps.td | 1 +
mlir/include/mlir/Dialect/ArmNeon/ArmNeon.td | 1 +
mlir/include/mlir/Dialect/ArmSME/IR/ArmSME.td | 1 +
mlir/include/mlir/Dialect/ArmSVE/IR/ArmSVE.td | 1 +
.../mlir/Dialect/Complex/IR/ComplexBase.td | 1 +
mlir/include/mlir/Dialect/DLTI/DLTIBase.td | 1 +
mlir/include/mlir/Dialect/IRDL/IR/IRDL.td | 1 +
.../mlir/Dialect/Index/IR/IndexDialect.td | 1 +
mlir/include/mlir/Dialect/LLVMIR/VCIXOps.td | 1 +
mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td | 1 +
.../mlir/Dialect/Linalg/IR/LinalgBase.td | 1 +
.../Dialect/MLProgram/IR/MLProgramBase.td | 1 +
mlir/include/mlir/Dialect/MPI/IR/MPI.td | 1 +
.../include/mlir/Dialect/Ptr/IR/PtrDialect.td | 1 +
.../mlir/Dialect/Quant/IR/QuantBase.td | 1 +
mlir/include/mlir/Dialect/SCF/IR/SCFOps.td | 1 +
.../include/mlir/Dialect/SMT/IR/SMTDialect.td | 1 +
.../mlir/Dialect/Shard/IR/ShardBase.td | 1 +
.../mlir/Dialect/Tensor/IR/TensorBase.td | 1 +
mlir/include/mlir/Dialect/UB/IR/UBOps.td | 1 +
mlir/include/mlir/IR/DialectBase.td | 6 ++
mlir/include/mlir/TableGen/Dialect.h | 4 ++
mlir/lib/TableGen/Dialect.cpp | 4 ++
mlir/test/mlir-tblgen/op-format-invalid.td | 26 ++++++++
mlir/test/mlir-tblgen/op-format.td | 58 +++++++++++++++++
mlir/tools/mlir-tblgen/OpFormatGen.cpp | 62 +++++++++++++++++--
28 files changed, 203 insertions(+), 4 deletions(-)
diff --git a/mlir/docs/DefiningDialects/Operations.md b/mlir/docs/DefiningDialects/Operations.md
index 4302edbddf22c..1ef96130d836d 100644
--- a/mlir/docs/DefiningDialects/Operations.md
+++ b/mlir/docs/DefiningDialects/Operations.md
@@ -748,6 +748,10 @@ The available directives are as follows:
printed as part of the attribute dictionary unless a `prop-dict` is
present.
- Discardable attributes are always part of the `attr-dict`.
+ - For dialects that set `useStrictPropertiesInAssemblyFormat`,
+ `attr-dict` only carries discardable attributes for property-backed
+ operations. Inherent attributes must be bound directly in the format or
+ covered by `prop-dict`.
* `attr-dict-with-keyword`
@@ -1091,6 +1095,9 @@ to:
directives.
1. Unless all non-attribute properties appear in the format, the `prop-dict`
directive must be present.
+1. For dialects that set `useStrictPropertiesInAssemblyFormat`, every inherent
+ attribute and property must either appear in the format or be covered by the
+ `prop-dict` directive.
1. The `attr-dict` directive must always be present.
1. Must not contain overlapping information; e.g. multiple instances of
'attr-dict', types, operands, etc.
diff --git a/mlir/docs/DefiningDialects/_index.md b/mlir/docs/DefiningDialects/_index.md
index 987b51b4ab4ef..d4ffd066a61cd 100644
--- a/mlir/docs/DefiningDialects/_index.md
+++ b/mlir/docs/DefiningDialects/_index.md
@@ -272,6 +272,26 @@ void *MyDialect::getRegisteredInterfaceForOp(TypeID typeID, StringAttr opName);
For a more detail description of the expected usages of this hook, view the detailed
[interface documentation](../Interfaces.md/#dialect-fallback-for-opinterface).
+### Strict Property Assembly Formats
+
+Dialects can set `useStrictPropertiesInAssemblyFormat` to require declarative
+assembly formats for property-backed operations to account for all inherent
+attributes and properties:
+
+```tablegen
+def MyDialect : Dialect {
+ let useStrictPropertiesInAssemblyFormat = 1;
+}
+```
+
+This mode is disabled by default for now. When enabled, an operation format must
+either bind every inherent attribute and property directly in the format or
+include the `prop-dict` directive. Generated parsers also reject inherent
+attributes that arrive through `attr-dict`, so `attr-dict` only carries
+discardable attributes for these formats. See the
+[declarative assembly format](Operations.md/#declarative-assembly-format)
+documentation for the corresponding format requirements.
+
### Default Attribute/Type Parsers and Printers
When a dialect registers an Attribute or Type, it must also override the respective
diff --git a/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td b/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td
index 05db39eb571a6..994df5aef0d9a 100644
--- a/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td
+++ b/mlir/include/mlir/Dialect/Affine/IR/AffineOps.td
@@ -25,6 +25,7 @@ include "mlir/Interfaces/SideEffectInterfaces.td"
def Affine_Dialect : Dialect {
let name = "affine";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::affine";
let hasConstantMaterializer = 1;
let dependentDialects = ["arith::ArithDialect", "ub::UBDialect"];
diff --git a/mlir/include/mlir/Dialect/ArmNeon/ArmNeon.td b/mlir/include/mlir/Dialect/ArmNeon/ArmNeon.td
index ce86ff2cfd922..fd0fa7ecf9b0d 100644
--- a/mlir/include/mlir/Dialect/ArmNeon/ArmNeon.td
+++ b/mlir/include/mlir/Dialect/ArmNeon/ArmNeon.td
@@ -23,6 +23,7 @@ include "mlir/IR/OpBase.td"
def ArmNeon_Dialect : Dialect {
let name = "arm_neon";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::arm_neon";
// Note: this does not need to depend on LLVMDialect as long as functions in
diff --git a/mlir/include/mlir/Dialect/ArmSME/IR/ArmSME.td b/mlir/include/mlir/Dialect/ArmSME/IR/ArmSME.td
index ffafb2569310e..f937af9c35a71 100644
--- a/mlir/include/mlir/Dialect/ArmSME/IR/ArmSME.td
+++ b/mlir/include/mlir/Dialect/ArmSME/IR/ArmSME.td
@@ -23,6 +23,7 @@ include "mlir/Dialect/LLVMIR/LLVMOpBase.td"
def ArmSME_Dialect : Dialect {
let name = "arm_sme";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::arm_sme";
let summary = "Basic dialect to target Arm SME architectures";
let description = [{
diff --git a/mlir/include/mlir/Dialect/ArmSVE/IR/ArmSVE.td b/mlir/include/mlir/Dialect/ArmSVE/IR/ArmSVE.td
index a38485ea30ff4..61bb13b76167c 100644
--- a/mlir/include/mlir/Dialect/ArmSVE/IR/ArmSVE.td
+++ b/mlir/include/mlir/Dialect/ArmSVE/IR/ArmSVE.td
@@ -22,6 +22,7 @@ include "mlir/Dialect/LLVMIR/LLVMOpBase.td"
def ArmSVE_Dialect : Dialect {
let name = "arm_sve";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::arm_sve";
let summary = "Basic dialect to target Arm SVE architectures";
let description = [{
diff --git a/mlir/include/mlir/Dialect/Complex/IR/ComplexBase.td b/mlir/include/mlir/Dialect/Complex/IR/ComplexBase.td
index c8af498f44829..4efe1bcc620c2 100644
--- a/mlir/include/mlir/Dialect/Complex/IR/ComplexBase.td
+++ b/mlir/include/mlir/Dialect/Complex/IR/ComplexBase.td
@@ -14,6 +14,7 @@ include "mlir/IR/OpBase.td"
def Complex_Dialect : Dialect {
let name = "complex";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::complex";
let description = [{
The complex dialect is intended to hold complex numbers creation and
diff --git a/mlir/include/mlir/Dialect/DLTI/DLTIBase.td b/mlir/include/mlir/Dialect/DLTI/DLTIBase.td
index 3754f3699c7fd..f7c9be4fd7880 100644
--- a/mlir/include/mlir/Dialect/DLTI/DLTIBase.td
+++ b/mlir/include/mlir/Dialect/DLTI/DLTIBase.td
@@ -76,6 +76,7 @@ def DLTI_Dialect : Dialect {
}];
let useDefaultAttributePrinterParser = 1;
+ let useStrictPropertiesInAssemblyFormat = 1;
}
def HasDefaultDLTIDataLayout : NativeOpTrait<"HasDefaultDLTIDataLayout"> {
diff --git a/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td
index e822969fc575e..f554358ed373d 100644
--- a/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td
+++ b/mlir/include/mlir/Dialect/IRDL/IR/IRDL.td
@@ -74,6 +74,7 @@ def IRDL_Dialect : Dialect {
let name = "irdl";
let cppNamespace = "::mlir::irdl";
+ let useStrictPropertiesInAssemblyFormat = 1;
}
#endif // MLIR_DIALECT_IRDL_IR_IRDL
diff --git a/mlir/include/mlir/Dialect/Index/IR/IndexDialect.td b/mlir/include/mlir/Dialect/Index/IR/IndexDialect.td
index be0fea79ee392..df6087818cc8f 100644
--- a/mlir/include/mlir/Dialect/Index/IR/IndexDialect.td
+++ b/mlir/include/mlir/Dialect/Index/IR/IndexDialect.td
@@ -83,6 +83,7 @@ def IndexDialect : Dialect {
let hasConstantMaterializer = 1;
let useDefaultAttributePrinterParser = 1;
+ let useStrictPropertiesInAssemblyFormat = 1;
}
#endif // INDEX_DIALECT
diff --git a/mlir/include/mlir/Dialect/LLVMIR/VCIXOps.td b/mlir/include/mlir/Dialect/LLVMIR/VCIXOps.td
index 27d9a32dd8e03..02f80bbd56451 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/VCIXOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/VCIXOps.td
@@ -31,6 +31,7 @@ def VCIX_Dialect : Dialect {
let name = "vcix";
let cppNamespace = "::mlir::vcix";
let dependentDialects = ["LLVM::LLVMDialect"];
+ let useStrictPropertiesInAssemblyFormat = 1;
let description = [{
The SiFive Vector Coprocessor Interface (VCIX) provides a flexible mechanism
to extend application processors with custom coprocessors and
diff --git a/mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td
index 8c4e409e9c395..c52a1c0c94402 100644
--- a/mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td
+++ b/mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td
@@ -37,6 +37,7 @@ def XeVM_Dialect : Dialect {
}];
let useDefaultAttributePrinterParser = 1;
+ let useStrictPropertiesInAssemblyFormat = 1;
}
class XeVM_Attr<string attrName, string attrMnemonic, list<Trait> traits = []>
diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgBase.td b/mlir/include/mlir/Dialect/Linalg/IR/LinalgBase.td
index a459656b982e6..55b62b643b0e9 100644
--- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgBase.td
+++ b/mlir/include/mlir/Dialect/Linalg/IR/LinalgBase.td
@@ -43,6 +43,7 @@ def Linalg_Dialect : Dialect {
"tensor::TensorDialect",
];
let useDefaultAttributePrinterParser = 1;
+ let useStrictPropertiesInAssemblyFormat = 1;
let hasCanonicalizer = 1;
let hasOperationAttrVerify = 1;
let hasConstantMaterializer = 1;
diff --git a/mlir/include/mlir/Dialect/MLProgram/IR/MLProgramBase.td b/mlir/include/mlir/Dialect/MLProgram/IR/MLProgramBase.td
index a585059020eaf..5ed346aeade5d 100644
--- a/mlir/include/mlir/Dialect/MLProgram/IR/MLProgramBase.td
+++ b/mlir/include/mlir/Dialect/MLProgram/IR/MLProgramBase.td
@@ -13,6 +13,7 @@ include "mlir/IR/OpBase.td"
def MLProgram_Dialect : Dialect {
let name = "ml_program";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::ml_program";
let description = [{
The MLProgram dialect contains structural operations and types for
diff --git a/mlir/include/mlir/Dialect/MPI/IR/MPI.td b/mlir/include/mlir/Dialect/MPI/IR/MPI.td
index 0c62a1794e19e..6869df20936d4 100644
--- a/mlir/include/mlir/Dialect/MPI/IR/MPI.td
+++ b/mlir/include/mlir/Dialect/MPI/IR/MPI.td
@@ -15,6 +15,7 @@ include "mlir/IR/EnumAttr.td"
def MPI_Dialect : Dialect {
let name = "mpi";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::mpi";
let description = [{
This dialect models the Message Passing Interface (MPI), version
diff --git a/mlir/include/mlir/Dialect/Ptr/IR/PtrDialect.td b/mlir/include/mlir/Dialect/Ptr/IR/PtrDialect.td
index c98df5775195a..bf1f1a3c89f5a 100644
--- a/mlir/include/mlir/Dialect/Ptr/IR/PtrDialect.td
+++ b/mlir/include/mlir/Dialect/Ptr/IR/PtrDialect.td
@@ -20,6 +20,7 @@ include "mlir/IR/OpBase.td"
def Ptr_Dialect : Dialect {
let name = "ptr";
+ let useStrictPropertiesInAssemblyFormat = 1;
let summary = "Pointer dialect";
let description = [{
The pointer dialect provides types and operations for representing and
diff --git a/mlir/include/mlir/Dialect/Quant/IR/QuantBase.td b/mlir/include/mlir/Dialect/Quant/IR/QuantBase.td
index b129e4b57e353..f11717f835f9b 100644
--- a/mlir/include/mlir/Dialect/Quant/IR/QuantBase.td
+++ b/mlir/include/mlir/Dialect/Quant/IR/QuantBase.td
@@ -17,6 +17,7 @@ include "mlir/IR/OpBase.td"
def Quant_Dialect : Dialect {
let name = "quant";
+ let useStrictPropertiesInAssemblyFormat = 1;
let description = [{
The `quant` dialect offers a framework for defining and manipulating
quantized values. Central to this framework is the `!quant.uniform` data
diff --git a/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td b/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
index c0d1ac501cc77..1e7bba2cdad35 100644
--- a/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
+++ b/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
@@ -27,6 +27,7 @@ include "mlir/Interfaces/ViewLikeInterface.td"
def SCF_Dialect : Dialect {
let name = "scf";
let cppNamespace = "::mlir::scf";
+ let useStrictPropertiesInAssemblyFormat = 1;
let description = [{
The `scf` (structured control flow) dialect contains operations that
diff --git a/mlir/include/mlir/Dialect/SMT/IR/SMTDialect.td b/mlir/include/mlir/Dialect/SMT/IR/SMTDialect.td
index 00f170659946e..4b33b07da30c1 100644
--- a/mlir/include/mlir/Dialect/SMT/IR/SMTDialect.td
+++ b/mlir/include/mlir/Dialect/SMT/IR/SMTDialect.td
@@ -13,6 +13,7 @@ include "mlir/IR/DialectBase.td"
def SMTDialect : Dialect {
let name = "smt";
+ let useStrictPropertiesInAssemblyFormat = 1;
let summary = "a dialect that models satisfiability modulo theories";
let cppNamespace = "mlir::smt";
diff --git a/mlir/include/mlir/Dialect/Shard/IR/ShardBase.td b/mlir/include/mlir/Dialect/Shard/IR/ShardBase.td
index 41ae31807c825..9af607f7bca5b 100644
--- a/mlir/include/mlir/Dialect/Shard/IR/ShardBase.td
+++ b/mlir/include/mlir/Dialect/Shard/IR/ShardBase.td
@@ -21,6 +21,7 @@ include "mlir/IR/EnumAttr.td"
def Shard_Dialect : Dialect {
let name = "shard";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::shard";
let description = [{
diff --git a/mlir/include/mlir/Dialect/Tensor/IR/TensorBase.td b/mlir/include/mlir/Dialect/Tensor/IR/TensorBase.td
index 9d0add92737f3..900ad5f40830c 100644
--- a/mlir/include/mlir/Dialect/Tensor/IR/TensorBase.td
+++ b/mlir/include/mlir/Dialect/Tensor/IR/TensorBase.td
@@ -13,6 +13,7 @@ include "mlir/IR/OpBase.td"
def Tensor_Dialect : Dialect {
let name = "tensor";
+ let useStrictPropertiesInAssemblyFormat = 1;
let cppNamespace = "::mlir::tensor";
let description = [{
diff --git a/mlir/include/mlir/Dialect/UB/IR/UBOps.td b/mlir/include/mlir/Dialect/UB/IR/UBOps.td
index 1bff39add691e..666301799256b 100644
--- a/mlir/include/mlir/Dialect/UB/IR/UBOps.td
+++ b/mlir/include/mlir/Dialect/UB/IR/UBOps.td
@@ -20,6 +20,7 @@ def UB_Dialect : Dialect {
let hasConstantMaterializer = 1;
let useDefaultAttributePrinterParser = 1;
+ let useStrictPropertiesInAssemblyFormat = 1;
}
// Base class for UB dialect attributes.
diff --git a/mlir/include/mlir/IR/DialectBase.td b/mlir/include/mlir/IR/DialectBase.td
index efa09a43ec581..3b41e841eb3ad 100644
--- a/mlir/include/mlir/IR/DialectBase.td
+++ b/mlir/include/mlir/IR/DialectBase.td
@@ -55,6 +55,12 @@ class Dialect {
// dialect declaration.
code extraClassDeclaration = "";
+ // If this dialect should require declarative parsers for property-backed
+ // operations to bind every inherent attribute and property directly in the
+ // custom assembly format, or otherwise cover them with `prop-dict`. This
+ // stricter mode is disabled by default for now.
+ bit useStrictPropertiesInAssemblyFormat = 0;
+
// If this dialect overrides the hook for materializing constants.
bit hasConstantMaterializer = 0;
diff --git a/mlir/include/mlir/TableGen/Dialect.h b/mlir/include/mlir/TableGen/Dialect.h
index 30f9d690b678d..68230196b17cc 100644
--- a/mlir/include/mlir/TableGen/Dialect.h
+++ b/mlir/include/mlir/TableGen/Dialect.h
@@ -54,6 +54,10 @@ class Dialect {
// Returns the dialects extra class declaration code.
std::optional<StringRef> getExtraClassDeclaration() const;
+ /// Returns true if this dialect uses strict properties in declarative
+ /// assembly formats.
+ bool useStrictPropertiesInAssemblyFormat() const;
+
/// Returns true if this dialect has a canonicalizer.
bool hasCanonicalizer() const;
diff --git a/mlir/lib/TableGen/Dialect.cpp b/mlir/lib/TableGen/Dialect.cpp
index 7aaf4dd57c50e..84530bfa4f6fd 100644
--- a/mlir/lib/TableGen/Dialect.cpp
+++ b/mlir/lib/TableGen/Dialect.cpp
@@ -62,6 +62,10 @@ std::optional<StringRef> Dialect::getExtraClassDeclaration() const {
return value.empty() ? std::optional<StringRef>() : value;
}
+bool Dialect::useStrictPropertiesInAssemblyFormat() const {
+ return def->getValueAsBit("useStrictPropertiesInAssemblyFormat");
+}
+
bool Dialect::hasCanonicalizer() const {
return def->getValueAsBit("hasCanonicalizer");
}
diff --git a/mlir/test/mlir-tblgen/op-format-invalid.td b/mlir/test/mlir-tblgen/op-format-invalid.td
index 59446bb9d7019..1944bc4feb634 100644
--- a/mlir/test/mlir-tblgen/op-format-invalid.td
+++ b/mlir/test/mlir-tblgen/op-format-invalid.td
@@ -8,10 +8,18 @@ include "mlir/Interfaces/InferTypeOpInterface.td"
def TestDialect : Dialect {
let name = "test";
}
+def TestStrictPropertiesDialect : Dialect {
+ let name = "test_strict_properties";
+ let useStrictPropertiesInAssemblyFormat = 1;
+}
class TestFormat_Op<string fmt, list<Trait> traits = []>
: Op<TestDialect, "format_op", traits> {
let assemblyFormat = fmt;
}
+class TestStrictPropertiesFormat_Op<string fmt, list<Trait> traits = []>
+ : Op<TestStrictPropertiesDialect, "format_op", traits> {
+ let assemblyFormat = fmt;
+}
//===----------------------------------------------------------------------===//
// Directives
@@ -37,6 +45,24 @@ def DirectiveAttrDictInvalidD : TestFormat_Op<[{
type(attr-dict)
}]>;
+// CHECK: error: strict properties in assembly format requires prop-dict
+// CHECK-SAME: unless all inherent attributes and properties are bound in the
+// CHECK-SAME: custom assembly format; missing attribute 'attr'
+def DirectiveAttrDictInvalidE : TestStrictPropertiesFormat_Op<[{
+ attr-dict
+}]> {
+ let arguments = (ins I64Attr:$attr);
+}
+
+// CHECK: error: strict properties in assembly format requires prop-dict
+// CHECK-SAME: unless all inherent attributes and properties are bound in the
+// CHECK-SAME: custom assembly format; missing property 'prop'
+def DirectiveAttrDictInvalidF : TestStrictPropertiesFormat_Op<[{
+ attr-dict
+}]> {
+ let arguments = (ins IntProp<"int64_t">:$prop);
+}
+
//===----------------------------------------------------------------------===//
// custom
//===----------------------------------------------------------------------===//
diff --git a/mlir/test/mlir-tblgen/op-format.td b/mlir/test/mlir-tblgen/op-format.td
index eea574888aee5..6c7e6961abfa7 100644
--- a/mlir/test/mlir-tblgen/op-format.td
+++ b/mlir/test/mlir-tblgen/op-format.td
@@ -5,10 +5,18 @@ include "mlir/IR/OpBase.td"
def TestDialect : Dialect {
let name = "test";
}
+def TestStrictPropertiesDialect : Dialect {
+ let name = "test_strict_properties";
+ let useStrictPropertiesInAssemblyFormat = 1;
+}
class TestFormat_Op<string fmt, list<Trait> traits = []>
: Op<TestDialect, "format_op", traits> {
let assemblyFormat = fmt;
}
+class TestStrictPropertiesFormat_Op<string fmt, list<Trait> traits = []>
+ : Op<TestStrictPropertiesDialect, "format_op", traits> {
+ let assemblyFormat = fmt;
+}
//===----------------------------------------------------------------------===//
// Directives
@@ -18,6 +26,50 @@ class TestFormat_Op<string fmt, list<Trait> traits = []>
// custom
//===----------------------------------------------------------------------===//
+// CHECK-LABEL: AttrDictDefaultInherentAttr::parse
+// CHECK: verifyInherentAttrs
+def AttrDictDefaultInherentAttr : TestFormat_Op<[{
+ $attr attr-dict
+}]>, Arguments<(ins I64Attr:$attr)>;
+
+// CHECK-LABEL: AttrDictStrictInferredSegmentAttr::parse
+// CHECK: result.getOrAddProperties<AttrDictStrictInferredSegmentAttr::Properties>().segment_sizes = parser.getBuilder().getDenseI32ArrayAttr(inputsOperandGroupSizes);
+// CHECK-LABEL: AttrDictStrictInferredSegmentAttr::print
+// CHECK: elidedAttrs.push_back("segment_sizes");
+def AttrDictStrictInferredSegmentAttr : TestStrictPropertiesFormat_Op<[{
+ custom<Foo>($inputs, type($inputs)) attr-dict
+}]>, Arguments<(ins VariadicOfVariadic<I64, "segment_sizes">:$inputs,
+ DenseI32ArrayAttr:$segment_sizes)>;
+
+def PropDictStrictInferredSegmentAttr : TestStrictPropertiesFormat_Op<[{
+ custom<Foo>($inputs, type($inputs)) prop-dict attr-dict
+}]>, Arguments<(ins VariadicOfVariadic<I64, "segment_sizes">:$inputs,
+ DenseI32ArrayAttr:$segment_sizes)>;
+
+// CHECK-LABEL: AttrDictStrictInherentAttr::parse
+// CHECK-NOT: verifyInherentAttrs
+// CHECK: if (result.attributes.get("attr"))
+// CHECK-NEXT: return parser.emitError(loc, "inherent attribute 'attr' cannot
+// CHECK-SAME: be parsed from attr-dict when strict properties in assembly
+// CHECK-SAME: format is enabled");
+// CHECK-NOT: verifyInherentAttrs
+// CHECK: return ::mlir::success();
+def AttrDictStrictInherentAttr : TestStrictPropertiesFormat_Op<[{
+ $attr attr-dict
+}]>, Arguments<(ins I64Attr:$attr)>;
+
+def AttrDictStrictProperty : TestStrictPropertiesFormat_Op<[{
+ $prop attr-dict
+}]>, Arguments<(ins IntProp<"int64_t">:$prop)>;
+
+def AttrDictStrictPropDict : TestStrictPropertiesFormat_Op<[{
+ prop-dict attr-dict
+}]>, Arguments<(ins IntProp<"int64_t">:$prop)>;
+
+def AttrDictStrictPropDictInherentAttr : TestStrictPropertiesFormat_Op<[{
+ prop-dict attr-dict
+}]>, Arguments<(ins I64Attr:$attr)>;
+
// CHECK-LABEL: CustomStringLiteralA::parse
// CHECK: parseFoo({{.*}}, parser.getBuilder().getI1Type())
// CHECK-LABEL: CustomStringLiteralA::print
@@ -140,6 +192,12 @@ def PropDictSegmentSizesRequired : TestFormat_Op<[{
}], [AttrSizedOperandSegments]>,
Arguments<(ins Variadic<I64>:$a1, Variadic<I64>:$a2)>;
+// CHECK-LABEL: PropDictStrictInferredSegmentAttr::setPropertiesFromParsedAttr
+// CHECK-NOT: segment_sizes
+// CHECK: return ::mlir::success();
+// CHECK-LABEL: PropDictStrictInferredSegmentAttr::print
+// CHECK: elidedProps.push_back("segment_sizes");
+
// CHECK-LABEL: RegionRef::parse
// CHECK: auto odsResult = parseCustom(parser, *bodyRegion);
// CHECK-LABEL: RegionRef::print
diff --git a/mlir/tools/mlir-tblgen/OpFormatGen.cpp b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
index 2ae276a62e939..548851503c445 100644
--- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp
+++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp
@@ -20,6 +20,7 @@
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/ADT/TypeSwitch.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
@@ -349,7 +350,10 @@ struct OperationFormat {
};
OperationFormat(const Operator &op, bool hasProperties)
- : useProperties(hasProperties), opCppClassName(op.getCppClassName()) {
+ : useProperties(hasProperties),
+ useStrictPropertiesInAssemblyFormat(
+ op.getDialect().useStrictPropertiesInAssemblyFormat()),
+ opCppClassName(op.getCppClassName()) {
operandTypes.resize(op.getNumOperands(), TypeResolution());
resultTypes.resize(op.getNumResults(), TypeResolution());
@@ -358,6 +362,11 @@ struct OperationFormat {
});
hasSingleBlockTrait = op.getTrait("::mlir::OpTrait::SingleBlock");
+
+ for (const NamedAttribute &attr : op.getAttributes()) {
+ if (!attr.attr.isDerivedAttr())
+ inherentAttrNames.push_back(attr.name);
+ }
}
/// Generate the operation parser from this format.
@@ -407,12 +416,18 @@ struct OperationFormat {
/// Indicate whether we need to use properties for the current operator.
bool useProperties;
+ /// Indicate whether the dialect uses strict properties in assembly formats.
+ bool useStrictPropertiesInAssemblyFormat;
+
/// Indicate whether prop-dict is used in the format
- bool hasPropDict;
+ bool hasPropDict = false;
/// The Operation class name
StringRef opCppClassName;
+ /// The names of inherent attributes for this operation.
+ SmallVector<StringRef> inherentAttrNames;
+
/// A map of buildable types to indices.
llvm::MapVector<StringRef, int, StringMap<int>> buildableTypes;
@@ -1383,6 +1398,8 @@ if (attr && ::mlir::failed(setFromAttr(prop.{1}, attr, [&]() {{
for (const NamedProperty &namedProperty : op.getProperties()) {
if (fmt.usedProperties.contains(&namedProperty))
continue;
+ if (fmt.inferredAttributes.contains(namedProperty.name))
+ continue;
auto scope = body.scope("{\n", "}\n", /*indent=*/true);
@@ -1402,6 +1419,8 @@ if (attr && ::mlir::failed(setFromAttr(prop.{1}, attr, [&]() {{
for (const NamedAttribute &namedAttr : op.getAttributes()) {
if (fmt.usedAttributes.contains(&namedAttr))
continue;
+ if (fmt.inferredAttributes.contains(namedAttr.name))
+ continue;
const Attribute &attr = namedAttr.attr;
// Derived attributes do not need to be parsed.
@@ -1677,13 +1696,20 @@ void OperationFormat::genElementParser(FormatElement *element, MethodBody &body,
<< (attrDict->isWithKeyword() ? "WithKeyword" : "")
<< "(result.attributes))\n"
<< " return ::mlir::failure();\n";
- if (useProperties) {
+ if (useProperties && !useStrictPropertiesInAssemblyFormat) {
body << "if (failed(verifyInherentAttrs(result.name, result.attributes, "
"[&]() {\n"
<< " return parser.emitError(loc) << \"'\" << "
"result.name.getStringRef() << \"' op \";\n"
<< " })))\n"
<< " return ::mlir::failure();\n";
+ } else if (useProperties) {
+ for (StringRef name : inherentAttrNames) {
+ body << "if (result.attributes.get(\"" << name << "\"))\n"
+ << " return parser.emitError(loc, \"inherent attribute '" << name
+ << "' cannot be parsed from attr-dict when strict properties in "
+ "assembly format is enabled\");\n";
+ }
}
body.unindent() << "}\n";
body.unindent();
@@ -2083,6 +2109,8 @@ static void genPropDictPrinter(OperationFormat &fmt, Operator &op,
for (const NamedProperty *namedProperty : fmt.usedProperties)
body << " elidedProps.push_back(\"" << namedProperty->name << "\");\n";
+ for (StringRef key : fmt.inferredAttributes.keys())
+ body << " elidedProps.push_back(\"" << key << "\");\n";
for (const NamedAttribute *namedAttr : fmt.usedAttributes)
body << " elidedProps.push_back(\"" << namedAttr->name << "\");\n";
@@ -2133,7 +2161,7 @@ static void genAttrDictPrinter(OperationFormat &fmt, Operator &op,
genVariadicSegmentElision(fmt, op, body, "elidedAttrs");
- for (const StringRef key : fmt.inferredAttributes.keys())
+ for (StringRef key : fmt.inferredAttributes.keys())
body << " elidedAttrs.push_back(\"" << key << "\");\n";
for (const NamedAttribute *attr : fmt.usedAttributes)
body << " elidedAttrs.push_back(\"" << attr->name << "\");\n";
@@ -2906,6 +2934,32 @@ LogicalResult OpFormatParser::verify(SMLoc loc,
failed(verifyOIListElements(loc, elements)))
return failure();
+ if (fmt.useProperties && fmt.useStrictPropertiesInAssemblyFormat &&
+ !hasPropDict) {
+ auto emitMissingError = [&](StringRef kind,
+ StringRef name) -> LogicalResult {
+ return emitError(loc,
+ llvm::Twine("strict properties in assembly format "
+ "requires prop-dict unless all inherent "
+ "attributes and properties are bound in "
+ "the custom assembly format; "
+ "missing ") +
+ kind + " '" + name + "'");
+ };
+ for (const NamedAttribute &attr : op.getAttributes()) {
+ if (attr.attr.isDerivedAttr())
+ continue;
+ if (fmt.inferredAttributes.contains(attr.name))
+ continue;
+ if (!seenAttrs.count(&attr))
+ return emitMissingError("attribute", attr.name);
+ }
+ for (const NamedProperty &prop : op.getProperties()) {
+ if (!seenProperties.count(&prop))
+ return emitMissingError("property", prop.name);
+ }
+ }
+
// Collect the set of used attributes in the format.
fmt.usedAttributes = std::move(seenAttrs);
fmt.usedProperties = std::move(seenProperties);
More information about the Mlir-commits
mailing list