Files
clang-p2996/mlir/test/Dialect/MemRef/transform-ops.mlir
Oleksandr "Alex" Zinenko e4384149b5 [mlir] use transform-interpreter in test passes (#70040)
Update most test passes to use the transform-interpreter pass instead of
the test-transform-dialect-interpreter-pass. The new "main" interpreter
pass has a named entry point instead of looking up the top-level op with
`PossibleTopLevelOpTrait`, which is arguably a more understandable
interface. The change is mechanical, rewriting an unnamed sequence into
a named one and wrapping the transform IR in to a module when necessary.

Add an option to the transform-interpreter pass to target a tagged
payload op instead of the root anchor op, which is also useful for repro
generation.

Only the test in the transform dialect proper and the examples have not
been updated yet. These will be updated separately after a more careful
consideration of testing coverage of the transform interpreter logic.
2023-10-24 16:12:34 +02:00

376 lines
16 KiB
MLIR

// RUN: mlir-opt %s -transform-interpreter -verify-diagnostics -allow-unregistered-dialect -split-input-file | FileCheck %s
// CHECK-DAG: memref.global "private" @[[ALLOC0:alloc.*]] : memref<2x32xf32>
// CHECK-DAG: memref.global "private" @[[ALLOC1:alloc.*]] : memref<2x32xf32>
// CHECK-DAG: func.func @func(%[[LB:.*]]: index, %[[UB:.*]]: index)
func.func @func(%lb: index, %ub: index) {
// CHECK-DAG: scf.forall (%[[ARG0:.*]], %[[ARG1:.*]]) in (%[[LB]], %[[UB]])
scf.forall (%arg0, %arg1) in (%lb, %ub) {
// CHECK-DAG: %[[MR0:.*]] = memref.get_global @[[ALLOC0]] : memref<2x32xf32>
// CHECK-DAG: %[[MR1:.*]] = memref.get_global @[[ALLOC1]] : memref<2x32xf32>
// CHECK-DAG: memref.store %{{.*}}, %[[MR0]][%{{.*}}, %{{.*}}] : memref<2x32xf32>
// CHECK-DAG: memref.store %{{.*}}, %[[MR1]][%{{.*}}, %{{.*}}] : memref<2x32xf32>
%cst = arith.constant 0.0 : f32
%mr0 = memref.alloca() : memref<2x32xf32>
%mr1 = memref.alloca() : memref<2x32xf32>
memref.store %cst, %mr0[%arg0, %arg1] : memref<2x32xf32>
memref.store %cst, %mr1[%arg0, %arg1] : memref<2x32xf32>
}
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {
%alloca = transform.structured.match ops{["memref.alloca"]} in %arg0
: (!transform.any_op) -> !transform.op<"memref.alloca">
%get_global, %global = transform.memref.alloca_to_global %alloca
: (!transform.op<"memref.alloca">)
-> (!transform.any_op, !transform.any_op)
transform.yield
}
}
// -----
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0) -> ((d0 floordiv 4) mod 2)>
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0)[s0] -> (d0 + s0)>
// CHECK-LABEL: func @multi_buffer
func.func @multi_buffer(%in: memref<16xf32>) {
// CHECK: %[[A:.*]] = memref.alloc() : memref<2x4xf32>
// expected-remark @below {{transformed}}
%tmp = memref.alloc() : memref<4xf32>
// CHECK: %[[C0:.*]] = arith.constant 0 : index
// CHECK: %[[C4:.*]] = arith.constant 4 : index
%c0 = arith.constant 0 : index
%c4 = arith.constant 4 : index
%c16 = arith.constant 16 : index
// CHECK: scf.for %[[IV:.*]] = %[[C0]]
scf.for %i0 = %c0 to %c16 step %c4 {
// CHECK: %[[I:.*]] = affine.apply #[[$MAP0]](%[[IV]])
// CHECK: %[[SV:.*]] = memref.subview %[[A]][%[[I]], 0] [1, 4] [1, 1] : memref<2x4xf32> to memref<4xf32, strided<[1], offset: ?>>
%1 = memref.subview %in[%i0] [4] [1] : memref<16xf32> to memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>>
// CHECK: memref.copy %{{.*}}, %[[SV]] : memref<4xf32, #[[$MAP1]]> to memref<4xf32, strided<[1], offset: ?>>
memref.copy %1, %tmp : memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>> to memref<4xf32>
"some_use"(%tmp) : (memref<4xf32>) ->()
}
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["memref.alloc"]} in %arg1 : (!transform.any_op) -> !transform.op<"memref.alloc">
%1 = transform.memref.multibuffer %0 {factor = 2 : i64} : (!transform.op<"memref.alloc">) -> !transform.any_op
// Verify that the returned handle is usable.
transform.test_print_remark_at_operand %1, "transformed" : !transform.any_op
transform.yield
}
}
// -----
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0) -> ((d0 floordiv 4) mod 2)>
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0)[s0] -> (d0 + s0)>
// CHECK-LABEL: func @multi_buffer_on_affine_loop
func.func @multi_buffer_on_affine_loop(%in: memref<16xf32>) {
// CHECK: %[[A:.*]] = memref.alloc() : memref<2x4xf32>
// expected-remark @below {{transformed}}
%tmp = memref.alloc() : memref<4xf32>
// CHECK: %[[C0:.*]] = arith.constant 0 : index
%c0 = arith.constant 0 : index
// CHECK: affine.for %[[IV:.*]] = 0
affine.for %i0 = 0 to 16 step 4 {
// CHECK: %[[I:.*]] = affine.apply #[[$MAP0]](%[[IV]])
// CHECK: %[[SV:.*]] = memref.subview %[[A]][%[[I]], 0] [1, 4] [1, 1] : memref<2x4xf32> to memref<4xf32, strided<[1], offset: ?>>
%1 = memref.subview %in[%i0] [4] [1] : memref<16xf32> to memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>>
// CHECK: memref.copy %{{.*}}, %[[SV]] : memref<4xf32, #[[$MAP1]]> to memref<4xf32, strided<[1], offset: ?>>
memref.copy %1, %tmp : memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>> to memref<4xf32>
"some_use"(%tmp) : (memref<4xf32>) ->()
}
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["memref.alloc"]} in %arg1 : (!transform.any_op) -> !transform.op<"memref.alloc">
%1 = transform.memref.multibuffer %0 {factor = 2 : i64} : (!transform.op<"memref.alloc">) -> !transform.any_op
// Verify that the returned handle is usable.
transform.test_print_remark_at_operand %1, "transformed" : !transform.any_op
transform.yield
}
}
// -----
// Trying to use multibuffer on allocs that are used in different loops
// with none dominating the other is going to fail.
// Check that we emit a proper error for that.
func.func @multi_buffer_uses_with_no_loop_dominator(%in: memref<16xf32>, %cond: i1) {
// expected-error @below {{op failed to multibuffer}}
%tmp = memref.alloc() : memref<4xf32>
%c0 = arith.constant 0 : index
%c4 = arith.constant 4 : index
%c16 = arith.constant 16 : index
scf.if %cond {
scf.for %i0 = %c0 to %c16 step %c4 {
%var = memref.subview %in[%i0] [4] [1] : memref<16xf32> to memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>>
memref.copy %var, %tmp : memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>> to memref<4xf32>
"some_use"(%tmp) : (memref<4xf32>) ->()
}
}
scf.for %i0 = %c0 to %c16 step %c4 {
%1 = memref.subview %in[%i0] [4] [1] : memref<16xf32> to memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>>
memref.copy %1, %tmp : memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>> to memref<4xf32>
"some_use"(%tmp) : (memref<4xf32>) ->()
}
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["memref.alloc"]} in %arg1 : (!transform.any_op) -> !transform.op<"memref.alloc">
%1 = transform.memref.multibuffer %0 {factor = 2 : i64} : (!transform.op<"memref.alloc">) -> !transform.any_op
transform.yield
}
}
// -----
// Make sure the multibuffer operation is typed so that it only supports
// memref.alloc.
// Check that we emit an error if we try to match something else.
func.func @multi_buffer_reject_alloca(%in: memref<16xf32>, %cond: i1) {
%tmp = memref.alloca() : memref<4xf32>
%c0 = arith.constant 0 : index
%c4 = arith.constant 4 : index
%c16 = arith.constant 16 : index
scf.if %cond {
scf.for %i0 = %c0 to %c16 step %c4 {
%var = memref.subview %in[%i0] [4] [1] : memref<16xf32> to memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>>
memref.copy %var, %tmp : memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>> to memref<4xf32>
"some_use"(%tmp) : (memref<4xf32>) ->()
}
}
scf.for %i0 = %c0 to %c16 step %c4 {
%1 = memref.subview %in[%i0] [4] [1] : memref<16xf32> to memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>>
memref.copy %1, %tmp : memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>> to memref<4xf32>
"some_use"(%tmp) : (memref<4xf32>) ->()
}
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["memref.alloca"]} in %arg1 : (!transform.any_op) -> !transform.op<"memref.alloca">
// expected-error @below {{'transform.memref.multibuffer' op operand #0 must be Transform IR handle to memref.alloc operations, but got '!transform.op<"memref.alloca">'}}
%1 = transform.memref.multibuffer %0 {factor = 2 : i64} : (!transform.op<"memref.alloca">) -> !transform.any_op
transform.yield
}
}
// -----
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0) -> ((d0 floordiv 4) mod 2)>
// CHECK-DAG: #[[$MAP1:.*]] = affine_map<(d0)[s0] -> (d0 + s0)>
// CHECK-LABEL: func @multi_buffer_one_alloc_with_use_outside_of_loop
// Make sure we manage to apply multi_buffer to the memref that is used in
// the loop (%tmp) and don't error out for the one that is not (%tmp2).
func.func @multi_buffer_one_alloc_with_use_outside_of_loop(%in: memref<16xf32>) {
// CHECK: %[[A:.*]] = memref.alloc() : memref<2x4xf32>
// expected-remark @below {{transformed}}
%tmp = memref.alloc() : memref<4xf32>
%tmp2 = memref.alloc() : memref<4xf32>
"some_use_outside_of_loop"(%tmp2) : (memref<4xf32>) -> ()
// CHECK: %[[C0:.*]] = arith.constant 0 : index
// CHECK: %[[C4:.*]] = arith.constant 4 : index
%c0 = arith.constant 0 : index
%c4 = arith.constant 4 : index
%c16 = arith.constant 16 : index
// CHECK: scf.for %[[IV:.*]] = %[[C0]]
scf.for %i0 = %c0 to %c16 step %c4 {
// CHECK: %[[I:.*]] = affine.apply #[[$MAP0]](%[[IV]])
// CHECK: %[[SV:.*]] = memref.subview %[[A]][%[[I]], 0] [1, 4] [1, 1] : memref<2x4xf32> to memref<4xf32, strided<[1], offset: ?>>
%1 = memref.subview %in[%i0] [4] [1] : memref<16xf32> to memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>>
// CHECK: memref.copy %{{.*}}, %[[SV]] : memref<4xf32, #[[$MAP1]]> to memref<4xf32, strided<[1], offset: ?>>
memref.copy %1, %tmp : memref<4xf32, affine_map<(d0)[s0] -> (d0 + s0)>> to memref<4xf32>
"some_use"(%tmp) : (memref<4xf32>) ->()
}
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["memref.alloc"]} in %arg1 : (!transform.any_op) -> !transform.op<"memref.alloc">
%1 = transform.memref.multibuffer %0 {factor = 2 : i64} : (!transform.op<"memref.alloc">) -> !transform.any_op
// Verify that the returned handle is usable.
transform.test_print_remark_at_operand %1, "transformed" : !transform.any_op
transform.yield
}
}
// -----
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0) -> ((d0 floordiv 4) mod 2)>
// CHECK-LABEL: func @multi_buffer
func.func @multi_buffer_no_analysis(%in: memref<16xf32>) {
// CHECK: %[[A:.*]] = memref.alloc() : memref<2x4xf32>
// expected-remark @below {{transformed}}
%tmp = memref.alloc() : memref<4xf32>
// CHECK: %[[C0:.*]] = arith.constant 0 : index
// CHECK: %[[C4:.*]] = arith.constant 4 : index
%c0 = arith.constant 0 : index
%c4 = arith.constant 4 : index
%c16 = arith.constant 16 : index
// CHECK: scf.for %[[IV:.*]] = %[[C0]]
scf.for %i0 = %c0 to %c16 step %c4 {
// CHECK: %[[I:.*]] = affine.apply #[[$MAP0]](%[[IV]])
// CHECK: %[[SV:.*]] = memref.subview %[[A]][%[[I]], 0] [1, 4] [1, 1] : memref<2x4xf32> to memref<4xf32, strided<[1], offset: ?>>
"some_write_read"(%tmp) : (memref<4xf32>) ->()
}
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["memref.alloc"]} in %arg1 : (!transform.any_op) -> !transform.op<"memref.alloc">
%1 = transform.memref.multibuffer %0 {factor = 2 : i64, skip_analysis} : (!transform.op<"memref.alloc">) -> !transform.any_op
// Verify that the returned handle is usable.
transform.test_print_remark_at_operand %1, "transformed" : !transform.any_op
transform.yield
}
}
// -----
// CHECK-DAG: #[[$MAP0:.*]] = affine_map<(d0) -> ((d0 floordiv 4) mod 2)>
// CHECK-LABEL: func @multi_buffer_dealloc
func.func @multi_buffer_dealloc(%in: memref<16xf32>) {
// CHECK: %[[A:.*]] = memref.alloc() : memref<2x4xf32>
// expected-remark @below {{transformed}}
%tmp = memref.alloc() : memref<4xf32>
// CHECK: %[[C0:.*]] = arith.constant 0 : index
// CHECK: %[[C4:.*]] = arith.constant 4 : index
%c0 = arith.constant 0 : index
%c4 = arith.constant 4 : index
%c16 = arith.constant 16 : index
// CHECK: scf.for %[[IV:.*]] = %[[C0]]
scf.for %i0 = %c0 to %c16 step %c4 {
// CHECK: %[[I:.*]] = affine.apply #[[$MAP0]](%[[IV]])
// CHECK: %[[SV:.*]] = memref.subview %[[A]][%[[I]], 0] [1, 4] [1, 1] : memref<2x4xf32> to memref<4xf32, strided<[1], offset: ?>>
"some_write_read"(%tmp) : (memref<4xf32>) ->()
}
// CHECK-NOT: memref.dealloc {{.*}} : memref<4xf32>
// CHECK: memref.dealloc %[[A]] : memref<2x4xf32>
memref.dealloc %tmp : memref<4xf32>
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["memref.alloc"]} in %arg1 : (!transform.any_op) -> !transform.op<"memref.alloc">
%1 = transform.memref.multibuffer %0 {factor = 2 : i64, skip_analysis} : (!transform.op<"memref.alloc">) -> !transform.any_op
// Verify that the returned handle is usable.
transform.test_print_remark_at_operand %1, "transformed" : !transform.any_op
transform.yield
}
}
// -----
// CHECK-LABEL: func.func @dead_alloc
func.func @dead_alloc() {
// CHECK-NOT: %{{.+}} = memref.alloc
%0 = memref.alloc() : memref<8x64xf32, 3>
%1 = memref.subview %0[0, 0] [8, 4] [1, 1] : memref<8x64xf32, 3> to
memref<8x4xf32, affine_map<(d0, d1) -> (d0 * 64 + d1)>, 3>
%c0 = arith.constant 0 : index
%cst_0 = arith.constant dense<0.000000e+00> : vector<1x4xf32>
vector.transfer_write %cst_0, %1[%c0, %c0] {in_bounds = [true, true]} :
vector<1x4xf32>, memref<8x4xf32, affine_map<(d0, d1) -> (d0 * 64 + d1)>, 3>
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
transform.memref.erase_dead_alloc_and_stores %0 : (!transform.any_op) -> ()
transform.yield
}
}
// -----
// CHECK-LABEL: @store_to_load
// CHECK-SAME: (%[[ARG:.+]]: vector<4xf32>)
// CHECK-NOT: memref.alloc()
// CHECK-NOT: vector.transfer_write
// CHECK-NOT: vector.transfer_read
// CHECK: return %[[ARG]] : vector<4xf32>
func.func @store_to_load(%arg: vector<4xf32>) -> vector<4xf32> {
%c0 = arith.constant 0 : index
%cst_1 = arith.constant 0.000000e+00 : f32
%alloc = memref.alloc() {alignment = 64 : i64} : memref<64xf32>
vector.transfer_write %arg, %alloc[%c0] {in_bounds = [true]} : vector<4xf32>, memref<64xf32>
%r = vector.transfer_read %alloc[%c0], %cst_1 {in_bounds = [true]} : memref<64xf32>, vector<4xf32>
return %r : vector<4xf32>
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
transform.memref.erase_dead_alloc_and_stores %0 : (!transform.any_op) -> ()
transform.yield
}
}
// -----
// CHECK-LABEL: func @lower_to_llvm
// CHECK-NOT: memref.alloc
// CHECK: llvm.call @malloc
func.func @lower_to_llvm() {
%0 = memref.alloc() : memref<2048xi8>
return
}
module attributes {transform.with_named_sequence} {
transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
%0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op
transform.apply_conversion_patterns to %0 {
transform.apply_conversion_patterns.dialect_to_llvm "memref"
} with type_converter {
transform.apply_conversion_patterns.memref.memref_to_llvm_type_converter
} {legal_dialects = ["func", "llvm"]} : !transform.any_op
transform.yield
}
}