[Mlir-commits] [mlir] 6d9cbf4 - [MLIR][ODS] Add strict property assembly format mode (#196269)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Aug 14 12:09:16 PDT 2026


Author: Mehdi Amini
Date: 2026-08-14T19:09:10Z
New Revision: 6d9cbf4b6c5b79928bf1d3ada10e67e06e4c3953

URL: https://github.com/llvm/llvm-project/commit/6d9cbf4b6c5b79928bf1d3ada10e67e06e4c3953
DIFF: https://github.com/llvm/llvm-project/commit/6d9cbf4b6c5b79928bf1d3ada10e67e06e4c3953.diff

LOG: [MLIR][ODS] Add strict property assembly format mode (#196269)

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

Added: 
    

Modified: 
    mlir/docs/DefiningDialects/Operations.md
    mlir/docs/DefiningDialects/_index.md
    mlir/include/mlir/Dialect/Affine/IR/AffineOps.td
    mlir/include/mlir/Dialect/ArmNeon/ArmNeon.td
    mlir/include/mlir/Dialect/ArmSME/IR/ArmSME.td
    mlir/include/mlir/Dialect/ArmSVE/IR/ArmSVE.td
    mlir/include/mlir/Dialect/Complex/IR/ComplexBase.td
    mlir/include/mlir/Dialect/DLTI/DLTIBase.td
    mlir/include/mlir/Dialect/IRDL/IR/IRDL.td
    mlir/include/mlir/Dialect/Index/IR/IndexDialect.td
    mlir/include/mlir/Dialect/LLVMIR/VCIXOps.td
    mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td
    mlir/include/mlir/Dialect/Linalg/IR/LinalgBase.td
    mlir/include/mlir/Dialect/MLProgram/IR/MLProgramBase.td
    mlir/include/mlir/Dialect/MPI/IR/MPI.td
    mlir/include/mlir/Dialect/Ptr/IR/PtrDialect.td
    mlir/include/mlir/Dialect/Quant/IR/QuantBase.td
    mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
    mlir/include/mlir/Dialect/SMT/IR/SMTDialect.td
    mlir/include/mlir/Dialect/Shard/IR/ShardBase.td
    mlir/include/mlir/Dialect/Tensor/IR/TensorBase.td
    mlir/include/mlir/Dialect/UB/IR/UBOps.td
    mlir/include/mlir/IR/DialectBase.td
    mlir/include/mlir/TableGen/Dialect.h
    mlir/lib/TableGen/Dialect.cpp
    mlir/test/mlir-tblgen/op-format-invalid.td
    mlir/test/mlir-tblgen/op-format.td
    mlir/tools/mlir-tblgen/OpFormatGen.cpp

Removed: 
    


################################################################################
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