1// RUN: mlir-opt -allow-unregistered-dialect -split-input-file -test-legalize-patterns -verify-diagnostics %s | FileCheck %s 2 3// CHECK-LABEL: verifyDirectPattern 4func @verifyDirectPattern() -> i32 { 5 // CHECK-NEXT: "test.legal_op_a"() {status = "Success"} 6 %result = "test.illegal_op_a"() : () -> (i32) 7 // expected-remark@+1 {{op 'std.return' is not legalizable}} 8 return %result : i32 9} 10 11// CHECK-LABEL: verifyLargerBenefit 12func @verifyLargerBenefit() -> i32 { 13 // CHECK-NEXT: "test.legal_op_a"() {status = "Success"} 14 %result = "test.illegal_op_c"() : () -> (i32) 15 // expected-remark@+1 {{op 'std.return' is not legalizable}} 16 return %result : i32 17} 18 19// CHECK-LABEL: func private @remap_input_1_to_0() 20func private @remap_input_1_to_0(i16) 21 22// CHECK-LABEL: func @remap_input_1_to_1(%arg0: f64) 23func @remap_input_1_to_1(%arg0: i64) { 24 // CHECK-NEXT: "test.valid"{{.*}} : (f64) 25 "test.invalid"(%arg0) : (i64) -> () 26} 27 28// CHECK-LABEL: func @remap_call_1_to_1(%arg0: f64) 29func @remap_call_1_to_1(%arg0: i64) { 30 // CHECK-NEXT: call @remap_input_1_to_1(%arg0) : (f64) -> () 31 // expected-remark@+1 {{op 'std.call' is not legalizable}} 32 call @remap_input_1_to_1(%arg0) : (i64) -> () 33 // expected-remark@+1 {{op 'std.return' is not legalizable}} 34 return 35} 36 37// CHECK-LABEL: func @remap_input_1_to_N({{.*}}f16, {{.*}}f16) 38func @remap_input_1_to_N(%arg0: f32) -> f32 { 39 // CHECK-NEXT: [[CAST:%.*]] = "test.cast"(%arg0, %arg1) : (f16, f16) -> f32 40 // CHECK-NEXT: "test.return"{{.*}} : (f16, f16) -> () 41 "test.return"(%arg0) : (f32) -> () 42} 43 44// CHECK-LABEL: func @remap_input_1_to_N_remaining_use(%arg0: f16, %arg1: f16) 45func @remap_input_1_to_N_remaining_use(%arg0: f32) { 46 // CHECK-NEXT: [[CAST:%.*]] = "test.cast"(%arg0, %arg1) : (f16, f16) -> f32 47 // CHECK-NEXT: "work"([[CAST]]) : (f32) -> () 48 // expected-remark@+1 {{op 'work' is not legalizable}} 49 "work"(%arg0) : (f32) -> () 50} 51 52// CHECK-LABEL: func @remap_materialize_1_to_1(%{{.*}}: i43) 53func @remap_materialize_1_to_1(%arg0: i42) { 54 // CHECK: %[[V:.*]] = "test.cast"(%arg0) : (i43) -> i42 55 // CHECK: "test.return"(%[[V]]) 56 "test.return"(%arg0) : (i42) -> () 57} 58 59// CHECK-LABEL: func @remap_input_to_self 60func @remap_input_to_self(%arg0: index) { 61 // CHECK-NOT: test.cast 62 // CHECK: "work" 63 // expected-remark@+1 {{op 'work' is not legalizable}} 64 "work"(%arg0) : (index) -> () 65} 66 67// CHECK-LABEL: func @remap_multi(%arg0: f64, %arg1: f64) -> (f64, f64) 68func @remap_multi(%arg0: i64, %unused: i16, %arg1: i64) -> (i64, i64) { 69 // CHECK-NEXT: "test.valid"{{.*}} : (f64, f64) 70 "test.invalid"(%arg0, %arg1) : (i64, i64) -> () 71} 72 73// CHECK-LABEL: func @no_remap_nested 74func @no_remap_nested() { 75 // CHECK-NEXT: "foo.region" 76 // expected-remark@+1 {{op 'foo.region' is not legalizable}} 77 "foo.region"() ({ 78 // CHECK-NEXT: ^bb0(%{{.*}}: i64, %{{.*}}: i16, %{{.*}}: i64): 79 ^bb0(%i0: i64, %unused: i16, %i1: i64): 80 // CHECK-NEXT: "test.valid"{{.*}} : (i64, i64) 81 "test.invalid"(%i0, %i1) : (i64, i64) -> () 82 }) : () -> () 83 // expected-remark@+1 {{op 'std.return' is not legalizable}} 84 return 85} 86 87// CHECK-LABEL: func @remap_moved_region_args 88func @remap_moved_region_args() { 89 // CHECK-NEXT: return 90 // CHECK-NEXT: ^bb1(%{{.*}}: f64, %{{.*}}: f64, %{{.*}}: f16, %{{.*}}: f16): 91 // CHECK-NEXT: "test.cast"{{.*}} : (f16, f16) -> f32 92 // CHECK-NEXT: "test.valid"{{.*}} : (f64, f64, f32) 93 "test.region"() ({ 94 ^bb1(%i0: i64, %unused: i16, %i1: i64, %2: f32): 95 "test.invalid"(%i0, %i1, %2) : (i64, i64, f32) -> () 96 }) : () -> () 97 // expected-remark@+1 {{op 'std.return' is not legalizable}} 98 return 99} 100 101// CHECK-LABEL: func @remap_cloned_region_args 102func @remap_cloned_region_args() { 103 // CHECK-NEXT: return 104 // CHECK-NEXT: ^bb1(%{{.*}}: f64, %{{.*}}: f64, %{{.*}}: f16, %{{.*}}: f16): 105 // CHECK-NEXT: "test.cast"{{.*}} : (f16, f16) -> f32 106 // CHECK-NEXT: "test.valid"{{.*}} : (f64, f64, f32) 107 "test.region"() ({ 108 ^bb1(%i0: i64, %unused: i16, %i1: i64, %2: f32): 109 "test.invalid"(%i0, %i1, %2) : (i64, i64, f32) -> () 110 }) {legalizer.should_clone} : () -> () 111 // expected-remark@+1 {{op 'std.return' is not legalizable}} 112 return 113} 114 115// CHECK-LABEL: func @remap_drop_region 116func @remap_drop_region() { 117 // CHECK-NEXT: return 118 // CHECK-NEXT: } 119 "test.drop_region_op"() ({ 120 ^bb1(%i0: i64, %unused: i16, %i1: i64, %2: f32): 121 "test.invalid"(%i0, %i1, %2) : (i64, i64, f32) -> () 122 }) : () -> () 123 // expected-remark@+1 {{op 'std.return' is not legalizable}} 124 return 125} 126 127// CHECK-LABEL: func @dropped_input_in_use 128func @dropped_input_in_use(%arg: i16, %arg2: i64) { 129 // CHECK-NEXT: "test.cast"{{.*}} : () -> i16 130 // CHECK-NEXT: "work"{{.*}} : (i16) 131 // expected-remark@+1 {{op 'work' is not legalizable}} 132 "work"(%arg) : (i16) -> () 133} 134 135// CHECK-LABEL: func @up_to_date_replacement 136func @up_to_date_replacement(%arg: i8) -> i8 { 137 // CHECK-NEXT: return 138 %repl_1 = "test.rewrite"(%arg) : (i8) -> i8 139 %repl_2 = "test.rewrite"(%repl_1) : (i8) -> i8 140 // expected-remark@+1 {{op 'std.return' is not legalizable}} 141 return %repl_2 : i8 142} 143 144// CHECK-LABEL: func @remove_foldable_op 145// CHECK-SAME: (%[[ARG_0:[a-z0-9]*]]: i32) 146func @remove_foldable_op(%arg0 : i32) -> (i32) { 147 // CHECK-NEXT: return %[[ARG_0]] 148 %0 = "test.op_with_region_fold"(%arg0) ({ 149 "foo.op_with_region_terminator"() : () -> () 150 }) : (i32) -> (i32) 151 // expected-remark@+1 {{op 'std.return' is not legalizable}} 152 return %0 : i32 153} 154 155// CHECK-LABEL: @create_block 156func @create_block() { 157 // Check that we created a block with arguments. 158 // CHECK-NOT: test.create_block 159 // CHECK: ^{{.*}}(%{{.*}}: i32, %{{.*}}: i32): 160 "test.create_block"() : () -> () 161 162 // expected-remark@+1 {{op 'std.return' is not legalizable}} 163 return 164} 165 166// CHECK-LABEL: @bounded_recursion 167func @bounded_recursion() { 168 // CHECK: test.recursive_rewrite 0 169 test.recursive_rewrite 3 170 // expected-remark@+1 {{op 'std.return' is not legalizable}} 171 return 172} 173 174// ----- 175 176func @fail_to_convert_illegal_op() -> i32 { 177 // expected-error@+1 {{failed to legalize operation 'test.illegal_op_f'}} 178 %result = "test.illegal_op_f"() : () -> (i32) 179 return %result : i32 180} 181 182// ----- 183 184func @fail_to_convert_illegal_op_in_region() { 185 // expected-error@+1 {{failed to legalize operation 'test.region_builder'}} 186 "test.region_builder"() : () -> () 187 return 188} 189 190// ----- 191 192// Check that the entry block arguments of a region are untouched in the case 193// of failure. 194 195// CHECK-LABEL: func @fail_to_convert_region 196func @fail_to_convert_region() { 197 // CHECK-NEXT: "test.region" 198 // CHECK-NEXT: ^bb{{.*}}(%{{.*}}: i64): 199 "test.region"() ({ 200 ^bb1(%i0: i64): 201 // expected-error@+1 {{failed to legalize operation 'test.region_builder'}} 202 "test.region_builder"() : () -> () 203 "test.valid"() : () -> () 204 }) : () -> () 205 return 206} 207 208// ----- 209 210// CHECK-LABEL: @create_illegal_block 211func @create_illegal_block() { 212 // Check that we can undo block creation, i.e. that the block was removed. 213 // CHECK: test.create_illegal_block 214 // CHECK-NOT: ^{{.*}}(%{{.*}}: i32, %{{.*}}: i32): 215 // expected-remark@+1 {{op 'test.create_illegal_block' is not legalizable}} 216 "test.create_illegal_block"() : () -> () 217 218 // expected-remark@+1 {{op 'std.return' is not legalizable}} 219 return 220} 221 222// ----- 223 224// CHECK-LABEL: @undo_block_arg_replace 225func @undo_block_arg_replace() { 226 // expected-remark@+1 {{op 'test.undo_block_arg_replace' is not legalizable}} 227 "test.undo_block_arg_replace"() ({ 228 ^bb0(%arg0: i32): 229 // CHECK: ^bb0(%[[ARG:.*]]: i32): 230 // CHECK-NEXT: "test.return"(%[[ARG]]) : (i32) 231 232 "test.return"(%arg0) : (i32) -> () 233 }) : () -> () 234 // expected-remark@+1 {{op 'std.return' is not legalizable}} 235 return 236} 237 238// ----- 239 240// The op in this function is rewritten to itself (and thus remains illegal) by 241// a pattern that removes its second block after adding an operation into it. 242// Check that we can undo block removal successfully. 243// CHECK-LABEL: @undo_block_erase 244func @undo_block_erase() { 245 // CHECK: test.undo_block_erase 246 "test.undo_block_erase"() ({ 247 // expected-remark@-1 {{not legalizable}} 248 // CHECK: "unregistered.return"()[^[[BB:.*]]] 249 "unregistered.return"()[^bb1] : () -> () 250 // expected-remark@-1 {{not legalizable}} 251 // CHECK: ^[[BB]] 252 ^bb1: 253 // CHECK: unregistered.return 254 "unregistered.return"() : () -> () 255 // expected-remark@-1 {{not legalizable}} 256 }) : () -> () 257} 258 259// ----- 260 261// The op in this function is attempted to be rewritten to another illegal op 262// with an attached region containing an invalid terminator. The terminator is 263// created before the parent op. The deletion should not crash when deleting 264// created ops in the inverse order, i.e. deleting the parent op and then the 265// child op. 266// CHECK-LABEL: @undo_child_created_before_parent 267func @undo_child_created_before_parent() { 268 // expected-remark@+1 {{is not legalizable}} 269 "test.illegal_op_with_region_anchor"() : () -> () 270 // expected-remark@+1 {{op 'std.return' is not legalizable}} 271 return 272} 273