Skip to content

[mlir][transform] Update transform.loop.peel #67482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 27, 2023

Conversation

banach-space
Copy link
Contributor

@banach-space banach-space commented Sep 26, 2023

This patch updates transform.loop.peel so that this Op returns two
rather than one handle:

  • one for the peeled loop, and
  • one for the remainder loop.

Also, following this change this Op will fail if peeling fails. This is
consistent with other similar Ops that also fail if no transformation
takes place.

This patch updates `transform.loop.peel` so that this Op returns two
rather than one handle:
  * one for the peeled loop, and
  * one for the remainder loop.
Also, following this change this Op will fail if peeling fails. This is
consistent with other similar Ops that also fail if no transformation
takes place.
@llvmbot
Copy link
Member

llvmbot commented Sep 26, 2023

@llvm/pr-subscribers-mlir-scf
@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-linalg

Changes

This patch updates transform.loop.peel so that this Op returns two
rather than one handle:

  • one for the peeled loop, and
  • one for the remainder loop.

Also, following this change this Op will fail if peeling fails. This is
consistent with other similar Ops that also fail if no transformation
takes place.


Full diff: https://github.com/llvm/llvm-project/pull/67482.diff

5 Files Affected:

  • (modified) mlir/include/mlir/Dialect/SCF/TransformOps/SCFTransformOps.td (+9-10)
  • (modified) mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp (+8-6)
  • (modified) mlir/test/Dialect/Linalg/transform-op-fuse.mlir (+1-1)
  • (modified) mlir/test/Dialect/SCF/transform-ops-invalid.mlir (+20)
  • (modified) mlir/test/Dialect/SCF/transform-ops.mlir (+1-1)
diff --git a/mlir/include/mlir/Dialect/SCF/TransformOps/SCFTransformOps.td b/mlir/include/mlir/Dialect/SCF/TransformOps/SCFTransformOps.td
index 207a004c54ef5af..3adf5f0dc97dd5a 100644
--- a/mlir/include/mlir/Dialect/SCF/TransformOps/SCFTransformOps.td
+++ b/mlir/include/mlir/Dialect/SCF/TransformOps/SCFTransformOps.td
@@ -142,23 +142,22 @@ def LoopPeelOp : Op<Transform_Dialect, "loop.peel",
 
      This operation ignores non-scf::ForOp ops and drops them in the return.
 
-     This operation always succeeds and returns the scf::ForOp with the
-     postcondition: "the loop trip count is divisible by the step".
-     This operation may return the same unmodified loop handle when peeling did
-     not modify the IR (i.e. the loop trip count was already divisible).
+     This operation returns two scf::ForOp, with the first Op satisfying the
+     postcondition: "the loop trip count is divisible by the step". The second
+     loop Op contains the remaining iteration. Note that even though the
+     Payload IR modification may be performed in-place, this operation consumes
+     the operand handle and produces a new one.
 
-     Note that even though the Payload IR modification may be performed
-     in-place, this operation consumes the operand handle and produces a new
-     one.
+     #### Return Modes
 
-     TODO: Return both the peeled loop and the remainder loop.
+     Produces a definite failure if peeling fails.
   }];
 
   let arguments =
       (ins Transform_ScfForOp:$target,
            DefaultValuedAttr<BoolAttr, "false">:$fail_if_already_divisible);
-  // TODO: Return both the peeled loop and the remainder loop.
-  let results = (outs TransformHandleTypeInterface:$transformed);
+  let results = (outs TransformHandleTypeInterface:$peeled_loop,
+                      TransformHandleTypeInterface:$remainder_loop);
 
   let assemblyFormat =
     "$target attr-dict `:` functional-type(operands, results)";
diff --git a/mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp b/mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp
index d7e8c38478ced1a..65d503d7c4ad8b8 100644
--- a/mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp
+++ b/mlir/lib/Dialect/SCF/TransformOps/SCFTransformOps.cpp
@@ -226,14 +226,16 @@ transform::LoopPeelOp::applyToOne(transform::TransformRewriter &rewriter,
                                   transform::ApplyToEachResultList &results,
                                   transform::TransformState &state) {
   scf::ForOp result;
-  // This helper returns failure when peeling does not occur (i.e. when the IR
-  // is not modified). This is not a failure for the op as the postcondition:
-  //    "the loop trip count is divisible by the step"
-  // is valid.
   LogicalResult status =
       scf::peelForLoopAndSimplifyBounds(rewriter, target, result);
-  // TODO: Return both the peeled loop and the remainder loop.
-  results.push_back(failed(status) ? target : result);
+  if (failed(status)) {
+    DiagnosedSilenceableFailure diag = emitSilenceableError()
+                                       << "failed to peel";
+    return diag;
+  }
+  results.push_back(target);
+  results.push_back(result);
+
   return DiagnosedSilenceableFailure::success();
 }
 
diff --git a/mlir/test/Dialect/Linalg/transform-op-fuse.mlir b/mlir/test/Dialect/Linalg/transform-op-fuse.mlir
index d3772e9c04eef52..72c61661b55efd3 100644
--- a/mlir/test/Dialect/Linalg/transform-op-fuse.mlir
+++ b/mlir/test/Dialect/Linalg/transform-op-fuse.mlir
@@ -48,7 +48,7 @@ transform.sequence failures(propagate) {
   %0 = transform.structured.match ops{["linalg.elemwise_binary"]} in %arg1 : (!transform.any_op) -> !transform.any_op
   %1, %loops:2 = transform.structured.fuse %0 {tile_sizes = [32, 32], tile_interchange = [0, 1]}
     : (!transform.any_op) -> (!transform.any_op, !transform.op<"scf.for">, !transform.any_op)
-  transform.loop.peel %loops#0 : (!transform.op<"scf.for">) -> !transform.any_op
+  transform.loop.peel %loops#0 : (!transform.op<"scf.for">) -> (!transform.any_op, !transform.any_op)
 }
 
 // -----
diff --git a/mlir/test/Dialect/SCF/transform-ops-invalid.mlir b/mlir/test/Dialect/SCF/transform-ops-invalid.mlir
index b69ea0f5975cd80..e8212f500e693ad 100644
--- a/mlir/test/Dialect/SCF/transform-ops-invalid.mlir
+++ b/mlir/test/Dialect/SCF/transform-ops-invalid.mlir
@@ -59,3 +59,23 @@ transform.sequence failures(propagate) {
   // expected-error @below {{failed to outline}}
   transform.loop.outline %0 {func_name = "foo"} : (!transform.any_op) -> (!transform.any_op, !transform.any_op)
 }
+
+// -----
+
+func.func @test_loops_do_not_get_peeled() {
+  %lb = arith.constant 0 : index
+  %ub = arith.constant 40 : index
+  %step = arith.constant 5 : index
+  scf.for %i = %lb to %ub step %step {
+    arith.addi %i, %i : index
+  }
+  return
+}
+
+transform.sequence failures(propagate) {
+^bb1(%arg1: !transform.any_op):
+  %0 = transform.structured.match ops{["arith.addi"]} in %arg1 : (!transform.any_op) -> !transform.any_op
+  %1 = transform.loop.get_parent_for %0 : (!transform.any_op) -> !transform.op<"scf.for">
+  // expected-error @below {{failed to peel}}
+  transform.loop.peel %1 : (!transform.op<"scf.for">) -> (!transform.any_op, !transform.any_op)
+}
diff --git a/mlir/test/Dialect/SCF/transform-ops.mlir b/mlir/test/Dialect/SCF/transform-ops.mlir
index 7718ebcd1fc6ae0..779e750fb500326 100644
--- a/mlir/test/Dialect/SCF/transform-ops.mlir
+++ b/mlir/test/Dialect/SCF/transform-ops.mlir
@@ -107,7 +107,7 @@ transform.sequence failures(propagate) {
 ^bb1(%arg1: !transform.any_op):
   %0 = transform.structured.match ops{["arith.addi"]} in %arg1 : (!transform.any_op) -> !transform.any_op
   %1 = transform.loop.get_parent_for %0 : (!transform.any_op) -> !transform.op<"scf.for">
-  transform.loop.peel %1 : (!transform.op<"scf.for">) -> !transform.any_op
+  transform.loop.peel %1 : (!transform.op<"scf.for">) -> (!transform.any_op, !transform.any_op)
 }
 
 // -----

Copy link
Member

@ftynse ftynse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we double-check that if one iteration is peeled off, the loop is still present in the IR, and the handle expectedly points to that loop?

Also FYI, I quickly looked at the internals of the peeling implementation and it mixes Rewriter-based and direct IR mutation, which may lead to crashes that are fun to debug.

Add extra verification as suggestedy by Alex
@banach-space
Copy link
Contributor Author

Could we double-check that if one iteration is peeled off, the loop is still present in the IR, and the handle expectedly points to that loop?

Sure, added extra verification in a follow-up fix-up commit.

Also FYI, I quickly looked at the internals of the peeling implementation and it mixes Rewriter-based and direct IR mutation, which may lead to crashes that are fun to debug.

Thanks for the heads-up! ATM I am using this to play around with vectorisation of the remainder loop and it works fine ™️ 🤞🏻 .

@banach-space banach-space merged commit 1d5ccce into llvm:main Sep 27, 2023
banach-space added a commit that referenced this pull request Sep 27, 2023
Fixes an accidental omission in #67482
@aartbik
Copy link
Contributor

aartbik commented Sep 27, 2023

This broke a bot:

Failed Tests (1):
MLIR :: python/dialects/transform_loop_ext.py

@banach-space
Copy link
Contributor Author

This broke a bot:

Failed Tests (1): MLIR :: python/dialects/transform_loop_ext.py

Sorry about that, patch reverted. I forgot about the Python bindings :(

banach-space added a commit that referenced this pull request Sep 28, 2023
This patch updates `transform.loop.peel` so that this Op returns two
rather than one handle:
  * one for the peeled loop, and
  * one for the remainder loop.
Also, following this change this Op will fail if peeling fails. This is
consistent with other similar Ops that also fail if no transformation
takes place.

Relands #67482 with an extra fix for transform_loop_ext.py
legrosbuffle pushed a commit to legrosbuffle/llvm-project that referenced this pull request Sep 29, 2023
This patch updates `transform.loop.peel` so that this Op returns two
rather than one handle:
  * one for the peeled loop, and
  * one for the remainder loop.

Also, following this change this Op will fail if peeling fails. This is
consistent with other similar Ops that also fail if no transformation
takes place.
legrosbuffle pushed a commit to legrosbuffle/llvm-project that referenced this pull request Sep 29, 2023
legrosbuffle pushed a commit to legrosbuffle/llvm-project that referenced this pull request Sep 29, 2023
legrosbuffle pushed a commit to legrosbuffle/llvm-project that referenced this pull request Sep 29, 2023
This patch updates `transform.loop.peel` so that this Op returns two
rather than one handle:
  * one for the peeled loop, and
  * one for the remainder loop.
Also, following this change this Op will fail if peeling fails. This is
consistent with other similar Ops that also fail if no transformation
takes place.

Relands llvm#67482 with an extra fix for transform_loop_ext.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants