[Mlir-commits] [mlir] [mlir] Extend moveValueDefinitions/moveOperationDependencies with cross-region support (PR #176343)

Jorn Tuyls llvmlistbot at llvm.org
Fri Jan 30 02:48:30 PST 2026


https://github.com/jtuyls updated https://github.com/llvm/llvm-project/pull/176343

>From 0ed181762cca8e93cd9926d0fa4d9c1def3855ef Mon Sep 17 00:00:00 2001
From: Jorn Tuyls <jorn.tuyls at gmail.com>
Date: Fri, 16 Jan 2026 05:13:34 -0600
Subject: [PATCH] [mlir] Extend moveValueDefinitions and
 moveOperationDependencies with cross-region support

---
 mlir/include/mlir/Transforms/RegionUtils.h    |   35 +-
 mlir/lib/Transforms/Utils/RegionUtils.cpp     |  144 +-
 mlir/test/Transforms/move-operation-deps.mlir | 1190 +++++++++++------
 3 files changed, 928 insertions(+), 441 deletions(-)

diff --git a/mlir/include/mlir/Transforms/RegionUtils.h b/mlir/include/mlir/Transforms/RegionUtils.h
index f78d0bfbe0725..f8db72aabefe3 100644
--- a/mlir/include/mlir/Transforms/RegionUtils.h
+++ b/mlir/include/mlir/Transforms/RegionUtils.h
@@ -71,11 +71,18 @@ SmallVector<Value> makeRegionIsolatedFromAbove(
     llvm::function_ref<bool(Operation *)> cloneOperationIntoRegion =
         [](Operation *) { return false; });
 
-/// Move SSA values used within an operation before an insertion point,
-/// so that the operation itself (or its replacement) can be moved to
-/// the insertion point. Current support is only for movement of
-/// dependencies of `op` before `insertionPoint` in the same basic block.
-/// Any side-effecting operations in the dependency chain pessimistically
+/// Move the operation dependencies (producers) of `op` before `insertionPoint`,
+/// so that `op` itself can subsequently be moved. This includes transitive
+/// dependencies. Supports movement within the same block or from nested regions
+/// to an outer block.
+///
+/// The following conditions cause the move to fail:
+/// - `insertionPoint` does not dominate `op`.
+/// - Movement across an isolated-from-above region boundary.
+/// - A dependency uses a block argument that wouldn't dominate
+/// `insertionPoint`.
+/// - `insertionPoint` is itself a dependency of `op` (cycle).
+/// - Any side-effecting operations in the dependency chain pessimistically
 /// blocks movement.
 LogicalResult moveOperationDependencies(RewriterBase &rewriter, Operation *op,
                                         Operation *insertionPoint,
@@ -83,11 +90,19 @@ LogicalResult moveOperationDependencies(RewriterBase &rewriter, Operation *op,
 LogicalResult moveOperationDependencies(RewriterBase &rewriter, Operation *op,
                                         Operation *insertionPoint);
 
-/// Move definitions of `values` before an insertion point. Current support is
-/// only for movement of definitions within the same basic block. Note that this
-/// is an all-or-nothing approach. Either definitions of all values are moved
-/// before insertion point, or none of them are. Any side-effecting operations
-/// in the producer chain pessimistically blocks movement.
+/// Move definitions of `values` (and their transitive dependencies) before
+/// `insertionPoint`. Supports movement within the same block or from nested
+/// regions to an outer block.
+///
+/// This is all-or-nothing: either all definitions are moved, or none are.
+///
+/// The following conditions cause the move to fail:
+/// - Any value is a block argument (cannot be moved).
+/// - Any side-effecting operations in the dependency chain.
+/// - Movement across an isolated-from-above region boundary.
+/// - A dependency uses a block argument that wouldn't dominate
+/// `insertionPoint`.
+/// - `insertionPoint` is itself a dependency (cycle).
 LogicalResult moveValueDefinitions(RewriterBase &rewriter, ValueRange values,
                                    Operation *insertionPoint,
                                    DominanceInfo &dominance);
diff --git a/mlir/lib/Transforms/Utils/RegionUtils.cpp b/mlir/lib/Transforms/Utils/RegionUtils.cpp
index 29f059a966903..5406a51d2ab7f 100644
--- a/mlir/lib/Transforms/Utils/RegionUtils.cpp
+++ b/mlir/lib/Transforms/Utils/RegionUtils.cpp
@@ -1096,31 +1096,105 @@ LogicalResult mlir::simplifyRegions(RewriterBase &rewriter,
 // Move operation dependencies
 //===---------------------------------------------------------------------===//
 
+/// Check if moving operations in the slice before `insertionPoint` would break
+/// dominance due to block argument operands. Returns true if all block args
+/// dominate the insertion point (no issue), false otherwise. If `failingOp` is
+/// provided, it will be set to the first problematic op.
+///
+/// For operands defined by ops: either the defining op is in the slice (so
+/// dominance preserved), or it already dominates insertionPoint (otherwise it
+/// would be in the slice). So we only need to check block argument operands,
+/// both as direct operands and as values captured inside regions.
+static bool blockArgsDominateInsertionPoint(
+    const llvm::SetVector<Operation *> &slice, Operation *insertionPoint,
+    DominanceInfo &dominance, Operation **failingOp = nullptr) {
+  Block *insertionBlock = insertionPoint->getBlock();
+
+  // Returns true if the block arg dominates, false otherwise. Sets failingOp
+  // on failure.
+  auto argDominates = [&](BlockArgument arg, Operation *op) {
+    Block *argBlock = arg.getOwner();
+    bool dominates = argBlock == insertionBlock ||
+                     dominance.dominates(argBlock, insertionBlock);
+    if (!dominates && failingOp)
+      *failingOp = op;
+    return dominates;
+  };
+
+  for (Operation *op : slice) {
+    // Check direct operands.
+    for (Value operand : op->getOperands()) {
+      auto arg = dyn_cast<BlockArgument>(operand);
+      if (!arg)
+        continue;
+      if (!argDominates(arg, op))
+        return false;
+    }
+
+    // Check block arguments captured inside regions. Process one region at a
+    // time to enable early exit without collecting values from all regions.
+    for (Region &region : op->getRegions()) {
+      SetVector<Value> capturedValues;
+      getUsedValuesDefinedAbove(region, region, capturedValues);
+      for (Value val : capturedValues) {
+        auto arg = dyn_cast<BlockArgument>(val);
+        if (!arg)
+          continue;
+        if (!argDominates(arg, op))
+          return false;
+      }
+    }
+  }
+  return true;
+}
+
+/// Check if any region between an operation and an ancestor block is
+/// isolated from above. If so, moving the operation out would break
+/// the isolation semantics.
+static bool hasIsolatedRegionBetween(Operation *op, Block *ancestorBlock) {
+  Region *ancestorRegion = ancestorBlock->getParent();
+
+  // Walk up from the op's region to find if there's an isolated region
+  // between the op and the ancestor.
+  Region *region = op->getParentRegion();
+  while (region && region != ancestorRegion) {
+    Operation *parentOp = region->getParentOp();
+    if (!parentOp)
+      break;
+
+    if (parentOp->hasTrait<OpTrait::IsIsolatedFromAbove>())
+      return true;
+
+    region = parentOp->getParentRegion();
+  }
+  return false;
+}
+
 LogicalResult mlir::moveOperationDependencies(RewriterBase &rewriter,
                                               Operation *op,
                                               Operation *insertionPoint,
                                               DominanceInfo &dominance) {
-  // Currently unsupported case where the op and insertion point are
-  // in different basic blocks.
-  if (op->getBlock() != insertionPoint->getBlock()) {
-    return rewriter.notifyMatchFailure(
-        op, "unsupported case where operation and insertion point are not in "
-            "the same basic block");
-  }
-  // If `insertionPoint` does not dominate `op`, do nothing
+  Block *insertionBlock = insertionPoint->getBlock();
+
+  // If `insertionPoint` does not dominate `op`, do nothing.
   if (!dominance.properlyDominates(insertionPoint, op)) {
     return rewriter.notifyMatchFailure(op,
                                        "insertion point does not dominate op");
   }
 
+  // Verify we're not crossing an isolated region.
+  if (hasIsolatedRegionBetween(op, insertionBlock)) {
+    return rewriter.notifyMatchFailure(
+        op, "cannot move operation across isolated-from-above region");
+  }
+
   // Find the backward slice of operation for each `Value` the operation
   // depends on. Prune the slice to only include operations not already
   // dominated by the `insertionPoint`.
   BackwardSliceOptions options;
   options.inclusive = false;
   options.omitUsesFromAbove = false;
-  // Since current support is to only move within a same basic block,
-  // the slices dont need to look past block arguments.
+  // Block arguments cannot be moved; dominance check handles this case.
   options.omitBlockArguments = true;
   bool dependsOnSideEffectingOp = false;
   options.filter = [&](Operation *sliceBoundaryOp) {
@@ -1158,6 +1232,15 @@ LogicalResult mlir::moveOperationDependencies(RewriterBase &rewriter,
         "cannot move dependencies before operation in backward slice of op");
   }
 
+  // Verify no operation in the slice uses a block argument that wouldn't
+  // dominate at the new location.
+  Operation *badOp = nullptr;
+  if (!blockArgsDominateInsertionPoint(slice, insertionPoint, dominance,
+                                       &badOp)) {
+    return rewriter.notifyMatchFailure(
+        badOp, "moving op would break dominance for block argument operand");
+  }
+
   // We should move the slice in topological order, but `getBackwardSlice`
   // already does that. So no need to sort again.
   for (Operation *op : slice) {
@@ -1188,13 +1271,26 @@ LogicalResult mlir::moveValueDefinitions(RewriterBase &rewriter,
           insertionPoint,
           "unsupported case of moving block argument before insertion point");
     }
-    // Check for currently unsupported case if the insertion point is in a
-    // different block.
-    if (value.getDefiningOp()->getBlock() != insertionPoint->getBlock()) {
+
+    Block *insertionBlock = insertionPoint->getBlock();
+    Operation *definingOp = value.getDefiningOp();
+    Block *definingBlock = definingOp->getBlock();
+
+    // Verify we're not crossing an isolated region.
+    if (hasIsolatedRegionBetween(definingOp, insertionBlock)) {
       return rewriter.notifyMatchFailure(
           insertionPoint,
-          "unsupported case of moving definition of value before an insertion "
-          "point in a different basic block");
+          "cannot move value definition across isolated-from-above region");
+    }
+
+    // Verify the insertion point's block dominates the defining block,
+    // otherwise we're trying to move "backwards" in the CFG which doesn't
+    // make sense.
+    if (!dominance.dominates(insertionBlock, definingBlock)) {
+      return rewriter.notifyMatchFailure(
+          insertionPoint,
+          "insertion point block does not dominate the value's defining "
+          "block");
     }
     prunedValues.push_back(value);
   }
@@ -1205,8 +1301,9 @@ LogicalResult mlir::moveValueDefinitions(RewriterBase &rewriter,
   BackwardSliceOptions options;
   options.inclusive = true;
   options.omitUsesFromAbove = false;
-  // Since current support is to only move within a same basic block,
-  // the slices dont need to look past block arguments.
+  // Block arguments cannot be moved, so we stop the slice computation there.
+  // If an op uses a block argument that wouldn't dominate at the new location,
+  // the dominance check will catch it.
   options.omitBlockArguments = true;
   bool dependsOnSideEffectingOp = false;
   options.filter = [&](Operation *sliceBoundaryOp) {
@@ -1243,9 +1340,20 @@ LogicalResult mlir::moveValueDefinitions(RewriterBase &rewriter,
         "cannot move dependencies before operation in backward slice of op");
   }
 
-  // Sort operations topologically before moving.
+  // Sort operations topologically. This is needed because we call
+  // getBackwardSlice multiple times (once per value), and the combined slice
+  // may not be in topological order when independent subgraphs interleave.
   mlir::topologicalSort(slice);
 
+  // Verify no operation in the slice uses a block argument that wouldn't
+  // dominate at the new location.
+  Operation *badOp = nullptr;
+  if (!blockArgsDominateInsertionPoint(slice, insertionPoint, dominance,
+                                       &badOp)) {
+    return rewriter.notifyMatchFailure(
+        badOp, "moving op would break dominance for block argument operand");
+  }
+
   for (Operation *op : slice)
     rewriter.moveOpBefore(op, insertionPoint);
   return success();
diff --git a/mlir/test/Transforms/move-operation-deps.mlir b/mlir/test/Transforms/move-operation-deps.mlir
index c6ef358e69822..f20413f32f205 100644
--- a/mlir/test/Transforms/move-operation-deps.mlir
+++ b/mlir/test/Transforms/move-operation-deps.mlir
@@ -1,111 +1,116 @@
-// RUN: mlir-opt --allow-unregistered-dialect --transform-interpreter --split-input-file --verify-diagnostics %s | FileCheck %s
+// RUN: mlir-opt --allow-unregistered-dialect --transform-interpreter
+// --split-input-file --verify-diagnostics %s | FileCheck %s
 
 // Check simple move of dependent operation before insertion.
-func.func @simple_move(%arg0 : f32) -> f32 {
-  %0 = "before"() : () -> (f32)
-  %1 = arith.addf %arg0, %arg0 {moved_op} : f32
-  %2 = "foo"(%1) : (f32) -> (f32)
-  return %2 : f32
-}
-// CHECK-LABEL: func @simple_move(
-//       CHECK:   %[[MOVED:.+]] = arith.addf {{.*}} {moved_op}
-//       CHECK:   %[[BEFORE:.+]] = "before"
-//       CHECK:   %[[FOO:.+]] = "foo"(%[[MOVED]])
-//       CHECK:   return %[[FOO]]
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+func.func @simple_move(% arg0
+                       : f32)
+    ->f32{
+      % 0 = "before"() : ()->(f32) % 1 = arith.addf % arg0,
+      % arg0{moved_op} : f32 % 2 = "foo"(% 1) : (f32)->(f32) return % 2 : f32
+    } // CHECK-LABEL: func @simple_move(
+      //       CHECK:   %[[MOVED:.+]] = arith.addf {{.*}} {moved_op}
+      //       CHECK:   %[[BEFORE:.+]] = "before"
+      //       CHECK:   %[[FOO:.+]] = "foo"(%[[MOVED]])
+      //       CHECK:   return %[[FOO]]
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match ops{["before"]} in %
+                  arg0
+        : (!transform.any_op)
+              ->!transform.any_op transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
 // Move operands that are implicitly captured by the op
-func.func @move_region_dependencies(%arg0 : f32) -> f32 {
-  %0 = "before"() : () -> (f32)
-  %1 = arith.addf %arg0, %arg0 {moved_op} : f32
-  %2 = "foo"() ({
-    %3 = arith.mulf %1, %1 : f32
-    "yield"(%3) : (f32) -> ()
-  }) : () -> (f32)
-  return %2 : f32
-}
-// CHECK-LABEL: func @move_region_dependencies(
-//       CHECK:   %[[MOVED:.+]] = arith.addf {{.*}} {moved_op}
-//       CHECK:   %[[BEFORE:.+]] = "before"
-//       CHECK:   %[[FOO:.+]] = "foo"
-//       CHECK:   return %[[FOO]]
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+func.func @move_region_dependencies(% arg0
+                                    : f32)
+    ->
+f32{
+  % 0 = "before"() : ()->(f32) % 1 = arith.addf % arg0,
+  % arg0{moved_op} : f32 %
+      2 = "foo"()({ % 3 = arith.mulf % 1, % 1 : f32 "yield"(% 3) : (f32)->()}) :
+              ()->(f32) return %
+          2 : f32
+} // CHECK-LABEL: func @move_region_dependencies(
+  //       CHECK:   %[[MOVED:.+]] = arith.addf {{.*}} {moved_op}
+  //       CHECK:   %[[BEFORE:.+]] = "before"
+  //       CHECK:   %[[FOO:.+]] = "foo"
+  //       CHECK:   return %[[FOO]]
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match ops{["before"]} in %
+                  arg0
+        : (!transform.any_op)
+              ->!transform.any_op transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
 // Move operations in toplogical sort order
-func.func @move_in_topological_sort_order(%arg0 : f32, %arg1 : f32) -> f32 {
-  %0 = "before"() : () -> (f32)
-  %1 = arith.addf %arg0, %arg0 {moved_op_1} : f32
-  %2 = arith.addf %arg1, %arg1 {moved_op_2} : f32
-  %3 = arith.mulf %1, %1 {moved_op_3} : f32
-  %4 = arith.mulf %1, %3 {moved_op_4} : f32
-  %5 = arith.mulf %2, %2 {moved_op_5} : f32
-  %6 = "foo"(%4, %5) : (f32, f32) -> (f32)
-  return %6 : f32
-}
-// CHECK-LABEL: func @move_in_topological_sort_order(
-//       CHECK:   %[[MOVED_1:.+]] = arith.addf {{.*}} {moved_op_1}
-//   CHECK-DAG:   %[[MOVED_2:.+]] = arith.mulf %[[MOVED_1]], %[[MOVED_1]] {moved_op_3}
-//   CHECK-DAG:   %[[MOVED_3:.+]] = arith.mulf %[[MOVED_1]], %[[MOVED_2]] {moved_op_4}
-//   CHECK-DAG:   %[[MOVED_4:.+]] = arith.addf {{.*}} {moved_op_2}
-//   CHECK-DAG:   %[[MOVED_5:.+]] = arith.mulf %[[MOVED_4]], %[[MOVED_4]] {moved_op_5}
-//       CHECK:   %[[BEFORE:.+]] = "before"
-//       CHECK:   %[[FOO:.+]] = "foo"(%[[MOVED_3]], %[[MOVED_5]])
-//       CHECK:   return %[[FOO]]
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+func.func @move_in_topological_sort_order(% arg0
+                                          : f32, % arg1
+                                          : f32)
+    ->f32{
+      % 0 = "before"() : ()->(f32) % 1 = arith.addf % arg0,
+      % arg0{moved_op_1} : f32 % 2 = arith.addf % arg1,
+      % arg1{moved_op_2} : f32 % 3 = arith.mulf % 1,
+      % 1 {moved_op_3} : f32 % 4 = arith.mulf % 1,
+      % 3 {moved_op_4} : f32 % 5 = arith.mulf % 2,
+      % 2 {moved_op_5} : f32 % 6 = "foo"(% 4, % 5) : (f32, f32)->(f32) return %
+                                   6 : f32
+    } // CHECK-LABEL: func @move_in_topological_sort_order(
+      //       CHECK:   %[[MOVED_1:.+]] = arith.addf {{.*}} {moved_op_1}
+      //   CHECK-DAG:   %[[MOVED_2:.+]] = arith.mulf %[[MOVED_1]], %[[MOVED_1]]
+      //   {moved_op_3} CHECK-DAG:   %[[MOVED_3:.+]] = arith.mulf %[[MOVED_1]],
+      //   %[[MOVED_2]] {moved_op_4} CHECK-DAG:   %[[MOVED_4:.+]] = arith.addf
+      //   {{.*}} {moved_op_2} CHECK-DAG:   %[[MOVED_5:.+]] = arith.mulf
+      //   %[[MOVED_4]], %[[MOVED_4]] {moved_op_5}
+      //       CHECK:   %[[BEFORE:.+]] = "before"
+      //       CHECK:   %[[FOO:.+]] = "foo"(%[[MOVED_3]], %[[MOVED_5]])
+      //       CHECK:   return %[[FOO]]
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match ops{["before"]} in %
+                  arg0
+        : (!transform.any_op)
+              ->!transform.any_op transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
-func.func @move_region_dependencies(%arg0 : f32) -> f32 {
-  %cond = arith.constant true
-  %0 = "before"() : () -> (f32)
-  %1 = arith.addf %arg0, %arg0 {moved_op_1} : f32
-  %2 = scf.if %cond -> f32 {
-    %inner = arith.mulf %1, %1 : f32
-    scf.yield %inner : f32
-  } else {
-    scf.yield %1 : f32
-  }
-  %3 = "foo"() ({
-    "yield"(%2) : (f32) -> ()
-  }) : () -> (f32)
-  return %3 : f32
+func.func @move_region_dependencies(% arg0 : f32)->f32 {
+  % cond = arith.constant true % 0 = "before"()
+      : ()->(f32) % 1 = arith.addf % arg0,
+    % arg0{moved_op_1} : f32 % 2 = scf.if % cond->f32 {
+    % inner = arith.mulf % 1, % 1 : f32 scf.yield % inner : f32
+  }
+  else {scf.yield % 1 : f32} % 3 =
+      "foo"()({"yield"(% 2) : (f32)->()}) : ()->(f32) return % 3 : f32
 }
 // CHECK-LABEL: func @move_region_dependencies(
 //       CHECK:   arith.constant true
@@ -115,15 +120,18 @@ func.func @move_region_dependencies(%arg0 : f32) -> f32 {
 //       CHECK:   %[[FOO:.+]] = "foo"
 //       CHECK:   return %[[FOO]]
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match ops{["before"]} in %
+                  arg0
+        : (!transform.any_op)
+              ->!transform.any_op transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
@@ -131,23 +139,20 @@ module attributes {transform.with_named_sequence} {
 
 // Current implementation omits following basic block argument when
 // computing slices. Verify that this gives expected result.
-func.func @ignore_basic_block_arguments() -> f32 {
-^bb0():
-  %8 = "test"() : () -> (f32)
-  return %8: f32
-^bb1(%bbArg : f32):
-  %cond = arith.constant true
-  %0 = "before"() : () -> (f32)
-  %1 = scf.if %cond -> f32 {
-    %inner = arith.addf %bbArg, %bbArg : f32
-    scf.yield %inner : f32
-  } else {
-    scf.yield %bbArg : f32
-  }
-  %2 = "foo"() ({
-    "yield"(%1) : (f32) -> ()
-  }) : () -> (f32)
-  return %2 : f32
+func.func @ignore_basic_block_arguments()->f32 {
+  ^bb0()
+      : %
+      8 = "test"()
+      : ()->(f32) return %
+              8 : f32 ^
+          bb1(% bbArg
+              : f32)
+      : % cond = arith.constant true % 0 =
+                     "before"() : ()->(f32) % 1 = scf.if % cond->f32 {
+    % inner = arith.addf % bbArg, % bbArg : f32 scf.yield % inner : f32
+  }
+  else {scf.yield % bbArg : f32} % 2 =
+      "foo"()({"yield"(% 1) : (f32)->()}) : ()->(f32) return % 2 : f32
 }
 // CHECK-LABEL: func @ignore_basic_block_arguments()
 //       CHECK:   arith.constant true
@@ -156,38 +161,44 @@ func.func @ignore_basic_block_arguments() -> f32 {
 //       CHECK:   %[[FOO:.+]] = "foo"
 //       CHECK:   return %[[FOO]]
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match ops{["before"]} in %
+                  arg0
+        : (!transform.any_op)
+              ->!transform.any_op transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
 // Fail when the "before" operation is part of the operation slice.
-func.func @do_not_move_slice() -> f32 {
-  %0 = arith.constant {before} 1.0 : f32
-  %1 = arith.addf %0, %0 {moved_op} : f32
-  %2 = "foo"(%1) : (f32) -> (f32)
-  return %2 : f32
-}
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match attributes{before} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    // expected-remark at +1{{cannot move dependencies before operation in backward slice of op}}
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+func.func @do_not_move_slice()->f32{
+  % 0 = arith.constant{before} 1.0 : f32 % 1 = arith.addf % 0,
+  % 0 {moved_op} : f32 % 2 = "foo"(% 1) : (f32)->(f32) return % 2 : f32
+}
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match attributes{before} in %
+                  arg0 : (!transform.any_op)
+                             ->!transform
+                             .any_op
+                                 // expected-remark at +1{{cannot move dependencies
+                                 // before operation in backward slice of op}}
+                                     transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
@@ -195,106 +206,117 @@ module attributes {transform.with_named_sequence} {
 
 // Fail when the "before" operation is part of the operation slice (computed
 // when looking through implicitly captured values).
-func.func @do_not_move_slice_region() -> f32 {
-  %cond = arith.constant true
-  %0 = arith.constant {before} 1.0 : f32
-  %1 = scf.if %cond -> f32 {
-    %inner = arith.addf %0, %0 : f32
-    scf.yield %inner : f32
-  } else {
-    scf.yield %0 : f32
-  }
-  %2 = "foo"() ({
-    "yield"(%1) : (f32) -> ()
-  }) : () -> (f32)
-  return %2 : f32
-}
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match attributes{before} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    // expected-remark at +1{{cannot move dependencies before operation in backward slice of op}}
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+func.func @do_not_move_slice_region()->f32 {
+  % cond = arith.constant true % 0 = arith.constant { before }
+  1.0 : f32 % 1 = scf.if % cond->f32 {
+    % inner = arith.addf % 0, % 0 : f32 scf.yield % inner : f32
+  }
+  else {scf.yield % 0 : f32} % 2 =
+      "foo"()({"yield"(% 1) : (f32)->()}) : ()->(f32) return % 2 : f32
+}
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match attributes{before} in %
+                  arg0 : (!transform.any_op)
+                             ->!transform
+                             .any_op
+                                 // expected-remark at +1{{cannot move dependencies
+                                 // before operation in backward slice of op}}
+                                     transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
 // Dont move ops when insertion point does not dominate the op
-func.func @do_not_move(%arg0 : f32) -> f32 {
-  %1 = arith.addf %arg0, %arg0 {moved_op} : f32
-  %2 = "foo"() ({
-    "yield"(%1) : (f32) -> ()
-  }) : () -> (f32)
-  %3 = "before"() : () -> f32
-  return %2 : f32
-}
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    // expected-remark at +1{{insertion point does not dominate op}}
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+func.func @do_not_move(% arg0
+                       : f32)
+    ->
+f32{
+  % 1 = arith.addf % arg0,
+  % arg0{moved_op} : f32 % 2 = "foo"()({"yield"(% 1) : (f32)->()}) : ()->(f32) %
+                               3 = "before"() : ()->f32 return % 2 : f32
+}
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match ops{["before"]} in %
+                  arg0
+        : (!transform.any_op)
+              ->!transform
+              .any_op
+                  // expected-remark at +1{{insertion point does not dominate op}}
+                  transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
 // Check simple move value definitions before insertion operation.
-func.func @simple_move_values(%arg0 : index) -> index {
-  %c0 = arith.constant 0 : index
-  %0 = "before"() : () -> (index)
-  %1 = arith.addi %arg0, %c0 {"moved_op_1"} : index
-  %2 = arith.subi %arg0, %c0 {"moved_op_2"} : index
-  %3 = "foo"(%1, %2) : (index, index) -> (index)
-  return %3 : index
-}
-// CHECK-LABEL: func @simple_move_values(
-//       CHECK:   %[[MOVED1:.+]] = arith.addi {{.*}} {moved_op_1}
-//       CHECK:   %[[MOVED2:.+]] = arith.subi {{.*}} {moved_op_2}
-//       CHECK:   %[[BEFORE:.+]] = "before"
-//       CHECK:   %[[FOO:.+]] = "foo"(%[[MOVED1]], %[[MOVED2]])
-//       CHECK:   return %[[FOO]]
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["arith.addi"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["arith.subi"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op3 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
-    %v2 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
-    transform.test.move_value_defns %v1, %v2 before %op3
-        : (!transform.any_value, !transform.any_value), !transform.any_op
-    transform.yield
+func.func @simple_move_values(% arg0
+                              : index)
+    ->index{
+      % c0 = arith.constant 0 : index % 0 = "before"() : ()->(index) %
+                                            1 = arith.addi % arg0,
+      % c0{"moved_op_1"} : index % 2 = arith.subi % arg0,
+      % c0{"moved_op_2"} : index %
+          3 = "foo"(% 1, % 2) : (index, index)->(index) return % 3 : index
+    } // CHECK-LABEL: func @simple_move_values(
+      //       CHECK:   %[[MOVED1:.+]] = arith.addi {{.*}} {moved_op_1}
+      //       CHECK:   %[[MOVED2:.+]] = arith.subi {{.*}} {moved_op_2}
+      //       CHECK:   %[[BEFORE:.+]] = "before"
+      //       CHECK:   %[[FOO:.+]] = "foo"(%[[MOVED1]], %[[MOVED2]])
+      //       CHECK:   return %[[FOO]]
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["arith.addi"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match ops{["arith.subi"]} in %
+                arg0 : (!transform.any_op)->!transform.any_op % op3 =
+                    transform.structured.match ops{["before"]} in %
+                    arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                        transform.get_result %
+                        op1[0] : (!transform.any_op)->!transform.any_value %
+                        v2 = transform.get_result %
+                             op2[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                             v1,
+      % v2 before % op3 : (!transform.any_value, !transform.any_value),
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
 // Compute slice including the implicitly captured values.
-func.func @move_region_dependencies_values(%arg0 : index, %cond : i1) -> index {
-  %0 = "before"() : () -> (index)
-  %1 = arith.addi %arg0, %arg0 {moved_op_1} : index
-  %2 = scf.if %cond -> index {
-    %3 = arith.muli %1, %1 {inner_op} : index
-    scf.yield %3 : index
-  } else {
-    scf.yield %1 : index
+func.func @move_region_dependencies_values(% arg0 : index, % cond : i1)->index {
+  % 0 = "before"()
+      : ()->(index) %
+        1 = arith.addi % arg0,
+    % arg0{moved_op_1} : index % 2 = scf.if % cond->index {
+    % 3 = arith.muli % 1, % 1 {inner_op} : index scf.yield % 3 : index
+  }
+  else {
+    scf.yield % 1 : index
   }
-  return %2 : index
+  return % 2 : index
 }
 // CHECK-LABEL: func @move_region_dependencies_values(
 //       CHECK:   %[[MOVED1:.+]] = arith.addi {{.*}} {moved_op_1}
@@ -302,252 +324,594 @@ func.func @move_region_dependencies_values(%arg0 : index, %cond : i1) -> index {
 //       CHECK:     arith.muli %[[MOVED1]], %[[MOVED1]] {inner_op}
 //       CHECK:   %[[BEFORE:.+]] = "before"
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["scf.if"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
-    transform.test.move_value_defns %v1 before %op2
-        : (!transform.any_value), !transform.any_op
-    transform.yield
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["scf.if"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match ops{["before"]} in %
+                arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                    transform.get_result %
+                    op1[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                    v1 before % op2 : (!transform.any_value),
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
 // Move operations in toplogical sort order
-func.func @move_values_in_topological_sort_order(%arg0 : index, %arg1 : index) -> index {
-  %0 = "before"() : () -> (index)
-  %1 = arith.addi %arg0, %arg0 {moved_op_1} : index
-  %2 = arith.addi %arg1, %arg1 {moved_op_2} : index
-  %3 = arith.muli %1, %1 {moved_op_3} : index
-  %4 = arith.andi %1, %3 {moved_op_4} : index
-  %5 = arith.subi %2, %2 {moved_op_5} : index
-  %6 = "foo"(%4, %5) : (index, index) -> (index)
-  return %6 : index
-}
-// CHECK-LABEL: func @move_values_in_topological_sort_order(
-//       CHECK:   %[[MOVED_1:.+]] = arith.addi {{.*}} {moved_op_1}
-//   CHECK-DAG:   %[[MOVED_2:.+]] = arith.muli %[[MOVED_1]], %[[MOVED_1]] {moved_op_3}
-//   CHECK-DAG:   %[[MOVED_3:.+]] = arith.andi %[[MOVED_1]], %[[MOVED_2]] {moved_op_4}
-//   CHECK-DAG:   %[[MOVED_4:.+]] = arith.addi {{.*}} {moved_op_2}
-//   CHECK-DAG:   %[[MOVED_5:.+]] = arith.subi %[[MOVED_4]], %[[MOVED_4]] {moved_op_5}
-//       CHECK:   %[[BEFORE:.+]] = "before"
-//       CHECK:   %[[FOO:.+]] = "foo"(%[[MOVED_3]], %[[MOVED_5]])
-//       CHECK:   return %[[FOO]]
+func.func @move_values_in_topological_sort_order(% arg0
+                                                 : index, % arg1
+                                                 : index)
+    ->index{
+      % 0 = "before"() : ()->(index) % 1 = arith.addi % arg0,
+      % arg0{moved_op_1} : index % 2 = arith.addi % arg1,
+      % arg1{moved_op_2} : index % 3 = arith.muli % 1,
+      % 1 {moved_op_3} : index % 4 = arith.andi % 1,
+      % 3 {moved_op_4} : index % 5 = arith.subi % 2,
+      % 2 {moved_op_5} : index %
+          6 = "foo"(% 4, % 5) : (index, index)->(index) return % 6 : index
+    } // CHECK-LABEL: func @move_values_in_topological_sort_order(
+      //       CHECK:   %[[MOVED_1:.+]] = arith.addi {{.*}} {moved_op_1}
+      //   CHECK-DAG:   %[[MOVED_2:.+]] = arith.muli %[[MOVED_1]], %[[MOVED_1]]
+      //   {moved_op_3} CHECK-DAG:   %[[MOVED_3:.+]] = arith.andi %[[MOVED_1]],
+      //   %[[MOVED_2]] {moved_op_4} CHECK-DAG:   %[[MOVED_4:.+]] = arith.addi
+      //   {{.*}} {moved_op_2} CHECK-DAG:   %[[MOVED_5:.+]] = arith.subi
+      //   %[[MOVED_4]], %[[MOVED_4]] {moved_op_5}
+      //       CHECK:   %[[BEFORE:.+]] = "before"
+      //       CHECK:   %[[FOO:.+]] = "foo"(%[[MOVED_3]], %[[MOVED_5]])
+      //       CHECK:   return %[[FOO]]
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["arith.andi"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match ops{["arith.subi"]} in %
+                arg0 : (!transform.any_op)->!transform.any_op % op3 =
+                    transform.structured.match ops{["before"]} in %
+                    arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                        transform.get_result %
+                        op1[0] : (!transform.any_op)->!transform.any_value %
+                        v2 = transform.get_result %
+                             op2[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                             v1,
+      % v2 before % op3 : (!transform.any_value, !transform.any_value),
+      !transform.any_op transform.yield
+  }
+}
+
+// -----
+
+// Move only those value definitions that are not dominated by insertion point
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["arith.andi"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["arith.subi"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op3 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
-    %v2 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
-    transform.test.move_value_defns %v1, %v2 before %op3
-        : (!transform.any_value, !transform.any_value), !transform.any_op
-    transform.yield
+func.func @move_only_required_defns(% arg0
+                                    : index)
+    ->(index, index, index, index){
+      % 0 = "unmoved_op"() : ()->(index) % 1 = "dummy_op"() : ()->(index) %
+                                               2 = "before"() : ()->(index) %
+                                                   3 = arith.addi % arg0,
+      % arg0{moved_op} : index return % 0,
+      % 1,
+      % 2,
+      % 3 : index,
+      index,
+      index,
+      index
+    } // CHECK-LABEL: func @move_only_required_defns(
+      //       CHECK:   %[[UNMOVED:.+]] = "unmoved_op"
+      //       CHECK:   %[[DUMMY:.+]] = "dummy_op"
+      //       CHECK:   %[[MOVED:.+]] = arith.addi {{.*}} {moved_op}
+      //       CHECK:   %[[BEFORE:.+]] = "before"
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["unmoved_op"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match ops{["dummy_op"]} in %
+                arg0 : (!transform.any_op)->!transform.any_op % op3 =
+                    transform.structured.match ops{["before"]} in %
+                    arg0 : (!transform.any_op)->!transform.any_op % op4 =
+                        transform.structured.match ops{["arith.addi"]} in %
+                        arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                            transform.get_result %
+                            op1[0] : (!transform.any_op)->!transform.any_value %
+                            v2 = transform.get_result %
+                                 op4[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                                 v1,
+      % v2 before % op3 : (!transform.any_value, !transform.any_value),
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
 // Move only those value definitions that are not dominated by insertion point
+// (duplicate test)
+
+func.func @move_only_required_defns_2(% arg0
+                                      : index)
+    ->(index, index, index, index){
+      % 0 = "unmoved_op"() : ()->(index) % 1 = "dummy_op"() : ()->(index) %
+                                               2 = "before"() : ()->(index) %
+                                                   3 = arith.subi % arg0,
+      % arg0{moved_op} : index return % 0,
+      % 1,
+      % 2,
+      % 3 : index,
+      index,
+      index,
+      index
+    } // CHECK-LABEL: func @move_only_required_defns_2(
+      //       CHECK:   %[[UNMOVED:.+]] = "unmoved_op"
+      //       CHECK:   %[[DUMMY:.+]] = "dummy_op"
+      //       CHECK:   %[[MOVED:.+]] = arith.subi {{.*}} {moved_op}
+      //       CHECK:   %[[BEFORE:.+]] = "before"
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["unmoved_op"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match ops{["dummy_op"]} in %
+                arg0 : (!transform.any_op)->!transform.any_op % op3 =
+                    transform.structured.match ops{["before"]} in %
+                    arg0 : (!transform.any_op)->!transform.any_op % op4 =
+                        transform.structured.match ops{["arith.subi"]} in %
+                        arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                            transform.get_result %
+                            op1[0] : (!transform.any_op)->!transform.any_value %
+                            v2 = transform.get_result %
+                                 op4[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                                 v1,
+      % v2 before % op3 : (!transform.any_value, !transform.any_value),
+      !transform.any_op transform.yield
+  }
+}
 
-func.func @move_only_required_defns(%arg0 : index) -> (index, index, index, index) {
-  %0 = "unmoved_op"() : () -> (index)
-  %1 = "dummy_op"() : () -> (index)
-  %2 = "before"() : () -> (index)
-  %3 = arith.addi %arg0, %arg0 {moved_op} : index
-  return %0, %1, %2, %3 : index, index, index, index
-}
-// CHECK-LABEL: func @move_only_required_defns(
-//       CHECK:   %[[UNMOVED:.+]] = "unmoved_op"
-//       CHECK:   %[[DUMMY:.+]] = "dummy_op"
-//       CHECK:   %[[MOVED:.+]] = arith.addi {{.*}} {moved_op}
-//       CHECK:   %[[BEFORE:.+]] = "before"
+// -----
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["unmoved_op"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["dummy_op"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op3 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op4 = transform.structured.match ops{["arith.addi"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
-    %v2 = transform.get_result %op4[0] : (!transform.any_op) -> !transform.any_value
-    transform.test.move_value_defns %v1, %v2 before %op3
-        : (!transform.any_value, !transform.any_value), !transform.any_op
-    transform.yield
+// Check handling of block arguments
+func.func @move_with_block_arguments()->(index, index){
+  % 0 = "unmoved_op"() : ()->(index)cf.br ^
+        bb0(% 0
+            : index) ^
+        bb0(% arg0
+            : index) :
+                % 1 = "before"() : ()->(index) % 2 = arith.addi % arg0,
+  % arg0{moved_op} : index return % 1,
+  % 2 : index,
+  index
+} // CHECK-LABEL: func @move_with_block_arguments()
+  //       CHECK:   %[[MOVED:.+]] = arith.addi {{.*}} {moved_op}
+  //       CHECK:   %[[BEFORE:.+]] = "before"
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["before"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match ops{["arith.addi"]} in %
+                arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                    transform.get_result %
+                    op2[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                    v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
+  }
+}
+
+// -----
+
+// Successfully move operation between blocks in the same region.
+// The function argument %arg0 dominates all blocks, so the move is valid.
+func.func @move_between_blocks_same_region(% arg0
+                                           : index, % cond
+                                           : i1)
+    ->index{
+      % 0 = "before"() : ()->(index)cf.cond_br % cond,
+      ^bb1,
+      ^bb2(% arg0
+           : index) ^
+          bb1 : % 1 = arith.addi % arg0,
+      %
+              arg0{
+                  to_move} : index cf.br ^
+          bb2(% 1
+              : index) ^
+          bb2(% result
+              : index) : return %
+              result : index
+    } // CHECK-LABEL: func @move_between_blocks_same_region
+      // CHECK:         %[[MOVED:.+]] = arith.addi {{.*}} {to_move}
+      // CHECK:         "before"
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["before"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match attributes{to_move} in %
+                arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                    transform.get_result %
+                    op2[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                    v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
-// Move only those value definitions that are not dominated by insertion point (duplicate test)
+//===----------------------------------------------------------------------===//
+// Cross-region move tests
+//===----------------------------------------------------------------------===//
 
-func.func @move_only_required_defns_2(%arg0 : index) -> (index, index, index, index) {
-  %0 = "unmoved_op"() : () -> (index)
-  %1 = "dummy_op"() : () -> (index)
-  %2 = "before"() : () -> (index)
-  %3 = arith.subi %arg0, %arg0 {moved_op} : index
-  return %0, %1, %2, %3 : index, index, index, index
+// Move multiple values with dependencies out of nested region
+func.func @move_chain_out_of_region(% arg0 : index, % cond : i1)->index {
+  % 0 = "before"() : ()->(index) % 1 = scf.if % cond->index {
+    % 2 = arith.addi % arg0, % arg0{dep1} : index % 3 = arith.muli % 2,
+      % 2 {dep2} : index % 4 = arith.subi % 3,
+      % arg0{to_move} : index scf.yield % 4 : index
+  }
+  else {
+    scf.yield % arg0 : index
+  }
+  return % 1 : index
 }
-// CHECK-LABEL: func @move_only_required_defns_2(
-//       CHECK:   %[[UNMOVED:.+]] = "unmoved_op"
-//       CHECK:   %[[DUMMY:.+]] = "dummy_op"
-//       CHECK:   %[[MOVED:.+]] = arith.subi {{.*}} {moved_op}
-//       CHECK:   %[[BEFORE:.+]] = "before"
+// CHECK-LABEL: func @move_chain_out_of_region(
+//       CHECK:   arith.addi {{.*}} {dep1}
+//       CHECK:   arith.muli {{.*}} {dep2}
+//       CHECK:   arith.subi {{.*}} {to_move}
+//       CHECK:   "before"
+//       CHECK:   scf.if
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["unmoved_op"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["dummy_op"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op3 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op4 = transform.structured.match ops{["arith.subi"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op1[0] : (!transform.any_op) -> !transform.any_value
-    %v2 = transform.get_result %op4[0] : (!transform.any_op) -> !transform.any_value
-    transform.test.move_value_defns %v1, %v2 before %op3
-        : (!transform.any_value, !transform.any_value), !transform.any_op
-    transform.yield
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["before"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match attributes{to_move} in %
+                arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                    transform.get_result %
+                    op2[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                    v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
-// Check handling of block arguments
-func.func @move_with_block_arguments() -> (index, index) {
-  %0 = "unmoved_op"() : () -> (index)
-  cf.br ^bb0(%0 : index)
- ^bb0(%arg0 : index) :
-  %1 = "before"() : () -> (index)
-  %2 = arith.addi %arg0, %arg0 {moved_op} : index
-  return %1, %2 : index, index
-}
-// CHECK-LABEL: func @move_with_block_arguments()
-//       CHECK:   %[[MOVED:.+]] = arith.addi {{.*}} {moved_op}
-//       CHECK:   %[[BEFORE:.+]] = "before"
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["arith.addi"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
-    transform.test.move_value_defns %v1 before %op1
-        : (!transform.any_value), !transform.any_op
-    transform.yield
+// moveValueDefinitions: cannot move when slice has side-effecting ops
+func.func @move_value_defs_side_effecting(% arg0
+                                          : memref<index>)
+    ->index{
+      % 0 = "before"() : ()->(index) %
+            1 = memref.load % arg0[] : memref<index> % 2 = arith.muli % 1,
+      % 1 {moved_op} : index return % 2 : index
+    }
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["before"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match attributes{moved_op} in %
+                arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                    transform.get_result %
+                    op2[0]
+        : (!transform.any_op)
+              ->!transform
+              .any_value
+                  // expected-remark at +1{{cannot move value definitions with
+                  // side-effecting operations in the slice}}
+                      transform.test.move_value_defns %
+                    v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
-// Do not move across basic blocks
-func.func @no_move_across_basic_blocks() -> (index, index) {
-  %0 = "unmoved_op"() : () -> (index)
-  %1 = "before"() : () -> (index)
-  cf.br ^bb0(%0 : index)
- ^bb0(%arg0 : index) :
-  %2 = arith.addi %arg0, %arg0 {moved_op} : index
-  return %1, %2 : index, index
+// moveOperationDependencies: cannot move when slice has side-effecting ops
+func.func @move_op_deps_side_effecting(% arg0
+                                       : memref<index>)
+    ->
+index{
+  % 0 = "before"() : ()->(index) % 1 = memref.load % arg0[] : memref<index> %
+                                       2 = arith.muli % 1,
+  % 1 {moved_op} : index % 3 = "foo"(% 2) : (index)->(index) return % 3 : index
+}
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match ops{["before"]} in %
+                  arg0 : (!transform.any_op)
+                             ->!transform
+                             .any_op
+                                 // expected-remark at +1{{cannot move operation
+                                 // with side-effecting dependencies}}
+                                     transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
+  }
 }
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["arith.addi"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
-    // expected-remark at +1{{unsupported case of moving definition of value before an insertion point in a different basic block}}
-    transform.test.move_value_defns %v1 before %op1
-        : (!transform.any_value), !transform.any_op
-    transform.yield
+// -----
+
+// Can move op using outer loop's IV when staying within outer loop
+func.func @move_op_using_outer_loop_iv(% lb
+                                       : index, % ub
+                                       : index, % step
+                                       : index)
+    ->index {
+  %result = scf.for %outer_iv = %lb to %ub step %step iter_args(%acc = %lb) -> index {
+    %before = "before"() : () -> (index)
+    scf.for %inner_iv = %lb to %ub step %step {
+      // Uses outer_iv which dominates within the outer loop body
+      %x = arith.addi %outer_iv, %outer_iv {to_move} : index
+      "use"(%x) : (index) -> ()
+    }
+    scf.yield %acc : index
+  }
+  return % result : index
+}
+// CHECK-LABEL: func @move_op_using_outer_loop_iv(
+//       CHECK:   scf.for %[[OUTER_IV:[a-zA-Z0-9_]+]] =
+//       CHECK:     arith.addi %[[OUTER_IV]], %[[OUTER_IV]] {to_move}
+//       CHECK:     "before"
+//       CHECK:     scf.for
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["before"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match attributes{to_move} in %
+                arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                    transform.get_result %
+                    op2[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                    v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
-func.func @move_isolated_from_above(%arg0 : index) -> () {
-  %1 = "before"() : () -> (index)
-  %2 = arith.addi %arg0, %arg0 {moved0} : index
-  %3 = arith.muli %2, %2 {moved1} : index
-  return
+// Move out of doubly nested non-isolated region
+func.func @move_out_of_doubly_nested_region(% arg0
+                                            : index, % cond1
+                                            : i1, % cond2
+                                            : i1)
+    ->index {
+  % 0 = "before"() : ()->(index) % 1 = scf.if % cond1->index {
+    % 2 = scf.if % cond2->index {
+      % 3 = arith.addi % arg0, % arg0{to_move} : index scf.yield % 3 : index
+    }
+    else {scf.yield % arg0 : index} scf.yield % 2 : index
+  }
+  else {
+    scf.yield % arg0 : index
+  }
+  return % 1 : index
 }
-// CHECK-LABEL: func @move_isolated_from_above(
-//       CHECK:   %[[MOVED0:.+]] = arith.addi {{.*}} {moved0}
-//       CHECK:   %[[MOVED1:.+]] = arith.muli %[[MOVED0]], %[[MOVED0]] {moved1}
+// CHECK-LABEL: func @move_out_of_doubly_nested_region(
+//       CHECK:   %[[MOVED:.+]] = arith.addi {{.*}} {to_move}
 //       CHECK:   %[[BEFORE:.+]] = "before"
+//       CHECK:   scf.if
+//       CHECK:     scf.if
+//       CHECK:       scf.yield %[[MOVED]]
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["before"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match attributes{to_move} in %
+                arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                    transform.get_result %
+                    op2[0]
+        : (!transform.any_op)
+              ->!transform.any_value transform.test.move_value_defns %
+                    v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
+  }
+}
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["arith.muli"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
-    transform.test.move_value_defns %v1 before %op1
-        : (!transform.any_value), !transform.any_op
-    transform.yield
+// -----
+
+// Move operand deps out of nested region
+func.func @move_operand_deps_out_of_region(% arg0 : index, % cond : i1)->index {
+  % 0 = "before"() : ()->(index) % 1 = scf.if % cond->index {
+    % 2 = arith.addi % arg0,
+      % arg0{dep} : index % 3 =
+          "foo"(% 2){target} : (index)->(index)scf.yield % 3 : index
+  }
+  else {
+    scf.yield % arg0 : index
+  }
+  return % 1 : index
+}
+// CHECK-LABEL: func @move_operand_deps_out_of_region(
+//       CHECK:   %[[DEP:.+]] = arith.addi {{.*}} {dep}
+//       CHECK:   %[[BEFORE:.+]] = "before"
+//       CHECK:   scf.if
+//       CHECK:     "foo"(%[[DEP]]) {target}
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["foo"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op %
+            op2 = transform.structured.match ops{["before"]} in %
+                  arg0
+        : (!transform.any_op)
+              ->!transform.any_op transform.test.move_operand_deps %
+                  op1 before % op2 : !transform.any_op,
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
-// moveValueDefinitions: cannot move when slice has side-effecting ops
-func.func @move_value_defs_side_effecting(%arg0 : memref<index>) -> index {
+// Cannot move op that depends on loop induction variable (block argument)
+func.func @cannot_move_op_using_loop_iv(% arg0
+                                        : index, % lb
+                                        : index, % ub
+                                        : index, % step
+                                        : index)
+    ->index {
   %0 = "before"() : () -> (index)
-  %1 = memref.load %arg0[] : memref<index>
-  %2 = arith.muli %1, %1 {moved_op} : index
-  return %2 : index
+  %1 = scf.for %iv = %lb to %ub step %step iter_args(%acc = %arg0) -> index {
+    % 2 = arith.addi % iv, % iv{to_move} : index % 3 = arith.addi % acc,
+      % 2 : index scf.yield % 3 : index
+  }
+  return % 1 : index
+}
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 =
+        transform.structured.match ops{["before"]} in %
+        arg0 : (!transform.any_op)->!transform.any_op % op2 =
+            transform.structured.match attributes{to_move} in %
+            arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                transform.get_result %
+                op2[0] : (!transform.any_op)
+                             ->!transform
+                             .any_value
+                                 // expected-remark at +1{{moving op would break
+                                 // dominance for block argument operand}}
+                                     transform.test.move_value_defns %
+                v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
+  }
 }
 
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match attributes{moved_op} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %v1 = transform.get_result %op2[0] : (!transform.any_op) -> !transform.any_value
-    // expected-remark at +1{{cannot move value definitions with side-effecting operations in the slice}}
-    transform.test.move_value_defns %v1 before %op1
-        : (!transform.any_value), !transform.any_op
-    transform.yield
+// -----
+
+// Cannot move out of an isolated-from-above region, even when op is in a
+// non-isolated region nested inside the isolated region
+func.func @cannot_move_out_of_isolated_region(% arg0
+                                              : index, % cond
+                                              : i1)
+    ->
+index{
+  % 0 =
+      "before"() : ()->(index) %
+      1 = "test.isolated_one_region_op"(% arg0, % cond)({
+            ^bb0(% inner_arg
+                 : index, % inner_cond
+                 : i1)
+                : // scf.if is NOT isolated, but it's inside an isolated region
+                % 2 = scf.if % inner_cond->index {
+              % 3 = arith.addi % inner_arg,
+                % inner_arg{to_move} : index scf.yield % 3 : index
+            }
+            else {
+              scf.yield % inner_arg : index
+            }
+            "test.region_yield"(% 2) : (index)->()
+          }) : (index, i1)
+              ->(index) return %
+          1 : index
+}
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 = transform.structured.match ops{["before"]} in %
+            arg0 : (!transform.any_op)->!transform.any_op % op2 =
+                transform.structured.match attributes{to_move} in %
+                arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                    transform.get_result %
+                    op2[0]
+        : (!transform.any_op)
+              ->!transform
+              .any_value
+                  // expected-remark at +1{{cannot move value definition across
+                  // isolated-from-above region}}
+                      transform.test.move_value_defns %
+                    v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
   }
 }
 
 // -----
 
-// moveOperationDependencies: cannot move when slice has side-effecting ops
-func.func @move_op_deps_side_effecting(%arg0 : memref<index>) -> index {
-  %0 = "before"() : () -> (index)
-  %1 = memref.load %arg0[] : memref<index>
-  %2 = arith.muli %1, %1 {moved_op} : index
-  %3 = "foo"(%2) : (index) -> (index)
-  return %3 : index
-}
-
-module attributes {transform.with_named_sequence} {
-  transform.named_sequence @__transform_main(%arg0 : !transform.any_op {transform.readonly}) {
-    %op1 = transform.structured.match ops{["foo"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    %op2 = transform.structured.match ops{["before"]} in %arg0
-        : (!transform.any_op) -> !transform.any_op
-    // expected-remark at +1{{cannot move operation with side-effecting dependencies}}
-    transform.test.move_operand_deps %op1 before %op2
-        : !transform.any_op, !transform.any_op
-    transform.yield
+// Fail when trying to move an operation whose region captures a block argument
+// that wouldn't dominate at the insertion point.
+func.func @captured_block_arg_does_not_dominate(% arg0
+                                                : f32, % cond
+                                                : i1)
+    ->f32 {
+  % 0 = arith.addf % arg0, % arg0{before} : f32 cf.br ^
+                               bb1(% 0
+                                   : f32) ^
+                               bb1(% bbArg
+                                   : f32)
+      : // scf.if will be part of the slice that needs to move.
+        // It has a region that captures %bbArg from bb1.
+        // Moving it before the "before" op in bb0 would be invalid because
+        // %bbArg (a block argument of bb1) doesn't dominate bb0.
+        % 1 = scf.if % cond->f32 {
+    % inner = arith.addf % bbArg, % bbArg : f32 scf.yield % inner : f32
+  }
+  else {scf.yield % bbArg : f32} % 2 = arith.mulf % 1,
+                                   % 1 {target} : f32 return % 2 : f32
+}
+
+module attributes{transform.with_named_sequence} {
+  transform.named_sequence
+      @__transform_main(% arg0
+                        : !transform.any_op{transform.readonly}) {
+    % op1 =
+        transform.structured.match attributes{before} in %
+        arg0 : (!transform.any_op)->!transform.any_op % op2 =
+            transform.structured.match attributes{target} in %
+            arg0 : (!transform.any_op)->!transform.any_op % v1 =
+                transform.get_result %
+                op2[0] : (!transform.any_op)
+                             ->!transform
+                             .any_value
+                                 // expected-remark at +1{{moving op would break
+                                 // dominance for block argument operand}}
+                                     transform.test.move_value_defns %
+                v1 before % op1 : (!transform.any_value),
+      !transform.any_op transform.yield
   }
 }



More information about the Mlir-commits mailing list