Skip to content

Make FunctionOpInterface check ReturnLike #112615

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 3 commits into
base: main
Choose a base branch
from

Conversation

tzunghanjuang
Copy link
Contributor

Description:

This PR is a follow-up for #110322. The previous PR makes OneShotBufferize check if a function operation has a terminator with the ReturnLike trait. However, transform.named_sequence does not expect yieldOp to have the trait. In that PR, the issue is circumvented by --transform-interpreter="debug-payload-root-tag=payload" .

This PR tries to tie ReturnLike to FunctionOpInface. However, I am not sure if it is correct to check ReturnLike in the tablegen for FunctionOpInface. I have not yet handled all the errors. Just want to create this PR for further confirmation.
We might also need to wait for this fix for transform.named_sequence (#111408) to be merged?

@llvmbot llvmbot added mlir mlir:bufferization Bufferization infrastructure labels Oct 16, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 16, 2024

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-bufferization

Author: Tzung-Han Juang (tzunghanjuang)

Changes

Description:

This PR is a follow-up for #110322. The previous PR makes OneShotBufferize check if a function operation has a terminator with the ReturnLike trait. However, transform.named_sequence does not expect yieldOp to have the trait. In that PR, the issue is circumvented by --transform-interpreter="debug-payload-root-tag=payload" .

This PR tries to tie ReturnLike to FunctionOpInface. However, I am not sure if it is correct to check ReturnLike in the tablegen for FunctionOpInface. I have not yet handled all the errors. Just want to create this PR for further confirmation.
We might also need to wait for this fix for transform.named_sequence (#111408) to be merged?


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

3 Files Affected:

  • (modified) mlir/include/mlir/Interfaces/FunctionInterfaces.h (+1)
  • (modified) mlir/include/mlir/Interfaces/FunctionInterfaces.td (+23)
  • (modified) mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp (+2-2)
diff --git a/mlir/include/mlir/Interfaces/FunctionInterfaces.h b/mlir/include/mlir/Interfaces/FunctionInterfaces.h
index e10e9bd342702a..f121a6823711a0 100644
--- a/mlir/include/mlir/Interfaces/FunctionInterfaces.h
+++ b/mlir/include/mlir/Interfaces/FunctionInterfaces.h
@@ -20,6 +20,7 @@
 #include "mlir/IR/SymbolTable.h"
 #include "mlir/IR/TypeUtilities.h"
 #include "mlir/Interfaces/CallInterfaces.h"
+#include "mlir/Interfaces/ControlFlowInterfaces.h"
 #include "llvm/ADT/BitVector.h"
 #include "llvm/ADT/SmallString.h"
 
diff --git a/mlir/include/mlir/Interfaces/FunctionInterfaces.td b/mlir/include/mlir/Interfaces/FunctionInterfaces.td
index 697f951748c675..244c7b33f0119b 100644
--- a/mlir/include/mlir/Interfaces/FunctionInterfaces.td
+++ b/mlir/include/mlir/Interfaces/FunctionInterfaces.td
@@ -108,6 +108,29 @@ def FunctionOpInterface : OpInterface<"FunctionOpInterface", [
         }
       }
 
+      // FunctionOpInterface is tied to a ReturnLike.
+      Operation *terminator = entryBlock.getTerminator();
+      if (terminator->hasTrait<OpTrait::ReturnLike>()) {
+        return $_op.emitOpError("The body of a FunctionOpInterface must")
+              << "have a ReturnLike terminator.";
+      }
+
+      // Match ReturnLike's operand types and FunctionOpInterface's 
+      // result types.
+      auto returnOperandTypes = terminator->getOperandTypes();
+      auto funcResultTypes =  $_op->getResultTypes();
+      if (funcResultTypes.size() != returnOperandTypes.size()) {
+        return $_op.emitOpError("The number of a FunctionOpInterface's")
+              << "result must match that of the ReturnLike operands.";
+      }
+
+      for (unsigned i = 0; i < funcResultTypes.size(); ++i) {
+        if (funcResultTypes[i] != returnOperandTypes[i]) {
+          return $_op.emitOpError("The result types of a FunctionOpInterface")
+              << "must match the operand types of the ReturnLike.";
+        }
+      }
+
       return success();
     }]>,
     InterfaceMethod<[{
diff --git a/mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp b/mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp
index a0e5c7fff7690f..bb1cf2fc3fd209 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/OneShotModuleBufferize.cpp
@@ -302,8 +302,8 @@ getFuncOpsOrderedByCalls(ModuleOp moduleOp,
       Operation *returnOp = getAssumedUniqueReturnOp(funcOp);
       if (!returnOp)
         return funcOp->emitError()
-               << "cannot bufferize a FuncOp with tensors and "
-                  "without a unique ReturnOp";
+               << "cannot bufferize a FunctionOpInterface with tensors and "
+                  "without a unique ReturnLike";
     }
 
     // Collect function calls and populate the caller map.

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.

I don't see a sufficiently strong rationale to change the requirements of the built-in interface or trait. Return-like is used in dataflow sense outside functions. There are function-like operations that don't use return-like terminators (consider co-routines). If bufferization needs some additional guarantees/information from terminators to work, it is better to define a new, bufferization-specific interface and mark complying operations as implementing this interface.

Comment on lines +111 to +134
// FunctionOpInterface is tied to a ReturnLike.
Operation *terminator = entryBlock.getTerminator();
if (!terminator->hasTrait<OpTrait::ReturnLike>()) {
return $_op.emitOpError("The body of a FunctionOpInterface must have ")
<< "a ReturnLike terminator, but the current terminator does not "
<< "have this trait.";
}

// Match ReturnLike's operand types and FunctionOpInterface's
// result types.
auto returnOperandTypes = terminator->getOperandTypes();
auto funcResultTypes = $_op->getResultTypes();
if (funcResultTypes.size() != returnOperandTypes.size()) {
return $_op.emitOpError("The number of a FunctionOpInterface's")
<< "results must match that of the ReturnLike operands.";
}

for (unsigned i = 0; i < funcResultTypes.size(); ++i) {
if (funcResultTypes[i] != returnOperandTypes[i]) {
return $_op.emitOpError("The result types of a FunctionOpInterface")
<< "must match the operand types of the ReturnLike.";
}
}

Copy link
Member

Choose a reason for hiding this comment

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

This is way too long to be inline code in a .td file. Please move this into a helper function in a .cpp file and call it from here. Having code as code rather than inlined strings makes it analyzable and processable by tooling.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you very much. It makes sense. I'll make the changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mlir:bufferization Bufferization infrastructure mlir
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants