Skip to content

[mlir][async]: Make async.execute operation with RecursiveMemoryEffects trait #116544

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

AviadCo
Copy link
Contributor

@AviadCo AviadCo commented Nov 17, 2024

No description provided.

@llvmbot
Copy link
Member

llvmbot commented Nov 17, 2024

@llvm/pr-subscribers-mlir

Author: Aviad Cohen (AviadCo)

Changes

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

2 Files Affected:

  • (modified) mlir/include/mlir/Dialect/Async/IR/AsyncOps.td (+2-1)
  • (added) mlir/test/Dialect/Async/canonicalize.mlir (+10)
diff --git a/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td b/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
index 33b67921752346..011bffd02a90c5 100644
--- a/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
+++ b/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
@@ -38,7 +38,8 @@ def Async_ExecuteOp :
                                                  ["getEntrySuccessorOperands",
                                                   "areTypesCompatible"]>,
                        AttrSizedOperandSegments,
-                       AutomaticAllocationScope]> {
+                       AutomaticAllocationScope,
+                       Pure]> {
   let summary = "Asynchronous execute operation";
   let description = [{
     The `body` region attached to the `async.execute` operation semantically
diff --git a/mlir/test/Dialect/Async/canonicalize.mlir b/mlir/test/Dialect/Async/canonicalize.mlir
new file mode 100644
index 00000000000000..1a74eaa344e169
--- /dev/null
+++ b/mlir/test/Dialect/Async/canonicalize.mlir
@@ -0,0 +1,10 @@
+// RUN: mlir-opt %s -split-input-file -canonicalize | FileCheck %s
+
+// CHECK-NOT: async.execute
+
+func.func @empty_execute() {
+  %token = async.execute {
+    async.yield
+  }
+  return
+}

@AviadCo AviadCo requested a review from sanjoy November 17, 2024 13:43
@llvmbot
Copy link
Member

llvmbot commented Nov 17, 2024

@llvm/pr-subscribers-mlir-async

Author: Aviad Cohen (AviadCo)

Changes

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

2 Files Affected:

  • (modified) mlir/include/mlir/Dialect/Async/IR/AsyncOps.td (+2-1)
  • (added) mlir/test/Dialect/Async/canonicalize.mlir (+10)
diff --git a/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td b/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
index 33b67921752346..011bffd02a90c5 100644
--- a/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
+++ b/mlir/include/mlir/Dialect/Async/IR/AsyncOps.td
@@ -38,7 +38,8 @@ def Async_ExecuteOp :
                                                  ["getEntrySuccessorOperands",
                                                   "areTypesCompatible"]>,
                        AttrSizedOperandSegments,
-                       AutomaticAllocationScope]> {
+                       AutomaticAllocationScope,
+                       Pure]> {
   let summary = "Asynchronous execute operation";
   let description = [{
     The `body` region attached to the `async.execute` operation semantically
diff --git a/mlir/test/Dialect/Async/canonicalize.mlir b/mlir/test/Dialect/Async/canonicalize.mlir
new file mode 100644
index 00000000000000..1a74eaa344e169
--- /dev/null
+++ b/mlir/test/Dialect/Async/canonicalize.mlir
@@ -0,0 +1,10 @@
+// RUN: mlir-opt %s -split-input-file -canonicalize | FileCheck %s
+
+// CHECK-NOT: async.execute
+
+func.func @empty_execute() {
+  %token = async.execute {
+    async.yield
+  }
+  return
+}

@joker-eph
Copy link
Collaborator

Can you elaborate on the motivation and the correctness in the description of this PR please?
This is surprising to me and I don't quite understand why this op can be Pure at the moment. Thanks.

@AviadCo
Copy link
Contributor Author

AviadCo commented Nov 18, 2024

Can you elaborate on the motivation and the correctness in the description of this PR please? This is surprising to me and I don't quite understand why this op can be Pure at the moment. Thanks.

Sure, the case I have is scf.if with result which means that I must have an else region. For example:

     %res = scf.if cond -> (!async.token) {
      ...
    } else {
      %token = async.execute {
        async.yield
      }
      scf.yield %token : !async.token
    }

In some phase, I reach the point where the async is being inside if that has no longer have result, for example:

    scf.if cond {
      ...
      %token = async.execute {
          .....
      }
      // Token is being used
    } else {
      async.execute {
        async.yield
      }
    }

In this case, I would expect that the whole else will be eliminated (folder) as the execute has no memory effect inside it and has no users. With Pure this will be optimized to:

    scf.if cond {
        %token = async.execute {
            .....
        }
        // Token is being used
    }

Keeping my specific flow apart, it seems legic to remove the execute region if it has no usages and have no memory effects like scf.for does.

@AviadCo AviadCo changed the title [mlir][async]: Make async.execute operation pure [mlir][async]: Make async.execute operation with RecursiveMemoryEffects trait Nov 18, 2024
@AviadCo
Copy link
Contributor Author

AviadCo commented Nov 18, 2024

Can you elaborate on the motivation and the correctness in the description of this PR please? This is surprising to me and I don't quite understand why this op can be Pure at the moment. Thanks.

Sure, the case I have is scf.if with result which means that I must have an else region. For example:

     %res = scf.if cond -> (!async.token) {
      ...
    } else {
      %token = async.execute {
        async.yield
      }
      scf.yield %token : !async.token
    }

In some phase, I reach the point where the async is being inside if that has no longer have result, for example:

    scf.if cond {
      ...
      %token = async.execute {
          .....
      }
      // Token is being used
    } else {
      async.execute {
        async.yield
      }
    }

In this case, I would expect that the whole else will be eliminated (folder) as the execute has no memory effect inside it and has no users. With Pure this will be optimized to:

    scf.if cond {
        %token = async.execute {
            .....
        }
        // Token is being used
    }

Keeping my specific flow apart, it seems legic to remove the execute region if it has no usages and have no memory effects like scf.for does.

@joker-eph I confused between Pure and RecursiveMemoryEffects - please see latest change

@AviadCo AviadCo closed this Nov 18, 2024
@AviadCo AviadCo reopened this Nov 18, 2024
@AviadCo AviadCo requested a review from joker-eph November 19, 2024 05:38
@joker-eph
Copy link
Collaborator

RecursiveMemoryEffects is more appropriate, thanks.

I have a hard time convincing me about correctness right now, but I can't imagine what can go wrong either: "submitting a task" isn't really side-effect free, but here it is more specifically "submitting a side-effect free task". So I would tend to agree with this patch.

It'd be nice if @sanjoy or @River707 would give a second opinion :)

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.

3 participants