@@ -234,3 +234,229 @@ module attributes {transform.with_named_sequence} {
234234 transform.yield
235235 }
236236}
237+
238+ // -----
239+
240+ // Check simple move value definitions before insertion operation.
241+ func.func @simple_move_values () -> f32 {
242+ %0 = " before" () : () -> (f32 )
243+ %1 = " moved_op_1" () : () -> (f32 )
244+ %2 = " moved_op_2" () : () -> (f32 )
245+ %3 = " foo" (%1 , %2 ) : (f32 , f32 ) -> (f32 )
246+ return %3 : f32
247+ }
248+ // CHECK-LABEL: func @simple_move_values()
249+ // CHECK: %[[MOVED1:.+]] = "moved_op_1"
250+ // CHECK: %[[MOVED2:.+]] = "moved_op_2"
251+ // CHECK: %[[BEFORE:.+]] = "before"
252+ // CHECK: %[[FOO:.+]] = "foo"(%[[MOVED1]], %[[MOVED2]])
253+ // CHECK: return %[[FOO]]
254+
255+ module attributes {transform.with_named_sequence } {
256+ transform.named_sequence @__transform_main (%arg0 : !transform.any_op {transform.readonly }) {
257+ %op1 = transform.structured.match ops {[" moved_op_1" ]} in %arg0
258+ : (!transform.any_op ) -> !transform.any_op
259+ %op2 = transform.structured.match ops {[" moved_op_2" ]} in %arg0
260+ : (!transform.any_op ) -> !transform.any_op
261+ %op3 = transform.structured.match ops {[" before" ]} in %arg0
262+ : (!transform.any_op ) -> !transform.any_op
263+ %v1 = transform.get_result %op1 [0 ] : (!transform.any_op ) -> !transform.any_value
264+ %v2 = transform.get_result %op2 [0 ] : (!transform.any_op ) -> !transform.any_value
265+ transform.test.move_value_defns %v1 , %v2 before %op3
266+ : (!transform.any_value , !transform.any_value ), !transform.any_op
267+ transform.yield
268+ }
269+ }
270+
271+ // -----
272+
273+ // Compute slice including the implicitly captured values.
274+ func.func @move_region_dependencies_values () -> f32 {
275+ %0 = " before" () : () -> (f32 )
276+ %1 = " moved_op_1" () : () -> (f32 )
277+ %2 = " moved_op_2" () ({
278+ %3 = " inner_op" (%1 ) : (f32 ) -> (f32 )
279+ " yield" (%3 ) : (f32 ) -> ()
280+ }) : () -> (f32 )
281+ return %2 : f32
282+ }
283+ // CHECK-LABEL: func @move_region_dependencies_values()
284+ // CHECK: %[[MOVED1:.+]] = "moved_op_1"
285+ // CHECK: %[[MOVED2:.+]] = "moved_op_2"
286+ // CHECK: %[[BEFORE:.+]] = "before"
287+
288+ module attributes {transform.with_named_sequence } {
289+ transform.named_sequence @__transform_main (%arg0 : !transform.any_op {transform.readonly }) {
290+ %op1 = transform.structured.match ops {[" moved_op_2" ]} in %arg0
291+ : (!transform.any_op ) -> !transform.any_op
292+ %op2 = transform.structured.match ops {[" before" ]} in %arg0
293+ : (!transform.any_op ) -> !transform.any_op
294+ %v1 = transform.get_result %op1 [0 ] : (!transform.any_op ) -> !transform.any_value
295+ transform.test.move_value_defns %v1 before %op2
296+ : (!transform.any_value ), !transform.any_op
297+ transform.yield
298+ }
299+ }
300+
301+ // -----
302+
303+ // Move operations in toplogical sort order
304+ func.func @move_values_in_topological_sort_order () -> f32 {
305+ %0 = " before" () : () -> (f32 )
306+ %1 = " moved_op_1" () : () -> (f32 )
307+ %2 = " moved_op_2" () : () -> (f32 )
308+ %3 = " moved_op_3" (%1 ) : (f32 ) -> (f32 )
309+ %4 = " moved_op_4" (%1 , %3 ) : (f32 , f32 ) -> (f32 )
310+ %5 = " moved_op_5" (%2 ) : (f32 ) -> (f32 )
311+ %6 = " foo" (%4 , %5 ) : (f32 , f32 ) -> (f32 )
312+ return %6 : f32
313+ }
314+ // CHECK-LABEL: func @move_values_in_topological_sort_order()
315+ // CHECK: %[[MOVED_1:.+]] = "moved_op_1"
316+ // CHECK-DAG: %[[MOVED_2:.+]] = "moved_op_3"(%[[MOVED_1]])
317+ // CHECK-DAG: %[[MOVED_3:.+]] = "moved_op_4"(%[[MOVED_1]], %[[MOVED_2]])
318+ // CHECK-DAG: %[[MOVED_4:.+]] = "moved_op_2"
319+ // CHECK-DAG: %[[MOVED_5:.+]] = "moved_op_5"(%[[MOVED_4]])
320+ // CHECK: %[[BEFORE:.+]] = "before"
321+ // CHECK: %[[FOO:.+]] = "foo"(%[[MOVED_3]], %[[MOVED_5]])
322+ // CHECK: return %[[FOO]]
323+
324+ module attributes {transform.with_named_sequence } {
325+ transform.named_sequence @__transform_main (%arg0 : !transform.any_op {transform.readonly }) {
326+ %op1 = transform.structured.match ops {[" moved_op_4" ]} in %arg0
327+ : (!transform.any_op ) -> !transform.any_op
328+ %op2 = transform.structured.match ops {[" moved_op_5" ]} in %arg0
329+ : (!transform.any_op ) -> !transform.any_op
330+ %op3 = transform.structured.match ops {[" before" ]} in %arg0
331+ : (!transform.any_op ) -> !transform.any_op
332+ %v1 = transform.get_result %op1 [0 ] : (!transform.any_op ) -> !transform.any_value
333+ %v2 = transform.get_result %op2 [0 ] : (!transform.any_op ) -> !transform.any_value
334+ transform.test.move_value_defns %v1 , %v2 before %op3
335+ : (!transform.any_value , !transform.any_value ), !transform.any_op
336+ transform.yield
337+ }
338+ }
339+
340+ // -----
341+
342+ // Move only those value definitions that are not dominated by insertion point
343+
344+ func.func @move_only_required_defns () -> (f32 , f32 , f32 , f32 ) {
345+ %0 = " unmoved_op" () : () -> (f32 )
346+ %1 = " dummy_op" () : () -> (f32 )
347+ %2 = " before" () : () -> (f32 )
348+ %3 = " moved_op" () : () -> (f32 )
349+ return %0 , %1 , %2 , %3 : f32 , f32 , f32 , f32
350+ }
351+ // CHECK-LABEL: func @move_only_required_defns()
352+ // CHECK: %[[UNMOVED:.+]] = "unmoved_op"
353+ // CHECK: %[[DUMMY:.+]] = "dummy_op"
354+ // CHECK: %[[MOVED:.+]] = "moved_op"
355+ // CHECK: %[[BEFORE:.+]] = "before"
356+
357+ module attributes {transform.with_named_sequence } {
358+ transform.named_sequence @__transform_main (%arg0 : !transform.any_op {transform.readonly }) {
359+ %op1 = transform.structured.match ops {[" unmoved_op" ]} in %arg0
360+ : (!transform.any_op ) -> !transform.any_op
361+ %op2 = transform.structured.match ops {[" dummy_op" ]} in %arg0
362+ : (!transform.any_op ) -> !transform.any_op
363+ %op3 = transform.structured.match ops {[" before" ]} in %arg0
364+ : (!transform.any_op ) -> !transform.any_op
365+ %op4 = transform.structured.match ops {[" moved_op" ]} in %arg0
366+ : (!transform.any_op ) -> !transform.any_op
367+ %v1 = transform.get_result %op1 [0 ] : (!transform.any_op ) -> !transform.any_value
368+ %v2 = transform.get_result %op4 [0 ] : (!transform.any_op ) -> !transform.any_value
369+ transform.test.move_value_defns %v1 , %v2 before %op3
370+ : (!transform.any_value , !transform.any_value ), !transform.any_op
371+ transform.yield
372+ }
373+ }
374+
375+ // -----
376+
377+ // Move only those value definitions that are not dominated by insertion point
378+
379+ func.func @move_only_required_defns () -> (f32 , f32 , f32 , f32 ) {
380+ %0 = " unmoved_op" () : () -> (f32 )
381+ %1 = " dummy_op" () : () -> (f32 )
382+ %2 = " before" () : () -> (f32 )
383+ %3 = " moved_op" () : () -> (f32 )
384+ return %0 , %1 , %2 , %3 : f32 , f32 , f32 , f32
385+ }
386+ // CHECK-LABEL: func @move_only_required_defns()
387+ // CHECK: %[[UNMOVED:.+]] = "unmoved_op"
388+ // CHECK: %[[DUMMY:.+]] = "dummy_op"
389+ // CHECK: %[[MOVED:.+]] = "moved_op"
390+ // CHECK: %[[BEFORE:.+]] = "before"
391+
392+ module attributes {transform.with_named_sequence } {
393+ transform.named_sequence @__transform_main (%arg0 : !transform.any_op {transform.readonly }) {
394+ %op1 = transform.structured.match ops {[" unmoved_op" ]} in %arg0
395+ : (!transform.any_op ) -> !transform.any_op
396+ %op2 = transform.structured.match ops {[" dummy_op" ]} in %arg0
397+ : (!transform.any_op ) -> !transform.any_op
398+ %op3 = transform.structured.match ops {[" before" ]} in %arg0
399+ : (!transform.any_op ) -> !transform.any_op
400+ %op4 = transform.structured.match ops {[" moved_op" ]} in %arg0
401+ : (!transform.any_op ) -> !transform.any_op
402+ %v1 = transform.get_result %op1 [0 ] : (!transform.any_op ) -> !transform.any_value
403+ %v2 = transform.get_result %op4 [0 ] : (!transform.any_op ) -> !transform.any_value
404+ transform.test.move_value_defns %v1 , %v2 before %op3
405+ : (!transform.any_value , !transform.any_value ), !transform.any_op
406+ transform.yield
407+ }
408+ }
409+
410+ // -----
411+
412+ // Check handling of block arguments
413+ func.func @move_only_required_defns () -> (f32 , f32 ) {
414+ %0 = " unmoved_op" () : () -> (f32 )
415+ cf.br ^bb0 (%0 : f32 )
416+ ^bb0 (%arg0 : f32 ) :
417+ %1 = " before" () : () -> (f32 )
418+ %2 = " moved_op" (%arg0 ) : (f32 ) -> (f32 )
419+ return %1 , %2 : f32 , f32
420+ }
421+ // CHECK-LABEL: func @move_only_required_defns()
422+ // CHECK: %[[MOVED:.+]] = "moved_op"
423+ // CHECK: %[[BEFORE:.+]] = "before"
424+
425+ module attributes {transform.with_named_sequence } {
426+ transform.named_sequence @__transform_main (%arg0 : !transform.any_op {transform.readonly }) {
427+ %op1 = transform.structured.match ops {[" before" ]} in %arg0
428+ : (!transform.any_op ) -> !transform.any_op
429+ %op2 = transform.structured.match ops {[" moved_op" ]} in %arg0
430+ : (!transform.any_op ) -> !transform.any_op
431+ %v1 = transform.get_result %op2 [0 ] : (!transform.any_op ) -> !transform.any_value
432+ transform.test.move_value_defns %v1 before %op1
433+ : (!transform.any_value ), !transform.any_op
434+ transform.yield
435+ }
436+ }
437+
438+ // -----
439+
440+ // Do not move across basic blocks
441+ func.func @no_move_across_basic_blocks () -> (f32 , f32 ) {
442+ %0 = " unmoved_op" () : () -> (f32 )
443+ %1 = " before" () : () -> (f32 )
444+ cf.br ^bb0 (%0 : f32 )
445+ ^bb0 (%arg0 : f32 ) :
446+ %2 = " moved_op" (%arg0 ) : (f32 ) -> (f32 )
447+ return %1 , %2 : f32 , f32
448+ }
449+
450+ module attributes {transform.with_named_sequence } {
451+ transform.named_sequence @__transform_main (%arg0 : !transform.any_op {transform.readonly }) {
452+ %op1 = transform.structured.match ops {[" before" ]} in %arg0
453+ : (!transform.any_op ) -> !transform.any_op
454+ %op2 = transform.structured.match ops {[" moved_op" ]} in %arg0
455+ : (!transform.any_op ) -> !transform.any_op
456+ %v1 = transform.get_result %op2 [0 ] : (!transform.any_op ) -> !transform.any_value
457+ // expected-remark@+1{{unsupported case of moving definition of value before an insertion point in a different basic block}}
458+ transform.test.move_value_defns %v1 before %op1
459+ : (!transform.any_value ), !transform.any_op
460+ transform.yield
461+ }
462+ }
0 commit comments