-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mlir][acc] Add LegalizeDataValues support for DeclareEnterOp #138008
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
Conversation
@llvm/pr-subscribers-openacc @llvm/pr-subscribers-mlir Author: Susan Tan (ス-ザン タン) (SusanTan) ChangesThe patch extends the existing LegalizeDataValues to support DeclareEnter and DeclareExit pair. Full diff: https://github.com/llvm/llvm-project/pull/138008.diff 2 Files Affected:
diff --git a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
index ff5845343313c..9c141550b184e 100644
--- a/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
+++ b/mlir/include/mlir/Dialect/OpenACC/OpenACC.h
@@ -58,7 +58,8 @@
#define ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS \
ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::LoopOp
#define ACC_DATA_CONSTRUCT_STRUCTURED_OPS \
- mlir::acc::DataOp, mlir::acc::DeclareOp, mlir::acc::HostDataOp
+ mlir::acc::DataOp, mlir::acc::DeclareOp, mlir::acc::HostDataOp, \
+ mlir::acc::DeclareEnterOp
#define ACC_DATA_CONSTRUCT_UNSTRUCTURED_OPS \
mlir::acc::EnterDataOp, mlir::acc::ExitDataOp, mlir::acc::UpdateOp, \
mlir::acc::DeclareEnterOp, mlir::acc::DeclareExitOp
diff --git a/mlir/lib/Dialect/OpenACC/Transforms/LegalizeDataValues.cpp b/mlir/lib/Dialect/OpenACC/Transforms/LegalizeDataValues.cpp
index f2abeab744d17..24f0ed3d35a0e 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/LegalizeDataValues.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/LegalizeDataValues.cpp
@@ -10,6 +10,7 @@
#include "mlir/Dialect/Func/IR/FuncOps.h"
#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "mlir/IR/Dominance.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Transforms/RegionUtils.h"
#include "llvm/Support/ErrorHandling.h"
@@ -70,8 +71,38 @@ static void replaceAllUsesInAccComputeRegionsWith(Value orig, Value replacement,
}
}
+// Helper function to process declare enter/exit pairs
+static void processDeclareEnterExit(
+ acc::DeclareEnterOp op, llvm::SmallVector<std::pair<Value, Value>> &values,
+ DominanceInfo &domInfo, PostDominanceInfo &postDomInfo) {
+ // For declare enter/exit pairs, verify there is exactly one exit op using the
+ // token
+ if (!op.getToken().hasOneUse())
+ op.emitError("declare enter token must have exactly one use");
+ Operation *user = *op.getToken().getUsers().begin();
+ auto declareExit = dyn_cast<acc::DeclareExitOp>(user);
+ if (!declareExit)
+ op.emitError("declare enter token must be used by declare exit op");
+
+ for (auto p : values) {
+ Value hostVal = std::get<0>(p);
+ Value deviceVal = std::get<1>(p);
+ for (auto &use : llvm::make_early_inc_range(hostVal.getUses())) {
+ Operation *owner = use.getOwner();
+ if (!domInfo.dominates(op.getOperation(), owner) ||
+ !postDomInfo.postDominates(declareExit.getOperation(), owner))
+ continue;
+ if (insideAccComputeRegion(owner))
+ use.set(deviceVal);
+ }
+ }
+}
+
template <typename Op>
-static void collectAndReplaceInRegion(Op &op, bool hostToDevice) {
+static void
+collectAndReplaceInRegion(Op &op, bool hostToDevice,
+ DominanceInfo *domInfo = nullptr,
+ PostDominanceInfo *postDomInfo = nullptr) {
llvm::SmallVector<std::pair<Value, Value>> values;
if constexpr (std::is_same_v<Op, acc::LoopOp>) {
@@ -82,16 +113,24 @@ static void collectAndReplaceInRegion(Op &op, bool hostToDevice) {
if constexpr (!std::is_same_v<Op, acc::KernelsOp> &&
!std::is_same_v<Op, acc::DataOp> &&
!std::is_same_v<Op, acc::DeclareOp> &&
- !std::is_same_v<Op, acc::HostDataOp>) {
+ !std::is_same_v<Op, acc::HostDataOp> &&
+ !std::is_same_v<Op, acc::DeclareEnterOp>) {
collectVars(op.getReductionOperands(), values, hostToDevice);
collectVars(op.getPrivateOperands(), values, hostToDevice);
collectVars(op.getFirstprivateOperands(), values, hostToDevice);
}
}
- for (auto p : values)
- replaceAllUsesInAccComputeRegionsWith<Op>(std::get<0>(p), std::get<1>(p),
- op.getRegion());
+ if constexpr (std::is_same_v<Op, acc::DeclareEnterOp>) {
+ assert(domInfo && postDomInfo &&
+ "Dominance info required for DeclareEnterOp");
+ processDeclareEnterExit(op, values, *domInfo, *postDomInfo);
+ } else {
+ for (auto p : values) {
+ replaceAllUsesInAccComputeRegionsWith<Op>(std::get<0>(p), std::get<1>(p),
+ op.getRegion());
+ }
+ }
}
class LegalizeDataValuesInRegion
@@ -105,6 +144,10 @@ class LegalizeDataValuesInRegion
func::FuncOp funcOp = getOperation();
bool replaceHostVsDevice = this->hostToDevice.getValue();
+ // Get dominance info for the function
+ DominanceInfo domInfo(funcOp);
+ PostDominanceInfo postDomInfo(funcOp);
+
funcOp.walk([&](Operation *op) {
if (!isa<ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS>(*op) &&
!(isa<ACC_DATA_CONSTRUCT_STRUCTURED_OPS>(*op) &&
@@ -125,6 +168,9 @@ class LegalizeDataValuesInRegion
collectAndReplaceInRegion(declareOp, replaceHostVsDevice);
} else if (auto hostDataOp = dyn_cast<acc::HostDataOp>(*op)) {
collectAndReplaceInRegion(hostDataOp, replaceHostVsDevice);
+ } else if (auto declareEnterOp = dyn_cast<acc::DeclareEnterOp>(*op)) {
+ collectAndReplaceInRegion(declareEnterOp, replaceHostVsDevice, &domInfo,
+ &postDomInfo);
} else {
llvm_unreachable("unsupported acc region op");
}
|
@@ -58,7 +58,8 @@ | |||
#define ACC_COMPUTE_CONSTRUCT_AND_LOOP_OPS \ | |||
ACC_COMPUTE_CONSTRUCT_OPS, mlir::acc::LoopOp | |||
#define ACC_DATA_CONSTRUCT_STRUCTURED_OPS \ | |||
mlir::acc::DataOp, mlir::acc::DeclareOp, mlir::acc::HostDataOp | |||
mlir::acc::DataOp, mlir::acc::DeclareOp, mlir::acc::HostDataOp, \ | |||
mlir::acc::DeclareEnterOp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should not be added to this list because although it can result from a structured declare construct, the operation itself allows unstructured flow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, i realized that was a mistake, changed it in the next commit
@@ -105,6 +144,10 @@ class LegalizeDataValuesInRegion | |||
func::FuncOp funcOp = getOperation(); | |||
bool replaceHostVsDevice = this->hostToDevice.getValue(); | |||
|
|||
// Get dominance info for the function | |||
DominanceInfo domInfo(funcOp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's compute this lazily - only compute it first time when you encounter acc.declare_enter.
if constexpr (std::is_same_v<Op, acc::DeclareEnterOp>) { | ||
// For declare enter/exit pairs, verify there is exactly one exit op using | ||
// the token | ||
if (!op.getToken().hasOneUse()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is too restrictive since from a language perspective multiple exits are allowed - and a dialect may choose to permit that. I think the algorithm here should collect all of the declare_exit
's and ensure that the replaced use post-dominates one of them.
if (!declareExit) | ||
op.emitError("declare enter token must be used by declare exit op"); | ||
exitOp = declareExit; | ||
} else if constexpr (std::is_same_v<Op, acc::EnterDataOp>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am excited about the potential to support acc.enter_data. As I looked at your algorithm here though I realized that maybe it should be left for another change. This is because enter_data and exit_data don't come in pairs - so a bit more analysis is needed to get this right. More specifically, finding an exit_data does not constitute the end of the lifetime for a particular variable.
exitOps.push_back(declareExit); | ||
} | ||
if (exitOps.empty()) | ||
op.emitError( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not an error. For example in the acc.global_ctor, an acc.declare_enter is used without an exit operation. That said, I think it seems OK to avoid doing any replacement of values in such a case - just don't make it an error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job!
…38008) The patch extends the existing LegalizeDataValues to support DeclareEnter and DeclareExit pair. Since unlike other ops, DeclareEnter and DeclareExit don't have a region defined, we use dominance/post dominance information to ensure only the uses within the region dominated by DeclareEnter and post dominated by DeclareExit are updated with data on device.
…38008) The patch extends the existing LegalizeDataValues to support DeclareEnter and DeclareExit pair. Since unlike other ops, DeclareEnter and DeclareExit don't have a region defined, we use dominance/post dominance information to ensure only the uses within the region dominated by DeclareEnter and post dominated by DeclareExit are updated with data on device.
…38008) The patch extends the existing LegalizeDataValues to support DeclareEnter and DeclareExit pair. Since unlike other ops, DeclareEnter and DeclareExit don't have a region defined, we use dominance/post dominance information to ensure only the uses within the region dominated by DeclareEnter and post dominated by DeclareExit are updated with data on device.
…38008) The patch extends the existing LegalizeDataValues to support DeclareEnter and DeclareExit pair. Since unlike other ops, DeclareEnter and DeclareExit don't have a region defined, we use dominance/post dominance information to ensure only the uses within the region dominated by DeclareEnter and post dominated by DeclareExit are updated with data on device.
…38008) The patch extends the existing LegalizeDataValues to support DeclareEnter and DeclareExit pair. Since unlike other ops, DeclareEnter and DeclareExit don't have a region defined, we use dominance/post dominance information to ensure only the uses within the region dominated by DeclareEnter and post dominated by DeclareExit are updated with data on device.
The patch extends the existing LegalizeDataValues to support DeclareEnter and DeclareExit pair.
Since unlike other ops, DeclareEnter and DeclareExit don't have a region defined, we use dominance/post dominance information to ensure only the uses within the region dominated by DeclareEnter and post dominated by DeclareExit are updated with data on device.