[mlir][ods] Do not print default-valued attributes when the value is equal to the default
This diff causes the `tblgen`-erated print() function to skip printing a `DefaultValuedAttr` attribute when the value is equal to the default. This feature will reduce the amount of custom printing code that needs to be written by users a relatively common scenario. As a motivating example, for the fastmath flags in the LLVMIR dialect, we would prefer to print this: ``` %0 = llvm.fadd %arg0, %arg1 : f32 ``` instead of this: ``` %0 = llvm.fadd %arg0, %arg1 {fastmathFlags = #llvm.fastmath<none>} : f32 ``` This diff makes the handling of print functionality for default-valued attributes standard. This is an updated version of https://reviews.llvm.org/D135398, without the per-attribute bit to control printing. Reviewed By: Mogball Differential Revision: https://reviews.llvm.org/D135993
This commit is contained in:
parent
2b32e4f98b
commit
f6ee194b68
|
@ -273,6 +273,9 @@ C++ default value. For example, a float default value should be specified as
|
|||
like `"0.5f"`, and an integer array default value should be specified as like
|
||||
`"{1, 2, 3}"`.
|
||||
|
||||
The generated operation printing function will not print default-valued
|
||||
attributes when the attribute value is equal to the default.
|
||||
|
||||
#### Confining attributes
|
||||
|
||||
`ConfinedAttr` is provided as a general mechanism to help modelling further
|
||||
|
|
|
@ -6,56 +6,56 @@
|
|||
|
||||
// CHECK-LABEL: func @raw_buffer_load_f32_from_rank_1
|
||||
func.func @raw_buffer_load_f32_from_rank_1(%src : memref<128xf32>, %offset : i32, %idx0 : i32) -> f32 {
|
||||
// CHECK: amdgpu.raw_buffer_load {boundsCheck = true, indexOffset = 1 : i32} %{{.*}}[{{.*}}] sgprOffset %{{.*}} : memref<128xf32>, i32 -> f32
|
||||
// CHECK: amdgpu.raw_buffer_load {indexOffset = 1 : i32} %{{.*}}[{{.*}}] sgprOffset %{{.*}} : memref<128xf32>, i32 -> f32
|
||||
%0 = amdgpu.raw_buffer_load {boundsCheck = true, indexOffset = 1 : i32} %src[%idx0] sgprOffset %offset : memref<128xf32>, i32 -> f32
|
||||
func.return %0 : f32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @raw_buffer_load_f32_from_rank_4
|
||||
func.func @raw_buffer_load_f32_from_rank_4(%src : memref<128x64x32x16xf32>, %offset : i32, %idx0 : i32, %idx1 : i32, %idx2 : i32, %idx3 : i32) -> f32 {
|
||||
// CHECK: amdgpu.raw_buffer_load {boundsCheck = true, indexOffset = 1 : i32} %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : memref<128x64x32x16xf32>, i32, i32, i32, i32 -> f32
|
||||
// CHECK: amdgpu.raw_buffer_load {indexOffset = 1 : i32} %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : memref<128x64x32x16xf32>, i32, i32, i32, i32 -> f32
|
||||
%0 = amdgpu.raw_buffer_load {boundsCheck = true, indexOffset = 1 : i32} %src[%idx0, %idx1, %idx2, %idx3] sgprOffset %offset : memref<128x64x32x16xf32>, i32, i32, i32, i32 -> f32
|
||||
func.return %0 : f32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @raw_buffer_load_4xf32_from_rank_4
|
||||
func.func @raw_buffer_load_4xf32_from_rank_4(%src : memref<128x64x32x16xf32>, %offset : i32, %idx0 : i32, %idx1 : i32, %idx2 : i32, %idx3 : i32) -> vector<4xf32> {
|
||||
// CHECK: amdgpu.raw_buffer_load {boundsCheck = true, indexOffset = 1 : i32} %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : memref<128x64x32x16xf32>, i32, i32, i32, i32 -> vector<4xf32>
|
||||
// CHECK: amdgpu.raw_buffer_load {indexOffset = 1 : i32} %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : memref<128x64x32x16xf32>, i32, i32, i32, i32 -> vector<4xf32>
|
||||
%0 = amdgpu.raw_buffer_load {boundsCheck = true, indexOffset = 1 : i32} %src[%idx0, %idx1, %idx2, %idx3] sgprOffset %offset : memref<128x64x32x16xf32>, i32, i32, i32, i32 -> vector<4xf32>
|
||||
func.return %0 : vector<4xf32>
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @raw_buffer_store_f32_to_rank_1
|
||||
func.func @raw_buffer_store_f32_to_rank_1(%value : f32, %dst : memref<128xf32>, %offset : i32, %idx0 : i32) {
|
||||
// CHECK: amdgpu.raw_buffer_store {boundsCheck = true, indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[{{.*}}] sgprOffset %{{.*}} : f32 -> memref<128xf32>, i32
|
||||
// CHECK: amdgpu.raw_buffer_store {indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[{{.*}}] sgprOffset %{{.*}} : f32 -> memref<128xf32>, i32
|
||||
amdgpu.raw_buffer_store {boundsCheck = true, indexOffset = 1 : i32} %value -> %dst[%idx0] sgprOffset %offset : f32 -> memref<128xf32>, i32
|
||||
func.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @raw_buffer_store_f32_to_rank_4
|
||||
func.func @raw_buffer_store_f32_to_rank_4(%value : f32, %dst : memref<128x64x32x16xf32>, %offset : i32, %idx0 : i32, %idx1 : i32, %idx2 : i32, %idx3 : i32) {
|
||||
// CHECK: amdgpu.raw_buffer_store {boundsCheck = true, indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : f32 -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
// CHECK: amdgpu.raw_buffer_store {indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : f32 -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
amdgpu.raw_buffer_store {boundsCheck = true, indexOffset = 1 : i32} %value -> %dst[%idx0, %idx1, %idx2, %idx3] sgprOffset %offset : f32 -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
func.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @raw_buffer_store_4xf32_to_rank_4
|
||||
func.func @raw_buffer_store_4xf32_to_rank_4(%value : vector<4xf32>, %dst : memref<128x64x32x16xf32>, %offset : i32, %idx0 : i32, %idx1 : i32, %idx2 : i32, %idx3 : i32) {
|
||||
// CHECK: amdgpu.raw_buffer_store {boundsCheck = true, indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : vector<4xf32> -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
// CHECK: amdgpu.raw_buffer_store {indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : vector<4xf32> -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
amdgpu.raw_buffer_store {boundsCheck = true, indexOffset = 1 : i32} %value -> %dst[%idx0, %idx1, %idx2, %idx3] sgprOffset %offset : vector<4xf32> -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
func.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @raw_buffer_atomic_fadd_f32_to_rank_1
|
||||
func.func @raw_buffer_atomic_fadd_f32_to_rank_1(%value : f32, %dst : memref<128xf32>, %offset : i32, %idx0 : i32) {
|
||||
// CHECK: amdgpu.raw_buffer_atomic_fadd {boundsCheck = true, indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[{{.*}}] sgprOffset %{{.*}} : f32 -> memref<128xf32>, i32
|
||||
// CHECK: amdgpu.raw_buffer_atomic_fadd {indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[{{.*}}] sgprOffset %{{.*}} : f32 -> memref<128xf32>, i32
|
||||
amdgpu.raw_buffer_atomic_fadd {boundsCheck = true, indexOffset = 1 : i32} %value -> %dst[%idx0] sgprOffset %offset : f32 -> memref<128xf32>, i32
|
||||
func.return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: func @raw_buffer_atomic_fadd_f32_to_rank_4
|
||||
func.func @raw_buffer_atomic_fadd_f32_to_rank_4(%value : f32, %dst : memref<128x64x32x16xf32>, %offset : i32, %idx0 : i32, %idx1 : i32, %idx2 : i32, %idx3 : i32) {
|
||||
// CHECK: amdgpu.raw_buffer_atomic_fadd {boundsCheck = true, indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : f32 -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
// CHECK: amdgpu.raw_buffer_atomic_fadd {indexOffset = 1 : i32} %{{.*}} -> %{{.*}}[%{{.*}}, %{{.*}}, %{{.*}}] sgprOffset %{{.*}} : f32 -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
amdgpu.raw_buffer_atomic_fadd {boundsCheck = true, indexOffset = 1 : i32} %value -> %dst[%idx0, %idx1, %idx2, %idx3] sgprOffset %offset : f32 -> memref<128x64x32x16xf32>, i32, i32, i32, i32
|
||||
func.return
|
||||
}
|
||||
|
|
|
@ -786,3 +786,20 @@ func.func @wrong_shape_fail() {
|
|||
} : () -> ()
|
||||
return
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Test DefaultValuedAttr Printing
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// CHECK-LABEL: @default_value_printing
|
||||
func.func @default_value_printing(%arg0 : i32) {
|
||||
// The attribute SHOULD NOT be printed because it is equal to the default
|
||||
// CHECK: test.default_value_print %arg0
|
||||
"test.default_value_print"(%arg0) {"value_with_default" = 0 : i32} : (i32) -> ()
|
||||
// The attribute SHOULD be printed because it is not equal to the default
|
||||
// CHECK: test.default_value_print {value_with_default = 1 : i32} %arg0
|
||||
"test.default_value_print"(%arg0) {"value_with_default" = 1 : i32} : (i32) -> ()
|
||||
return
|
||||
}
|
||||
|
|
|
@ -2919,6 +2919,16 @@ def TestDefaultStrAttrHasValueOp : TEST_Op<"has_str_value"> {
|
|||
def : Pat<(TestDefaultStrAttrNoValueOp $value),
|
||||
(TestDefaultStrAttrHasValueOp ConstantStrAttr<StrAttr, "foo">)>;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Test Ops with Default-Valued Attributes and Differing Print Settings
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
def TestDefaultAttrPrintOp : TEST_Op<"default_value_print"> {
|
||||
let arguments = (ins DefaultValuedAttr<I32Attr, "0">:$value_with_default,
|
||||
I32:$operand);
|
||||
let assemblyFormat = "attr-dict $operand";
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Test Ops with effects
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -61,7 +61,7 @@ def loopPipeline():
|
|||
# CHECK-LABEL: TEST: loopPipeline
|
||||
# CHECK: = transform.loop.pipeline %
|
||||
# CHECK-DAG: iteration_interval = 3
|
||||
# CHECK-DAG: read_latency = 10
|
||||
# (read_latency has default value and is not printed)
|
||||
|
||||
|
||||
@run
|
||||
|
|
|
@ -84,9 +84,7 @@ def testPad():
|
|||
# CHECK-DAG: padding_values = [4.200000e+01 : f32]
|
||||
# CHECK-DAG: padding_dimensions = [1]
|
||||
# CHECK-DAG: transpose_paddings = {{\[}}[1, 0]]
|
||||
# CHECK-DAG: hoist_paddings = []
|
||||
# CHECK-DAG: pack_paddings = []
|
||||
|
||||
# (hoist_paddings and pack_paddings have default values)
|
||||
|
||||
@run
|
||||
def testScalarize():
|
||||
|
|
|
@ -1644,24 +1644,39 @@ const char *enumAttrBeginPrinterCode = R"(
|
|||
/// Generate the printer for the 'attr-dict' directive.
|
||||
static void genAttrDictPrinter(OperationFormat &fmt, Operator &op,
|
||||
MethodBody &body, bool withKeyword) {
|
||||
body << " _odsPrinter.printOptionalAttrDict"
|
||||
<< (withKeyword ? "WithKeyword" : "")
|
||||
<< "((*this)->getAttrs(), /*elidedAttrs=*/{";
|
||||
body << " ::llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs;\n";
|
||||
// Elide the variadic segment size attributes if necessary.
|
||||
if (!fmt.allOperands &&
|
||||
op.getTrait("::mlir::OpTrait::AttrSizedOperandSegments"))
|
||||
body << "\"operand_segment_sizes\", ";
|
||||
body << " elidedAttrs.push_back(\"operand_segment_sizes\");\n";
|
||||
if (!fmt.allResultTypes &&
|
||||
op.getTrait("::mlir::OpTrait::AttrSizedResultSegments"))
|
||||
body << "\"result_segment_sizes\", ";
|
||||
if (!fmt.inferredAttributes.empty()) {
|
||||
for (const auto &attr : fmt.inferredAttributes)
|
||||
body << "\"" << attr.getKey() << "\", ";
|
||||
body << " elidedAttrs.push_back(\"result_segment_sizes\");\n";
|
||||
for (const StringRef key : fmt.inferredAttributes.keys())
|
||||
body << " elidedAttrs.push_back(\"" << key << "\");\n";
|
||||
for (const NamedAttribute *attr : fmt.usedAttributes)
|
||||
body << " elidedAttrs.push_back(\"" << attr->name << "\");\n";
|
||||
// Add code to check attributes for equality with the default value
|
||||
// for attributes with the elidePrintingDefaultValue bit set.
|
||||
for (const NamedAttribute &namedAttr : op.getAttributes()) {
|
||||
const Attribute &attr = namedAttr.attr;
|
||||
if (!attr.isDerivedAttr() && attr.hasDefaultValue()) {
|
||||
const StringRef &name = namedAttr.name;
|
||||
FmtContext fctx;
|
||||
fctx.withBuilder("odsBuilder");
|
||||
std::string defaultValue = std::string(
|
||||
tgfmt(attr.getConstBuilderTemplate(), &fctx, attr.getDefaultValue()));
|
||||
body << " {\n";
|
||||
body << " ::mlir::Builder odsBuilder(getContext());\n";
|
||||
body << " Attribute attr = " << op.getGetterName(name) << "Attr();\n";
|
||||
body << " if(attr && (attr == " << defaultValue << "))\n";
|
||||
body << " elidedAttrs.push_back(\"" << name << "\");\n";
|
||||
body << " }\n";
|
||||
}
|
||||
}
|
||||
llvm::interleaveComma(
|
||||
fmt.usedAttributes, body,
|
||||
[&](const NamedAttribute *attr) { body << "\"" << attr->name << "\""; });
|
||||
body << "});\n";
|
||||
body << " _odsPrinter.printOptionalAttrDict"
|
||||
<< (withKeyword ? "WithKeyword" : "")
|
||||
<< "((*this)->getAttrs(), elidedAttrs);\n";
|
||||
}
|
||||
|
||||
/// Generate the printer for a literal value. `shouldEmitSpace` is true if a
|
||||
|
|
Loading…
Reference in New Issue