480 lines · plain
1// RUN: mlir-opt %s --transform-interpreter -allow-unregistered-dialect --split-input-file --verify-diagnostics | FileCheck %s2 3// CHECK-LABEL: func @update_tracked_op_mapping()4// CHECK: "test.container"() ({5// CHECK: %0 = "test.foo"() {annotated} : () -> i326// CHECK: }) : () -> ()7func.func @update_tracked_op_mapping() {8 "test.container"() ({9 %0 = "test.foo"() {replace_with_new_op = "test.foo"} : () -> (i32)10 }) : () -> ()11 return12}13 14module attributes {transform.with_named_sequence} {15 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {16 %0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op17 %1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op18 transform.apply_patterns to %0 {19 transform.apply_patterns.transform.test_patterns20 } : !transform.any_op21 // Add an attribute to %1, which is now mapped to a new op.22 transform.annotate %1 "annotated" : !transform.any_op23 transform.yield24 }25}26 27// -----28 29// CHECK-LABEL: @limited_updates30func.func @limited_updates() {31 "test.container"() ({32 // Only one is replaced.33 // CHECK: "test.foo"() {replace_with_new_op = "test.foo"}34 // CHECK: "test.foo"() : ()35 %0 = "test.foo"() {replace_with_new_op = "test.foo"} : () -> (i32)36 %1 = "test.foo"() {replace_with_new_op = "test.foo"} : () -> (i32)37 }) : () -> ()38 return39}40 41module attributes {transform.with_named_sequence} {42 transform.named_sequence @__transform_main(%arg0: !transform.any_op) {43 // Pattern application will fail because of the upper limit, wrap in44 // sequence to suppress the error message.45 transform.sequence %arg0 : !transform.any_op failures(suppress) {46 ^bb0(%arg1: !transform.any_op):47 %0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op48 %1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op49 transform.apply_patterns to %0 {50 transform.apply_patterns.transform.test_patterns51 } {max_num_rewrites = 1} : !transform.any_op52 }53 transform.yield54 }55}56 57// -----58 59func.func @replacement_op_not_found() {60 "test.container"() ({61 // expected-note @below {{[0] replaced op}}62 // expected-note @below {{[0] replacement value 0}}63 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)64 }) : () -> ()65 return66}67 68module attributes {transform.with_named_sequence} {69 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {70 %0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op71 // expected-note @below {{replacement is required because this handle must be updated}}72 %1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op73 // expected-error @below {{tracking listener failed to find replacement op during application of this transform op}}74 // expected-note @below {{ran out of suitable replacement values}}75 transform.apply_patterns to %0 {76 transform.apply_patterns.transform.test_patterns77 } : !transform.any_op78 // %1 must be used in some way. If no replacement payload op could be found,79 // an error is thrown only if the handle is not dead.80 transform.annotate %1 "annotated" : !transform.any_op81 transform.yield82 }83}84 85// -----86 87// CHECK-LABEL: func @replacement_op_for_dead_handle_not_found()88// CHECK: "test.container"() ({89// CHECK: %0 = "test.bar"() : () -> i3290// CHECK: }) : () -> ()91func.func @replacement_op_for_dead_handle_not_found() {92 "test.container"() ({93 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)94 }) : () -> ()95 return96}97 98module attributes {transform.with_named_sequence} {99 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {100 %0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op101 %1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op102 // No error because %1 is dead.103 transform.apply_patterns to %0 {104 transform.apply_patterns.transform.test_patterns105 } : !transform.any_op106 transform.yield107 }108}109 110// -----111 112// CHECK-LABEL: func @replacement_op_not_found_silenced()113// CHECK: "test.container"() ({114// CHECK: %0 = "test.bar"() : () -> i32115// CHECK: }) : () -> ()116func.func @replacement_op_not_found_silenced() {117 "test.container"() ({118 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)119 }) : () -> ()120 return121}122 123module attributes {transform.with_named_sequence} {124 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {125 %0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op126 %1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op127 transform.apply_patterns to %0 {128 transform.apply_patterns.transform.test_patterns129 } {transform.silence_tracking_failures} : !transform.any_op130 transform.annotate %1 "annotated" : !transform.any_op131 transform.yield132 }133}134 135// -----136 137// CHECK-LABEL: func @patterns_apply_only_to_target_body()138// CHECK: %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> i32139func.func @patterns_apply_only_to_target_body() {140 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)141 return142}143 144module attributes {transform.with_named_sequence} {145 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {146 %0 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op147 transform.apply_patterns to %0 {148 transform.apply_patterns.transform.test_patterns149 } : !transform.any_op150 transform.yield151 }152}153 154// -----155 156// CHECK-LABEL: func @erase_tracked_op()157// CHECK: "test.container"() ({158// CHECK-NEXT: ^bb0:159// CHECK-NEXT: }) : () -> ()160func.func @erase_tracked_op() {161 "test.container"() ({162 // expected-remark @below {{matched op}}163 %0 = "test.erase_op"() {replace_with_new_op = "test.foo"} : () -> (i32)164 }) : () -> ()165 return166}167 168module attributes {transform.with_named_sequence} {169 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {170 %0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op171 %1 = transform.structured.match ops{["test.erase_op"]} in %arg1 : (!transform.any_op) -> !transform.any_op172 transform.debug.emit_remark_at %1, "matched op" : !transform.any_op173 transform.apply_patterns to %0 {174 transform.apply_patterns.transform.test_patterns175 } : !transform.any_op176 // No marker should be printed.177 transform.debug.emit_remark_at %1, "op was deleted" : !transform.any_op178 transform.yield179 }180}181 182// -----183 184// CHECK-LABEL: func @erase_tracked_op_in_named_sequence()185// CHECK: "test.container"() ({186// CHECK-NEXT: ^bb0:187// CHECK-NEXT: }) : () -> ()188module attributes {transform.with_named_sequence} {189 func.func @erase_tracked_op_in_named_sequence() {190 "test.container"() ({191 // expected-remark @below {{matched op}}192 %0 = "test.erase_op"() {replace_with_new_op = "test.foo"} : () -> (i32)193 }) : () -> ()194 return195 }196 197 transform.named_sequence @foo(%arg0: !transform.any_op {transform.readonly}) -> () {198 transform.apply_patterns to %arg0 {199 transform.apply_patterns.transform.test_patterns200 } : !transform.any_op201 transform.yield202 }203 204 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {205 %0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op206 %1 = transform.structured.match ops{["test.erase_op"]} in %arg1 : (!transform.any_op) -> !transform.any_op207 transform.debug.emit_remark_at %1, "matched op" : !transform.any_op208 transform.include @foo failures(propagate) (%0) : (!transform.any_op) -> ()209 // No marker should be printed.210 transform.debug.emit_remark_at %1, "op was deleted" : !transform.any_op211 transform.yield212 }213}214 215// -----216 217// CHECK-LABEL: func @canonicalization(218// CHECK: %[[c5:.*]] = arith.constant 5 : index219// CHECK: return %[[c5]]220func.func @canonicalization(%t: tensor<5xf32>) -> index {221 %c0 = arith.constant 0 : index222 %dim = tensor.dim %t, %c0 : tensor<5xf32>223 return %dim : index224}225 226module attributes {transform.with_named_sequence} {227 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {228 %0 = transform.structured.match ops{["tensor.dim"]} in %arg1 : (!transform.any_op) -> !transform.any_op229 %1 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op230 transform.apply_patterns to %1 {231 transform.apply_patterns.canonicalization232 } : !transform.any_op233 transform.yield234 }235}236 237// -----238 239// expected-note @below{{target payload op}}240module {241 func.func @invalid_pattern_application_to_transform_ir() {242 return243 }244 245 module attributes {transform.with_named_sequence} {246 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {247 // expected-error @below {{cannot apply transform to itself (or one of its ancestors)}}248 transform.apply_patterns to %arg1 {249 transform.apply_patterns.canonicalization250 } : !transform.any_op251 transform.yield252 }253 }254}255 256// -----257 258// CHECK-LABEL: func @canonicalization_and_cse(259// CHECK-NOT: memref.subview260// CHECK-NOT: memref.copy261func.func @canonicalization_and_cse(%m: memref<5xf32>) {262 %c2 = arith.constant 2 : index263 %s0 = memref.subview %m[1] [2] [1] : memref<5xf32> to memref<2xf32, strided<[1], offset: 1>>264 %s1 = memref.subview %m[1] [%c2] [1] : memref<5xf32> to memref<?xf32, strided<[1], offset: 1>>265 memref.copy %s0, %s1 : memref<2xf32, strided<[1], offset: 1>> to memref<?xf32, strided<[1], offset: 1>>266 return267}268 269module attributes {transform.with_named_sequence} {270 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {271 %1 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op272 transform.apply_patterns to %1 {273 transform.apply_patterns.canonicalization274 } {apply_cse} : !transform.any_op275 transform.yield276 }277}278 279// -----280 281// CHECK-LABEL: func @full_dialect_conversion282// CHECK-NEXT: %[[m:.*]] = "test.new_op"() : () -> memref<5xf32>283// CHECK-NEXT: %[[cast:.*]] = builtin.unrealized_conversion_cast %0 : memref<5xf32> to tensor<5xf32>284// CHECK-NEXT: return %[[cast]]285func.func @full_dialect_conversion() -> tensor<5xf32> {286 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (tensor<5xf32>)287 return %0 : tensor<5xf32>288}289 290module attributes {transform.with_named_sequence} {291 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {292 %0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op293 transform.apply_conversion_patterns to %0 {294 transform.apply_conversion_patterns.transform.test_conversion_patterns295 } with type_converter {296 transform.apply_conversion_patterns.transform.test_type_converter297 } {legal_ops = ["func.func", "func.return", "test.new_op"]}298 : !transform.any_op299 transform.yield300 }301}302 303// -----304 305// Full dialect conversion fails because test.bar is not replaced and not legal.306 307// expected-note @below{{target op}}308func.func @full_dialect_conversion_failed() -> tensor<5xf32> {309 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (tensor<5xf32>)310 // expected-error @below{{failed to legalize operation 'test.bar'}}311 "test.bar"() : () -> ()312 return %0 : tensor<5xf32>313}314 315module attributes {transform.with_named_sequence} {316 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {317 %0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op318 // expected-error @below{{dialect conversion failed}}319 transform.apply_conversion_patterns to %0 {320 transform.apply_conversion_patterns.transform.test_conversion_patterns321 } with type_converter {322 transform.apply_conversion_patterns.transform.test_type_converter323 } {legal_ops = ["func.func", "func.return", "test.new_op"]}324 : !transform.any_op325 transform.yield326 }327}328 329// -----330 331// Partial dialect conversion succeeds because test.bar is not explicitly332// illegal.333 334// CHECK-LABEL: func @partial_dialect_conversion335// CHECK-NEXT: %[[m:.*]] = "test.new_op"() : () -> memref<5xf32>336// CHECK-NEXT: %[[cast:.*]] = builtin.unrealized_conversion_cast %0 : memref<5xf32> to tensor<5xf32>337// CHECK-NEXT: "test.bar"338// CHECK-NEXT: return %[[cast]]339func.func @partial_dialect_conversion() -> tensor<5xf32> {340 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (tensor<5xf32>)341 "test.bar"() : () -> ()342 return %0 : tensor<5xf32>343}344 345module attributes {transform.with_named_sequence} {346 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {347 %0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op348 transform.apply_conversion_patterns to %0 {349 transform.apply_conversion_patterns.transform.test_conversion_patterns350 } with type_converter {351 transform.apply_conversion_patterns.transform.test_type_converter352 } {legal_ops = ["func.func", "func.return", "test.new_op"],353 partial_conversion} : !transform.any_op354 transform.yield355 }356}357 358// -----359 360module attributes {transform.with_named_sequence} {361 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {362 %0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op363 // expected-error @below{{pattern descriptor does not specify type converter and apply_conversion_patterns op has no default type converter}}364 transform.apply_conversion_patterns to %0 {365 // expected-note @below{{pattern descriptor op}}366 transform.apply_conversion_patterns.transform.test_conversion_patterns367 } {illegal_ops = ["test.foo"]} : !transform.any_op368 transform.yield369 }370}371 372// -----373 374module attributes {transform.with_named_sequence} {375 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {376 %0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op377 transform.apply_conversion_patterns to %0 {378 // expected-error @below{{expected LLVMTypeConverter}}379 transform.apply_conversion_patterns.dialect_to_llvm "test"380 } with type_converter {381 transform.apply_conversion_patterns.transform.test_type_converter382 } {illegal_ops = ["test.foo"],383 legal_ops = ["func.func", "func.return", "test.new_op"]}384 : !transform.any_op385 transform.yield386 }387}388 389// -----390 391module attributes {transform.with_named_sequence} {392 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {393 %0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op394 transform.apply_conversion_patterns to %0 {395 // expected-error @below{{unknown dialect or dialect not loaded: this_dialect_does_not_exist}}396 transform.apply_conversion_patterns.dialect_to_llvm "this_dialect_does_not_exist"397 } with type_converter {398 transform.apply_conversion_patterns.memref.memref_to_llvm_type_converter399 } {illegal_ops = ["test.foo"],400 legal_ops = ["func.func", "func.return", "test.new_op"]}401 : !transform.any_op402 transform.yield403 }404}405 406// -----407 408module attributes {transform.with_named_sequence} {409 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {410 %0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op411 transform.apply_conversion_patterns to %0 {412 // expected-error @below{{dialect does not implement ConvertToLLVMPatternInterface or extension was not loaded: transform}}413 transform.apply_conversion_patterns.dialect_to_llvm "transform"414 } with type_converter {415 transform.apply_conversion_patterns.memref.memref_to_llvm_type_converter416 } {illegal_ops = ["test.foo"],417 legal_ops = ["func.func", "func.return", "test.new_op"]}418 : !transform.any_op419 transform.yield420 }421}422 423// -----424 425module attributes { transform.with_named_sequence } {426 func.func @replacement_op_not_found() {427 // No op replacement can be found, but there are no handles that must be428 // updated. No error should be reported.429 "test.container"() ({430 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (i32)431 }) : () -> ()432 return433 }434 435 transform.named_sequence @patterns(%container: !transform.any_op {transform.readonly}) {436 transform.apply_patterns to %container {437 transform.apply_patterns.transform.test_patterns438 } : !transform.any_op439 transform.yield440 }441 442 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {443 %0 = transform.structured.match ops{["test.container"]} in %arg1 : (!transform.any_op) -> !transform.any_op444 %1 = transform.structured.match ops{["test.foo"]} in %arg1 : (!transform.any_op) -> !transform.any_op445 transform.annotate %1 "annotated" : !transform.any_op446 transform.include @patterns failures(propagate) (%0) : (!transform.any_op) -> ()447 transform.yield448 }449}450 451// -----452 453// "test.foo" is tracked and replaced with "test.new_op" during a dialect454// conversion. Make sure that the handle is updated accordingly.455 456// CHECK-LABEL: func @dialect_conversion_tracking457// CHECK-NEXT: %[[m:.*]] = "test.new_op"() {annotated} : () -> memref<5xf32>458// CHECK-NEXT: %[[cast:.*]] = builtin.unrealized_conversion_cast %0 : memref<5xf32> to tensor<5xf32>459// CHECK-NEXT: return %[[cast]]460func.func @dialect_conversion_tracking() -> tensor<5xf32> {461 %0 = "test.foo"() {replace_with_new_op = "test.bar"} : () -> (tensor<5xf32>)462 return %0 : tensor<5xf32>463}464 465module attributes {transform.with_named_sequence} {466 transform.named_sequence @__transform_main(%arg1: !transform.any_op) {467 %0 = transform.structured.match ops{["func.func"]} in %arg1 : (!transform.any_op) -> !transform.any_op468 %1 = transform.structured.match ops{["test.foo"]} in %0 : (!transform.any_op) -> !transform.any_op469 transform.apply_conversion_patterns to %0 {470 transform.apply_conversion_patterns.transform.test_conversion_patterns471 } with type_converter {472 transform.apply_conversion_patterns.transform.test_type_converter473 } {legal_ops = ["func.func", "func.return", "test.new_op"], preserve_handles}474 : !transform.any_op475 // Add an attribute to %1, which is now mapped to a new op.476 transform.annotate %1 "annotated" : !transform.any_op477 transform.yield478 }479}480