https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/217589
Generate a key-value prop-dict printer that dispatches each field to its ODS printer while retaining attribute conversion for non-compositional default parsers. Keep operation-specific property printer hooks ahead of the generated implementation and preserve legacy input compatibility. See #155475 Assisted-by: Codex >From 7988edac7e207afc08aa3fa120b07dc40e1c10ec Mon Sep 17 00:00:00 2001 From: Mehdi Amini <[email protected]> Date: Wed, 19 Aug 2026 09:04:44 -0700 Subject: [PATCH] [MLIR][ODS] Print prop-dict fields with custom printers Generate a key-value prop-dict printer that dispatches each field to its ODS printer while retaining attribute conversion for non-compositional default parsers. Keep operation-specific property printer hooks ahead of the generated implementation and preserve legacy input compatibility. Assisted-by: Codex --- mlir/docs/DefiningDialects/Operations.md | 5 +- mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td | 2 + .../include/mlir/Dialect/XeGPU/IR/XeGPUOps.td | 2 + mlir/include/mlir/IR/OpBase.td | 5 + mlir/include/mlir/IR/OpDefinition.h | 22 ++- mlir/include/mlir/TableGen/Operator.h | 3 + mlir/lib/TableGen/Operator.cpp | 4 + .../memref-to-emitc-alloc-copy.mlir | 8 +- .../memref-to-emitc-alloc-dealloc.mlir | 16 +- .../memref-to-emitc-alloc-load-store.mlir | 8 +- .../MemRefToEmitC/memref-to-emitc-copy.mlir | 2 +- .../memref-with-custom-types.mlir | 3 +- .../Dialect/EmitC/member_call_opaque.mlir | 4 +- mlir/test/IR/enum-attr-roundtrip.mlir | 8 +- mlir/test/IR/properties.mlir | 38 +++-- mlir/test/Target/LLVMIR/Import/intrinsic.ll | 2 +- mlir/test/lib/Dialect/Test/TestOps.td | 28 ++++ .../op-format-custom-properties-printer.td | 23 +++ mlir/test/mlir-tblgen/op-format.mlir | 12 +- mlir/tools/mlir-tblgen/OpFormatGen.cpp | 138 +++++++++++++++++- 20 files changed, 278 insertions(+), 55 deletions(-) create mode 100644 mlir/test/mlir-tblgen/op-format-custom-properties-printer.td diff --git a/mlir/docs/DefiningDialects/Operations.md b/mlir/docs/DefiningDialects/Operations.md index 419041a52b0da..a6e119727c906 100644 --- a/mlir/docs/DefiningDialects/Operations.md +++ b/mlir/docs/DefiningDialects/Operations.md @@ -768,7 +768,10 @@ The available directives are as follows: `FieldParser` specialization is available or when the selected specialization declares `isKeyValueCompositional = false`. - The legacy `<{key = attribute, ...}>` dictionary spelling is also - accepted when parsing and is used by the generated printer. + accepted when parsing. The generated printer uses the key-value + spelling and the same custom-printer or attribute-conversion choice. + Operations that provide a custom `printProperties` hook should set + `hasCustomPropertiesPrinter` to suppress the shadowed generated helper. - Any property or inherent attribute that is not used elsewhere in the format is parsed and printed as part of this list. - If present, the `attr-dict` will not contain any inherent attributes. diff --git a/mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td b/mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td index c52a1c0c94402..f9c80235aa1ca 100644 --- a/mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/XeVMOps.td @@ -48,6 +48,8 @@ class XeVM_Attr<string attrName, string attrMnemonic, list<Trait> traits = []> class XeVM_Op<string mnemonic, list<Trait> traits = []> : LLVM_OpBase<XeVM_Dialect, mnemonic, traits> { + let hasCustomPropertiesPrinter = 1; + code extraBaseClassDeclaration = [{ void printProperties(::mlir::MLIRContext *ctx, ::mlir::OpAsmPrinter &p, const Properties &prop, diff --git a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td index 49b98922cee4c..b6b60b252eec8 100644 --- a/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td +++ b/mlir/include/mlir/Dialect/XeGPU/IR/XeGPUOps.td @@ -26,6 +26,8 @@ include "mlir/Interfaces/ViewLikeInterface.td" class XeGPU_Op<string mnemonic, list<Trait> traits = []>: Op<XeGPU_Dialect, mnemonic, traits> { + let hasCustomPropertiesPrinter = 1; + code extraBaseClassDeclaration = [{ void printProperties(::mlir::MLIRContext *ctx, ::mlir::OpAsmPrinter &p, const Properties &prop, diff --git a/mlir/include/mlir/IR/OpBase.td b/mlir/include/mlir/IR/OpBase.td index 0d0669e90c3f7..a130d5847a1c7 100644 --- a/mlir/include/mlir/IR/OpBase.td +++ b/mlir/include/mlir/IR/OpBase.td @@ -408,6 +408,11 @@ class Op<Dialect dialect, string mnemonic, list<Trait> props = []> { /// * void print(OpAsmPrinter &p) bit hasCustomAssemblyFormat = 0; + /// This field indicates that the operation provides a custom + /// `printProperties` hook. Setting it avoids generating the default + /// per-field `prop-dict` printer that the hook would shadow. + bit hasCustomPropertiesPrinter = 0; + // A bit indicating if the operation has additional invariants that need to // verified (aside from those verified by other ODS constructs). If set to `1`, // an additional `LogicalResult verify()` declaration will be generated on the diff --git a/mlir/include/mlir/IR/OpDefinition.h b/mlir/include/mlir/IR/OpDefinition.h index 075d39194ed97..d92a93746bf54 100644 --- a/mlir/include/mlir/IR/OpDefinition.h +++ b/mlir/include/mlir/IR/OpDefinition.h @@ -1864,6 +1864,18 @@ class Op : public OpState, public Traits<ConcreteType>... { using detect_has_print_properties = llvm::is_detected<has_print_properties, T>; + /// Trait to check if T provides a generated printer for the key-value + /// spelling of `prop-dict`. + template <typename T, typename... Args> + using has_print_properties_as_key_value_list = + decltype(T::_odsPrintPropertiesAsKeyValueList( + std::declval<MLIRContext *>(), std::declval<OpAsmPrinter &>(), + std::declval<const typename PropertiesSelector<T>::type &>(), + std::declval<ArrayRef<StringRef>>())); + template <typename T> + using detect_has_print_properties_as_key_value_list = + llvm::is_detected<has_print_properties_as_key_value_list, T>; + /// Trait to check if parseProperties(OpAsmParser, T) exist template <typename T, typename... Args> using has_parse_properties = decltype(parseProperties( @@ -2036,15 +2048,19 @@ class Op : public OpState, public Traits<ConcreteType>... { InferredProperties<T> &properties) {} /// Print the operation properties with names not included within - /// 'elidedProps'. Unless overridden, this method will try to dispatch to a - /// `printProperties` free-function if it exists, and otherwise by converting - /// the properties to an Attribute. + /// 'elidedProps'. Unless overridden, this method first tries to dispatch to a + /// `printProperties` free-function, then to the generated per-field printer, + /// and finally converts the properties to an Attribute. template <typename T> static void printProperties(MLIRContext *ctx, OpAsmPrinter &p, const T &properties, ArrayRef<StringRef> elidedProps = {}) { if constexpr (detect_has_print_properties<T>::value) return printProperties(p, properties, elidedProps); + if constexpr (detect_has_print_properties_as_key_value_list< + ConcreteType>::value) + return ConcreteType::_odsPrintPropertiesAsKeyValueList(ctx, p, properties, + elidedProps); genericPrintProperties( p, ConcreteType::getPropertiesAsAttr(ctx, properties), elidedProps); } diff --git a/mlir/include/mlir/TableGen/Operator.h b/mlir/include/mlir/TableGen/Operator.h index f0514d8e61748..4c0ba2a1db9ec 100644 --- a/mlir/include/mlir/TableGen/Operator.h +++ b/mlir/include/mlir/TableGen/Operator.h @@ -143,6 +143,9 @@ class Operator { /// Returns true if default builders should not be generated. bool skipDefaultBuilders() const; + /// Returns true if the operation provides a custom properties printer. + bool hasCustomPropertiesPrinter() const; + /// Op result iterators. const_value_iterator result_begin() const; const_value_iterator result_end() const; diff --git a/mlir/lib/TableGen/Operator.cpp b/mlir/lib/TableGen/Operator.cpp index 82dfbcbfa4d4f..148c3408b707d 100644 --- a/mlir/lib/TableGen/Operator.cpp +++ b/mlir/lib/TableGen/Operator.cpp @@ -186,6 +186,10 @@ bool Operator::skipDefaultBuilders() const { return def.getValueAsBit("skipDefaultBuilders"); } +bool Operator::hasCustomPropertiesPrinter() const { + return def.getValueAsBit("hasCustomPropertiesPrinter"); +} + auto Operator::result_begin() const -> const_value_iterator { return results.begin(); } diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-copy.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-copy.mlir index be0b9baf502bc..8f5b93bffe27e 100644 --- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-copy.mlir +++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-copy.mlir @@ -18,7 +18,7 @@ func.func @alloc_copy(%arg0: memref<999xi32>) { // CHECK-LABEL: func.func @alloc_copy( // CHECK-SAME: %[[ARG0:.*]]: memref<999xi32>) { // CHECK: %[[UNREALIZED_CONVERSION_CAST_0:.*]] = builtin.unrealized_conversion_cast %[[ARG0]] : memref<999xi32> to !emitc.array<999xi32> -// CHECK: %[[CALL_OPAQUE_0:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// CHECK: %[[CALL_OPAQUE_0:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CHECK: %[[VAL_0:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index // CHECK: %[[MUL_0:.*]] = emitc.mul %[[CALL_OPAQUE_0]], %[[VAL_0]] : (!emitc.size_t, index) -> !emitc.size_t // CHECK: %[[CALL_OPAQUE_1:.*]] = emitc.call_opaque "malloc"(%[[MUL_0]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> @@ -30,11 +30,11 @@ func.func @alloc_copy(%arg0: memref<999xi32>) { // CHECK: %[[VAL_2:.*]] = "emitc.constant"() <{value = 0 : index}> : () -> index // CHECK: %[[SUBSCRIPT_1:.*]] = emitc.subscript %[[UNREALIZED_CONVERSION_CAST_1]]{{\[}}%[[VAL_2]]] : (!emitc.array<999xi32>, index) -> !emitc.lvalue<i32> // CHECK: %[[ADDRESS_OF_1:.*]] = emitc.address_of %[[SUBSCRIPT_1]] : !emitc.lvalue<i32> -// CHECK: %[[CALL_OPAQUE_2:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// CHECK: %[[CALL_OPAQUE_2:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CHECK: %[[VAL_3:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index // CHECK: %[[MUL_1:.*]] = emitc.mul %[[CALL_OPAQUE_2]], %[[VAL_3]] : (!emitc.size_t, index) -> !emitc.size_t // CHECK: emitc.call_opaque "memcpy"(%[[ADDRESS_OF_1]], %[[ADDRESS_OF_0]], %[[MUL_1]]) : (!emitc.ptr<i32>, !emitc.ptr<i32>, !emitc.size_t) -> () -// CHECK: %[[CALL_OPAQUE_3:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// CHECK: %[[CALL_OPAQUE_3:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CHECK: %[[VAL_4:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index // CHECK: %[[MUL_2:.*]] = emitc.mul %[[CALL_OPAQUE_3]], %[[VAL_4]] : (!emitc.size_t, index) -> !emitc.size_t // CHECK: %[[CALL_OPAQUE_4:.*]] = emitc.call_opaque "malloc"(%[[MUL_2]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> @@ -46,7 +46,7 @@ func.func @alloc_copy(%arg0: memref<999xi32>) { // CHECK: %[[VAL_6:.*]] = "emitc.constant"() <{value = 0 : index}> : () -> index // CHECK: %[[SUBSCRIPT_3:.*]] = emitc.subscript %[[UNREALIZED_CONVERSION_CAST_2]]{{\[}}%[[VAL_6]]] : (!emitc.array<999xi32>, index) -> !emitc.lvalue<i32> // CHECK: %[[ADDRESS_OF_3:.*]] = emitc.address_of %[[SUBSCRIPT_3]] : !emitc.lvalue<i32> -// CHECK: %[[CALL_OPAQUE_5:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// CHECK: %[[CALL_OPAQUE_5:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CHECK: %[[VAL_7:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index // CHECK: %[[MUL_3:.*]] = emitc.mul %[[CALL_OPAQUE_5]], %[[VAL_7]] : (!emitc.size_t, index) -> !emitc.size_t // CHECK: emitc.call_opaque "memcpy"(%[[ADDRESS_OF_3]], %[[ADDRESS_OF_2]], %[[MUL_3]]) : (!emitc.ptr<i32>, !emitc.ptr<i32>, !emitc.size_t) -> () diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir index 3194a40c16eeb..ad2329b922c81 100644 --- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir +++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-dealloc.mlir @@ -15,7 +15,7 @@ func.func @alloc_and_dealloc() { // CPP: module { // CPP-NEXT: emitc.include <"cstdlib"> // CPP-LABEL: alloc_and_dealloc() -// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index // CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t // CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> @@ -27,7 +27,7 @@ func.func @alloc_and_dealloc() { // NOCPP: module { // NOCPP-NEXT: emitc.include <"stdlib.h"> // NOCPP-LABEL: alloc_and_dealloc() -// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index // NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t // NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> @@ -43,7 +43,7 @@ func.func @alloc_and_dealloc_aligned() { } // CPP-LABEL: alloc_and_dealloc_aligned -// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t +// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <args = [f32]> : () -> !emitc.size_t // CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index // CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t // CPP-NEXT: %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t @@ -54,7 +54,7 @@ func.func @alloc_and_dealloc_aligned() { // CPP-NEXT: return // NOCPP-LABEL: alloc_and_dealloc_aligned -// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t +// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <args = [f32]> : () -> !emitc.size_t // NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 999 : index}> : () -> index // NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t // NOCPP-NEXT: %[[ALIGNMENT:.*]] = "emitc.constant"() <{value = 64 : index}> : () -> !emitc.size_t @@ -71,7 +71,7 @@ func.func @allocating_and_deallocating_multi() { } // CPP-LABEL: allocating_and_deallocating_multi -// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index // CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t // CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void"> @@ -81,7 +81,7 @@ func.func @allocating_and_deallocating_multi() { // CPP-NEXT: return // NOCPP-LABEL: allocating_and_deallocating_multi -// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 6993 : index}> : () -> index // NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t // NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> @@ -97,7 +97,7 @@ func.func @alloc_and_dealloc_rank0() { } // CPP-LABEL: alloc_and_dealloc_rank0 -// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index // CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t // CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> @@ -107,7 +107,7 @@ func.func @alloc_and_dealloc_rank0() { // CPP-NEXT: return // NOCPP-LABEL: alloc_and_dealloc_rank0 -// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t +// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index // NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t // NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-load-store.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-load-store.mlir index 653220470bb5a..43c5733baccb3 100644 --- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-load-store.mlir +++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-alloc-load-store.mlir @@ -21,7 +21,7 @@ // CHECK-SAME: %[[ARG_J:.*]]: !emitc.size_t) func.func private @memref_alloc_store(%v : f32, %i: index, %j: index) { /// Allocation size computation - // CHECK: %[[SIZEOF_F32:.*]] = call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t + // CHECK: %[[SIZEOF_F32:.*]] = call_opaque "sizeof"() <args = [f32]> : () -> !emitc.size_t // CHECK: %[[NUM_ELEMS:.*]] = "emitc.constant"() <{value = 32 : index}> : () -> index // CHECK: %[[TOTAL_BYTES:.*]] = mul %[[SIZEOF_F32]], %[[NUM_ELEMS]] : (!emitc.size_t, index) -> !emitc.size_t /// Alloc @@ -42,7 +42,7 @@ func.func private @memref_alloc_store(%v : f32, %i: index, %j: index) { // CHECK-SAME: %[[ARG_I:.*]]: !emitc.size_t, // CHECK-SAME: %[[ARG_J:.*]]: !emitc.size_t) -> f32 func.func private @memref_alloc_load(%i: index, %j: index) -> f32 { - // CHECK: %[[SIZEOF_F32:.*]] = call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t + // CHECK: %[[SIZEOF_F32:.*]] = call_opaque "sizeof"() <args = [f32]> : () -> !emitc.size_t // CHECK: %[[NUM_ELEMS:.*]] = "emitc.constant"() <{value = 32 : index}> : () -> index // CHECK: %[[TOTAL_BYTES:.*]] = mul %[[SIZEOF_F32]], %[[NUM_ELEMS]] : (!emitc.size_t, index) -> !emitc.size_t // CHECK: %[[MALLOC_PTR:.*]] = call_opaque "malloc"(%[[TOTAL_BYTES]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> @@ -77,7 +77,7 @@ func.func @memref_load_store(%buff0: memref<2xf32>, // CHECK-LABEL: emitc.func private @memref_alloc_store_rank0( // CHECK-SAME: %[[VAL:.*]]: i32) func.func private @memref_alloc_store_rank0(%v : i32) { - // CHECK: %[[SIZEOF_I32:.*]] = call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t + // CHECK: %[[SIZEOF_I32:.*]] = call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CHECK: %[[NUM_ELEMS:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index // CHECK: %[[TOTAL_BYTES:.*]] = mul %[[SIZEOF_I32]], %[[NUM_ELEMS]] : (!emitc.size_t, index) -> !emitc.size_t // CHECK: %[[MALLOC_PTR:.*]] = call_opaque "malloc"(%[[TOTAL_BYTES]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> @@ -92,7 +92,7 @@ func.func private @memref_alloc_store_rank0(%v : i32) { // CHECK-LABEL: emitc.func private @memref_alloc_load_rank0() -> i32 func.func private @memref_alloc_load_rank0() -> i32 { - // CHECK: %[[SIZEOF_I32:.*]] = call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t + // CHECK: %[[SIZEOF_I32:.*]] = call_opaque "sizeof"() <args = [i32]> : () -> !emitc.size_t // CHECK: %[[NUM_ELEMS:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index // CHECK: %[[TOTAL_BYTES:.*]] = mul %[[SIZEOF_I32]], %[[NUM_ELEMS]] : (!emitc.size_t, index) -> !emitc.size_t // CHECK: %[[MALLOC_PTR:.*]] = call_opaque "malloc"(%[[TOTAL_BYTES]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">> diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-copy.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-copy.mlir index 6105521d9326d..828aa68ea9f1c 100644 --- a/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-copy.mlir +++ b/mlir/test/Conversion/MemRefToEmitC/memref-to-emitc-copy.mlir @@ -21,7 +21,7 @@ func.func @copying(%arg0 : memref<9x4x5x7xf32>, %arg1 : memref<9x4x5x7xf32>) { // CHECK: %[[VAL_1:.*]] = "emitc.constant"() <{value = 0 : index}> : () -> index // CHECK: %[[SUBSCRIPT_1:.*]] = emitc.subscript %[[UNREALIZED_CONVERSION_CAST_0]]{{\[}}%[[VAL_1]], %[[VAL_1]], %[[VAL_1]], %[[VAL_1]]] : (!emitc.array<9x4x5x7xf32>, index, index, index, index) -> !emitc.lvalue<f32> // CHECK: %[[ADDRESS_OF_1:.*]] = emitc.address_of %[[SUBSCRIPT_1]] : !emitc.lvalue<f32> -// CHECK: %[[CALL_OPAQUE_0:.*]] = emitc.call_opaque "sizeof"() <{args = [f32]}> : () -> !emitc.size_t +// CHECK: %[[CALL_OPAQUE_0:.*]] = emitc.call_opaque "sizeof"() <args = [f32]> : () -> !emitc.size_t // CHECK: %[[VAL_2:.*]] = "emitc.constant"() <{value = 1260 : index}> : () -> index // CHECK: %[[MUL_0:.*]] = emitc.mul %[[CALL_OPAQUE_0]], %[[VAL_2]] : (!emitc.size_t, index) -> !emitc.size_t // CHECK: emitc.call_opaque "memcpy"(%[[ADDRESS_OF_1]], %[[ADDRESS_OF_0]], %[[MUL_0]]) : (!emitc.ptr<f32>, !emitc.ptr<f32>, !emitc.size_t) -> () diff --git a/mlir/test/Conversion/MemRefToEmitC/memref-with-custom-types.mlir b/mlir/test/Conversion/MemRefToEmitC/memref-with-custom-types.mlir index 4cc874633d456..bdb41baa4f743 100644 --- a/mlir/test/Conversion/MemRefToEmitC/memref-with-custom-types.mlir +++ b/mlir/test/Conversion/MemRefToEmitC/memref-with-custom-types.mlir @@ -5,7 +5,7 @@ // CHECK-LABEL: emitc.func @alloc_with_custom_element_type() func.func @alloc_with_custom_element_type() { - // CHECK: call_opaque "sizeof"() <{args = [!emitc.opaque<"TestElementT">]}> : () -> !emitc.size_t + // CHECK: call_opaque "sizeof"() <args = [!emitc.opaque<"TestElementT">]> : () -> !emitc.size_t // CHECK: cast // CHECK-SAME: !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<!emitc.opaque<"TestElementT">> %0 = memref.alloc() : memref<10x!test.memref_element> @@ -43,4 +43,3 @@ func.func @load_with_custom_element_type(%i: index) -> !test.memref_element { %v = memref.load %alloc[%i] : memref<4x!test.memref_element> return %v : !test.memref_element } - diff --git a/mlir/test/Dialect/EmitC/member_call_opaque.mlir b/mlir/test/Dialect/EmitC/member_call_opaque.mlir index 5971d7826b270..bc4e0d55ddb48 100644 --- a/mlir/test/Dialect/EmitC/member_call_opaque.mlir +++ b/mlir/test/Dialect/EmitC/member_call_opaque.mlir @@ -19,14 +19,14 @@ func.func @member_call_template_args(%arg0 : !emitc.opaque<"MyClass">) { return } // CHECK-LABEL: func @member_call_template_args -// CHECK: emitc.member_call_opaque %arg0 "method"() <{template_args = [i32]}> : !emitc.opaque<"MyClass">, () -> i32 +// CHECK: emitc.member_call_opaque %arg0 "method"() <template_args = [i32]> : !emitc.opaque<"MyClass">, () -> i32 func.func @member_call_reorder(%arg0 : !emitc.opaque<"MyClass">, %arg1 : i32, %arg2 : i32) { %0 = emitc.member_call_opaque %arg0 "method" (%arg1, %arg2) <{args = [1 : index, 0 : index]}> : !emitc.opaque<"MyClass">, (i32, i32) -> i32 return } // CHECK-LABEL: func @member_call_reorder -// CHECK: emitc.member_call_opaque %arg0 "method"(%arg1, %arg2) <{args = [1 : index, 0 : index]}> : !emitc.opaque<"MyClass">, (i32, i32) -> i32 +// CHECK: emitc.member_call_opaque %arg0 "method"(%arg1, %arg2) <args = [1 : index, 0 : index]> : !emitc.opaque<"MyClass">, (i32, i32) -> i32 func.func @member_call_lvalue_arg(%arg0 : !emitc.opaque<"MyClass">, %arg1 : !emitc.lvalue<i32>) { %0 = emitc.member_call_opaque %arg0 "method" (%arg1) : !emitc.opaque<"MyClass">, (!emitc.lvalue<i32>) -> i32 diff --git a/mlir/test/IR/enum-attr-roundtrip.mlir b/mlir/test/IR/enum-attr-roundtrip.mlir index d02776e226c80..113b5f07ee6f3 100644 --- a/mlir/test/IR/enum-attr-roundtrip.mlir +++ b/mlir/test/IR/enum-attr-roundtrip.mlir @@ -44,14 +44,14 @@ func.func @test_enum_prop() -> () { // CHECK: test.op_with_enum_prop first "test.op_with_enum_prop"() <{value = 0 : i32}> {} : () -> () - // CHECK: test.op_with_enum_prop_attr_form <{value = 0 : i32}> + // CHECK: test.op_with_enum_prop_attr_form <value = first> test.op_with_enum_prop_attr_form <{value = 0 : i32}> - // CHECK: test.op_with_enum_prop_attr_form <{value = 1 : i32}> + // CHECK: test.op_with_enum_prop_attr_form <value = second> test.op_with_enum_prop_attr_form <{value = #test<enum second>}> - // CHECK: test.op_with_enum_prop_attr_form_always <{value = #test<enum first>}> + // CHECK: test.op_with_enum_prop_attr_form_always <value = first> test.op_with_enum_prop_attr_form_always <{value = #test<enum first>}> - // CHECK: test.op_with_enum_prop_attr_form_always <{value = #test<enum second>} + // CHECK: test.op_with_enum_prop_attr_form_always <value = second> test.op_with_enum_prop_attr_form_always <{value = #test<enum second>}> return diff --git a/mlir/test/IR/properties.mlir b/mlir/test/IR/properties.mlir index 64548e41dc111..de37707a79d4d 100644 --- a/mlir/test/IR/properties.mlir +++ b/mlir/test/IR/properties.mlir @@ -14,7 +14,7 @@ test.with_properties a = 32, b = "foo", c = "bar", flag = true, array = [1, 2, 3 test.with_nice_properties "foo bar" is -3 // CHECK: test.with_wrapped_properties -// CHECK-SAME: <{prop = "content for properties"}>{{$}} +// CHECK-SAME: <prop = "content for properties">{{$}} // GENERIC: "test.with_wrapped_properties"() // GENERIC-SAME: <{prop = "content for properties"}> : () -> () test.with_wrapped_properties <{prop = "content for properties"}> @@ -28,44 +28,50 @@ test.empty_properties // GENERIC: "test.empty_properties"() test.empty_properties <> -// The key-value spelling uses the custom parsers for both attributes and -// properties. Until the custom printer is enabled, it round-trips to the -// generic DictionaryAttr spelling. -// CHECK: test.with_custom_prop_dict <{attr = 1 : i32, prop = 2 : i64}> +// The key-value spelling uses the custom parsers and printers for both +// attributes and properties. +// CHECK: test.with_custom_prop_dict <prop = 2, attr = 1> // GENERIC: "test.with_custom_prop_dict"() // GENERIC-SAME: <{attr = 1 : i32, defaulted = 42 : i64, prop = 2 : i64, unit = false}> test.with_custom_prop_dict <attr = 1, prop = 2> // The generic DictionaryAttr spelling remains accepted for compatibility. -// CHECK: test.with_custom_prop_dict <{attr = 3 : i32, prop = 4 : i64}> +// CHECK: test.with_custom_prop_dict <prop = 4, attr = 3> // GENERIC: "test.with_custom_prop_dict"() // GENERIC-SAME: <{attr = 3 : i32, defaulted = 42 : i64, prop = 4 : i64, unit = false}> test.with_custom_prop_dict <{attr = 3 : i32, prop = 4 : i64}> // Entries are order-independent, and optional/default-valued entries use // their custom parsers when present. -// CHECK: test.with_custom_prop_dict <{attr = 5 : i32, defaulted = 43 : i64, optional = "set", prop = 6 : i64}> +// CHECK: test.with_custom_prop_dict <prop = 6, defaulted = 43, attr = 5, optional = "set"> // GENERIC: "test.with_custom_prop_dict"() // GENERIC-SAME: <{attr = 5 : i32, defaulted = 43 : i64, optional = "set", prop = 6 : i64, unit = false}> test.with_custom_prop_dict <optional = "set", defaulted = 43, prop = 6, attr = 5> // A field name that is also the start of an attribute must not be consumed by // the legacy DictionaryAttr compatibility probe. -// CHECK: test.with_custom_prop_dict <{attr = 7 : i32, prop = 8 : i64, unit}> +// CHECK: test.with_custom_prop_dict <prop = 8, unit = unit, attr = 7> // GENERIC: "test.with_custom_prop_dict"() // GENERIC-SAME: <{attr = 7 : i32, defaulted = 42 : i64, prop = 8 : i64, unit}> test.with_custom_prop_dict <unit = unit, attr = 7, prop = 8> +// Inherent attributes use their custom assembly printer in the key-value +// spelling. Optional enum attributes compile and are omitted when absent. +// CHECK: test.with_custom_attr_prop_dict <prop = 9, attr = first> +test.with_custom_attr_prop_dict <attr = first, prop = 9> +// CHECK: test.with_custom_attr_prop_dict <prop = 10, attr = first, optionalAttr = second> +test.with_custom_attr_prop_dict <optionalAttr = second, prop = 10, attr = first> + // Properties bound elsewhere in the assembly format are excluded from the // key-value list. -// CHECK: test.with_properties_and_attr 7 <{rhs = 8 : i64}> +// CHECK: test.with_properties_and_attr 7 <rhs = 8> // GENERIC: "test.with_properties_and_attr"() // GENERIC-SAME: <{lhs = 7 : i32, rhs = 8 : i64}> test.with_properties_and_attr 7 <rhs = 8> // A property without a usable custom parser falls back to its attribute // conversion for this compatibility spelling. -// CHECK: test.with_wrapped_properties <{prop = "custom spelling"}> +// CHECK: test.with_wrapped_properties <prop = "custom spelling"> // GENERIC: "test.with_wrapped_properties"() // GENERIC-SAME: <{prop = "custom spelling"}> test.with_wrapped_properties <prop = "custom spelling"> @@ -89,7 +95,8 @@ test.with_wrapped_array_properties <prop = ["first", "second"]> // following scalar key also checks that the container does not consume the // outer comma. // CHECK: test.with_key_value_parser_boundaries -// CHECK-SAME: <{maybe = [], maybeEnum = [], next = 9 : i64, specializedMaybe = [7 : i16], specializedValues = array<i32: 3, 4>, values = array<i64: 1, 2>}> +// CHECK-SAME: <values = array<i64: 1, 2>, maybe = [], maybeEnum = [], +// CHECK-SAME: specializedValues = [3, 4], specializedMaybe = some<7>, next = 9> // GENERIC: "test.with_key_value_parser_boundaries"() // GENERIC-SAME: <{maybe = [], maybeEnum = [], next = 9 : i64, specializedMaybe = [7 : i16], specializedValues = array<i32: 3, 4>, values = array<i64: 1, 2>}> test.with_key_value_parser_boundaries <specializedValues = [3, 4], specializedMaybe = some<7>, values = array<i64: 1, 2>, maybe = [], maybeEnum = [], next = 9> @@ -97,7 +104,7 @@ test.with_key_value_parser_boundaries <specializedValues = [3, 4], specializedMa // A comma-separated bit-enum FieldParser is not compositional with the outer // list, so prop-dict uses its attribute conversion before parsing another key. // CHECK: test.op_with_bit_enum_prop_dict -// CHECK-SAME: <{flags = 3 : i32, next = 9 : i64}> +// CHECK-SAME: <flags = 3 : i32, next = 9> // GENERIC: "test.op_with_bit_enum_prop_dict"() // GENERIC-SAME: <{flags = 3 : i32, next = 9 : i64}> test.op_with_bit_enum_prop_dict <flags = 3 : i32, next = 9> @@ -132,7 +139,7 @@ test.variadic_segment_prop %ci64, %ci64 : %ci64 : i64, i64 : i64 end // `<{...}>`. Without the parser-side fix, re-parsing the CHECK line below // (which is exactly what the printer emits) fails with "duplicate or unknown // key 'operandSegmentSizes' in dictionary attribute". -// CHECK: test.variadic_segment_prop_bulk_type(%[[CI64]], %[[CI64]], %[[CI64]]) : (i64, i64, i64) -> (i64, i64, i64) <{operandSegmentSizes = array<i32: 2, 1>, resultSegmentSizes = array<i32: 2, 1>}> +// CHECK: test.variadic_segment_prop_bulk_type(%[[CI64]], %[[CI64]], %[[CI64]]) : (i64, i64, i64) -> (i64, i64, i64) <operandSegmentSizes = [2, 1], resultSegmentSizes = [2, 1]> // GENERIC: "test.variadic_segment_prop_bulk_type"(%[[CI64]], %[[CI64]], %[[CI64]]) <{operandSegmentSizes = array<i32: 2, 1>, resultSegmentSizes = array<i32: 2, 1>}> : (i64, i64, i64) -> (i64, i64, i64) test.variadic_segment_prop_bulk_type(%ci64, %ci64, %ci64) : (i64, i64, i64) -> (i64, i64, i64) <operandSegmentSizes = [2, 1], resultSegmentSizes = [2, 1]> @@ -190,7 +197,8 @@ test.with_array_properties ints = [1, 2] strings = ["a", "b"] nested = [[1, 2], // Tests that DefaultValuedProp is elided from prop-dict when value equals default. // CHECK: test.op_with_property_predicates -// CHECK-SAME: <{array = [], more_constrained = 1 : i64, non_empty_constrained = [1], non_empty_unconstrained = [1], scalar = 1 : i64, unconstrained = 0 : i64}> +// CHECK-SAME: <scalar = 1, more_constrained = 1, array = [], +// CHECK-SAME: non_empty_unconstrained = [1], non_empty_constrained = [1], unconstrained = 0> // CHECK-NOT: defaulted test.op_with_property_predicates <{ scalar = 1 : i64, @@ -203,8 +211,8 @@ test.op_with_property_predicates <{ // Keyed parsing composes optional and aggregate property parsers with a // following outer dictionary entry. // CHECK: test.op_with_property_predicates +// CHECK-SAME: optional = 2 // CHECK-SAME: array = [3, 4] -// CHECK-SAME: optional = [2] test.op_with_property_predicates < scalar = 1, optional = 2, diff --git a/mlir/test/Target/LLVMIR/Import/intrinsic.ll b/mlir/test/Target/LLVMIR/Import/intrinsic.ll index 5bbb920e423c8..070ffa654129b 100644 --- a/mlir/test/Target/LLVMIR/Import/intrinsic.ll +++ b/mlir/test/Target/LLVMIR/Import/intrinsic.ll @@ -582,7 +582,7 @@ define void @trap_intrinsics() { call void @llvm.trap() ; CHECK: llvm.intr.debugtrap call void @llvm.debugtrap() - ; CHECK: llvm.intr.ubsantrap <{failureKind = 1 : i8}> + ; CHECK: llvm.intr.ubsantrap <failureKind = 1> call void @llvm.ubsantrap(i8 1) ret void } diff --git a/mlir/test/lib/Dialect/Test/TestOps.td b/mlir/test/lib/Dialect/Test/TestOps.td index 0bd7fe401c3c9..2908d743f0ece 100644 --- a/mlir/test/lib/Dialect/Test/TestOps.td +++ b/mlir/test/lib/Dialect/Test/TestOps.td @@ -3645,6 +3645,16 @@ def TestOpWithCustomPropDict : TEST_Op<"with_custom_prop_dict"> { ); } +def TestOpWithCustomAttrPropDict + : TEST_Op<"with_custom_attr_prop_dict"> { + let assemblyFormat = "prop-dict attr-dict"; + let arguments = (ins + TestEnumAttr:$attr, + OptionalAttr<TestEnumAttr>:$optionalAttr, + I64Prop:$prop + ); +} + def TestOpWithPropertiesAndInferredType : TEST_Op<"with_properties_and_inferred_type", [ DeclareOpInterfaceMethods<InferTypeOpInterface> @@ -3842,6 +3852,13 @@ def KeyValueOptionalEnumProperty def KeyValueSpecializedListProperty : Property<"::llvm::SmallVector<int32_t>"> { + let printer = [{ + [&]() { + $_printer << "["; + ::llvm::interleaveComma($_storage, $_printer); + $_printer << "]"; + }() + }]; let convertToAttribute = "return ::mlir::DenseI32ArrayAttr::get($_ctxt, $_storage);"; let convertFromAttribute = [{ @@ -3860,6 +3877,15 @@ def KeyValueSpecializedListProperty def KeyValueSpecializedOptionalProperty : Property<"std::optional<int16_t>"> { + let printer = [{ + [&]() { + if (!$_storage) { + $_printer << "none"; + return; + } + $_printer << "some<" << *$_storage << ">"; + }() + }]; let convertToAttribute = [{ if (!$_storage) return ::mlir::ArrayAttr::get($_ctxt, {}); @@ -4005,6 +4031,7 @@ def PropertiesWithCustomPrint : Property<"PropertiesWithCustomPrint"> { } def TestOpWithNiceProperties : TEST_Op<"with_nice_properties"> { + let hasCustomPropertiesPrinter = 1; let assemblyFormat = "prop-dict attr-dict"; let arguments = (ins PropertiesWithCustomPrint:$prop @@ -4069,6 +4096,7 @@ def VersionedProperties : Property<"VersionedProperties"> { } def TestOpWithVersionedProperties : TEST_Op<"with_versioned_properties"> { + let hasCustomPropertiesPrinter = 1; let assemblyFormat = "prop-dict attr-dict"; let arguments = (ins VersionedProperties:$prop diff --git a/mlir/test/mlir-tblgen/op-format-custom-properties-printer.td b/mlir/test/mlir-tblgen/op-format-custom-properties-printer.td new file mode 100644 index 0000000000000..18743e41feb64 --- /dev/null +++ b/mlir/test/mlir-tblgen/op-format-custom-properties-printer.td @@ -0,0 +1,23 @@ +// RUN: mlir-tblgen -gen-op-defs -I %S/../../include %s | FileCheck %s + +include "mlir/IR/OpBase.td" + +def TestDialect : Dialect { + let name = "test"; + let cppNamespace = "::test"; +} + +// A custom properties printer shadows the generated per-field helper. +// CHECK-NOT: CustomPropertiesPrinterOp::_odsPrintPropertiesAsKeyValueList +// CHECK: void CustomPropertiesPrinterOp::print +// CHECK-NOT: CustomPropertiesPrinterOp::_odsPrintPropertiesAsKeyValueList +def CustomPropertiesPrinterOp : Op<TestDialect, "custom_properties_printer"> { + let arguments = (ins I64Attr:$attr); + let assemblyFormat = "prop-dict attr-dict"; + let hasCustomPropertiesPrinter = 1; + let extraClassDeclaration = [{ + void printProperties(::mlir::MLIRContext *, ::mlir::OpAsmPrinter &, + const Properties &, + ::mlir::ArrayRef<::llvm::StringRef>); + }]; +} diff --git a/mlir/test/mlir-tblgen/op-format.mlir b/mlir/test/mlir-tblgen/op-format.mlir index 5f44e5cffe675..315b4e57beea0 100644 --- a/mlir/test/mlir-tblgen/op-format.mlir +++ b/mlir/test/mlir-tblgen/op-format.mlir @@ -297,13 +297,13 @@ test.format_optional_prop_dict <{a = [], b = 1 : i32}> // CHECK: test.format_optional_prop_dict {{$}} test.format_optional_prop_dict <{}> -// CHECK: test.format_optional_prop_dict <{a = ["foo"]}> +// CHECK: test.format_optional_prop_dict <a = "foo"> test.format_optional_prop_dict <{a = ["foo"]}> -// CHECK: test.format_optional_prop_dict <{b = 2 : i32}> +// CHECK: test.format_optional_prop_dict <b = 2> test.format_optional_prop_dict <{b = 2 : i32}> -// CHECK: test.format_optional_prop_dict <{a = ["foo"], b = 2 : i32}> +// CHECK: test.format_optional_prop_dict <a = "foo", b = 2> test.format_optional_prop_dict <{a = ["foo"], b = 2 : i32}> //===----------------------------------------------------------------------===// @@ -532,15 +532,15 @@ test.format_optional_operand_type(%i64) : i64 // CHECK: test.format_infer_type_variadic_operands(%[[I32]], %[[I32]] : i32, i32) (%[[I64]], %[[I64]] : i64, i64) %ignored_res13:4 = test.format_infer_type_variadic_operands(%i32, %i32 : i32, i32) (%i64, %i64 : i64, i64) -// CHECK: test.with_properties_and_attr 16 <{rhs = 16 : i64}> +// CHECK: test.with_properties_and_attr 16 <rhs = 16> test.with_properties_and_attr 16 <{rhs = 16 : i64}> -// CHECK: test.with_properties_and_inferred_type 16 <{packed, rhs = 16 : i64}> +// CHECK: test.with_properties_and_inferred_type 16 <rhs = 16, packed = unit> %should_be_i32 = test.with_properties_and_inferred_type 16 <{packed, rhs = 16 : i64}> // Assert through the verifier that its inferred as i32. test.format_all_types_match_var %should_be_i32, %i32 : i32 -// CHECK: test.using_property_in_custom_and_other [1, 4, 20] <{other = 16 : i64}> +// CHECK: test.using_property_in_custom_and_other [1, 4, 20] <other = 16> test.using_property_in_custom_and_other [1, 4, 20] <{other = 16 : i64}> //===----------------------------------------------------------------------===// diff --git a/mlir/tools/mlir-tblgen/OpFormatGen.cpp b/mlir/tools/mlir-tblgen/OpFormatGen.cpp index 815c51c91dddd..e503482ba5b71 100644 --- a/mlir/tools/mlir-tblgen/OpFormatGen.cpp +++ b/mlir/tools/mlir-tblgen/OpFormatGen.cpp @@ -2297,7 +2297,7 @@ static const char *regionSingleBlockImplicitTerminatorPrinterCode = R"( /// {1}: The name of the enum attributes symbolToString function. static const char *enumAttrBeginPrinterCode = R"( { - auto caseValue = {0}(); + auto caseValue = {0}; auto caseValueStr = {1}(caseValue); )"; @@ -2348,6 +2348,129 @@ static void genVariadicSegmentElision(OperationFormat &fmt, Operator &op, body << " " << elidedStorage << ".push_back(\"resultSegmentSizes\");\n"; } +static void genEnumAttrPrinter(const NamedAttribute *var, const Operator &op, + MethodBody &body, StringRef valueExpression); + +/// Generate the key-value printer used by the default `prop-dict` printer. +static void genKeyValuePropDictPrinter(OperationFormat &fmt, Operator &op, + OpClass &opClass) { + if (!fmt.hasPropDict || !fmt.useProperties || op.hasCustomPropertiesPrinter()) + return; + + bool hasPrintableField = + !op.getProperties().empty() || + llvm::any_of( + op.getAttributes(), + [](const auto &attr) { return !attr.attr.isDerivedAttr(); }) || + (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments") && + fmt.allOperands) || + (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments") && + fmt.allResultTypes); + if (!hasPrintableField) + return; + + SmallVector<MethodParameter> paramList; + paramList.emplace_back("::mlir::MLIRContext *", "_odsContext"); + paramList.emplace_back("::mlir::OpAsmPrinter &", "_odsPrinter"); + paramList.emplace_back("const Properties &", "prop"); + paramList.emplace_back("::mlir::ArrayRef<::llvm::StringRef>", "elidedProps"); + Method *method = opClass.addStaticMethod( + "void", "_odsPrintPropertiesAsKeyValueList", std::move(paramList)); + MethodBody &body = method->body().indent(); + + body << R"decl( +bool first = true; +auto printKey = [&](::llvm::StringRef name) { + _odsPrinter << (first ? "<" : ", ") << name << " = "; + first = false; +}; +auto shouldPrint = [&](::llvm::StringRef name) { + return !::llvm::is_contained(elidedProps, name); +}; +)decl"; + + auto genSegmentSizesPrinter = [&](StringRef name) { + body << "if (shouldPrint(\"" << name << "\")) {\n" + << " printKey(\"" << name << "\");\n" + << " _odsPrinter << \"[\";\n" + << " ::llvm::interleaveComma(prop." << name << ", _odsPrinter);\n" + << " _odsPrinter << \"]\";\n" + << "}\n"; + }; + if (op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments") && + fmt.allOperands) + genSegmentSizesPrinter("operandSegmentSizes"); + if (op.getTrait("::mlir::OpTrait::AttrSizedResultSegments") && + fmt.allResultTypes) + genSegmentSizesPrinter("resultSegmentSizes"); + + for (const NamedProperty &namedProperty : op.getProperties()) { + const Property &property = namedProperty.prop; + body << "if (shouldPrint(\"" << namedProperty.name << "\")) {\n" + << " printKey(\"" << namedProperty.name << "\");\n"; + FmtContext printerContext; + printerContext.addSubst("_printer", "_odsPrinter"); + printerContext.addSubst("_ctxt", "_odsContext"); + printerContext.addSubst("_storage", "prop." + namedProperty.name); + if (property.usesDefaultParser()) { + body << " if constexpr (::mlir::detail::HasKeyValueFieldParser<" + "std::remove_cv_t<std::remove_reference_t<decltype(prop." + << namedProperty.name << ")>>>::value) {\n" + << " " << tgfmt(property.getPrinterCall(), &printerContext) + << ";\n" + << " } else {\n" + << " auto propertyAttr = [&]() -> ::mlir::Attribute {\n"; + FmtContext conversionContext; + conversionContext.addSubst("_ctxt", "_odsContext"); + conversionContext.addSubst("_storage", "prop." + namedProperty.name); + body << tgfmt(property.getConvertToAttributeCall(), &conversionContext) + << "\n" + << " }();\n" + << " _odsPrinter.printAttribute(propertyAttr);\n" + << " }\n"; + } else { + body << " " << tgfmt(property.getPrinterCall(), &printerContext) + << ";\n"; + } + body << "}\n"; + } + + for (const NamedAttribute &namedAttr : op.getAttributes()) { + if (namedAttr.attr.isDerivedAttr()) + continue; + StringRef name = namedAttr.name; + body << "if (shouldPrint(\"" << name << "\")"; + if (namedAttr.attr.isOptional()) + body << " && prop." << name; + body << ") {\n" + << " printKey(\"" << name << "\");\n"; + + if (canFormatEnumAttr(&namedAttr)) { + FmtContext conversionContext; + conversionContext.withSelf("prop." + name); + std::string valueExpression = std::string(tgfmt( + namedAttr.attr.getConvertFromStorageCall(), &conversionContext)); + genEnumAttrPrinter(&namedAttr, op, body, valueExpression); + } else if (shouldFormatSymbolNameAttr(&namedAttr)) { + body << " _odsPrinter.printSymbolName(prop." << name + << ".getValue());\n"; + } else { + AttributeVariable attrVariable(&namedAttr); + if (attrVariable.getTypeBuilder()) + body << " _odsPrinter.printAttributeWithoutType(prop." << name + << ");\n"; + else if (attrVariable.shouldBeQualified() || + namedAttr.attr.getStorageType() == "::mlir::Attribute") + body << " _odsPrinter.printAttribute(prop." << name << ");\n"; + else + body << " _odsPrinter.printStrippedAttrOrType(prop." << name << ");\n"; + } + body << "}\n"; + } + body << "if (!first)\n" + " _odsPrinter << \">\";\n"; +} + /// Generate the printer for the 'prop-dict' directive. static void genPropDictPrinter(OperationFormat &fmt, Operator &op, MethodBody &body) { @@ -2586,15 +2709,20 @@ static MethodBody &genTypeOperandPrinter(FormatElement *arg, const Operator &op, /// Generate the printer for an enum attribute. static void genEnumAttrPrinter(const NamedAttribute *var, const Operator &op, - MethodBody &body) { + MethodBody &body, + StringRef valueExpression = {}) { Attribute baseAttr = var->attr.getBaseAttr(); const EnumInfo enumInfo(getEnumInfoRecord(baseAttr)); std::vector<EnumCase> cases = enumInfo.getAllCases(); bool dereferenceGetter = var->attr.isOptional() && !var->attr.hasDefaultValue(); - body << formatv(enumAttrBeginPrinterCode, - (dereferenceGetter ? "*" : "") + op.getGetterName(var->name), + std::string caseValue = valueExpression.empty() + ? op.getGetterName(var->name) + "()" + : valueExpression.str(); + if (dereferenceGetter) + caseValue = "*(" + caseValue + ")"; + body << formatv(enumAttrBeginPrinterCode, caseValue, enumInfo.getSymbolToStringFnName()); // Get a string containing all of the cases that can't be represented with a @@ -2990,6 +3118,8 @@ void OperationFormat::genPrinter(Operator &op, OpClass &opClass) { bool shouldEmitSpace = true, lastWasPunctuation = false; for (FormatElement *element : elements) genElementPrinter(element, body, op, shouldEmitSpace, lastWasPunctuation); + + genKeyValuePropDictPrinter(*this, op, opClass); } //===----------------------------------------------------------------------===// _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
