-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[mlir][dataflow] disallow outside use of propagateIfChanged for DataFlowSolver #120885
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
[mlir][dataflow] disallow outside use of propagateIfChanged for DataFlowSolver #120885
Conversation
|
@llvm/pr-subscribers-mlir Author: Hongren Zheng (ZenithalHourlyRate) ChangesDetailed writeup is in google/heir#1153. See also #120881. In short, To solve such misuse, For downstream users affected by this, to correctly propagate the change, the Analysis should be re-run (check #120881) instead of just a The change to Cc @Mogball for review Full diff: https://github.com/llvm/llvm-project/pull/120885.diff 2 Files Affected:
diff --git a/mlir/include/mlir/Analysis/DataFlowFramework.h b/mlir/include/mlir/Analysis/DataFlowFramework.h
index 969664dc7a4fe3..1dcb32f762c003 100644
--- a/mlir/include/mlir/Analysis/DataFlowFramework.h
+++ b/mlir/include/mlir/Analysis/DataFlowFramework.h
@@ -394,13 +394,17 @@ class DataFlowSolver {
template <typename StateT, typename AnchorT>
StateT *getOrCreateState(AnchorT anchor);
+ /// Get the configuration of the solver.
+ const DataFlowConfig &getConfig() const { return config; }
+
+protected:
/// Propagate an update to an analysis state if it changed by pushing
/// dependent work items to the back of the queue.
+ /// This should only be used by DataFlowAnalysis instances.
+ /// When used outside of DataFlowAnalysis, the solver won't process
+ /// the work items
void propagateIfChanged(AnalysisState *state, ChangeResult changed);
- /// Get the configuration of the solver.
- const DataFlowConfig &getConfig() const { return config; }
-
private:
/// Configuration of the dataflow solver.
DataFlowConfig config;
diff --git a/mlir/lib/Analysis/DataFlow/IntegerRangeAnalysis.cpp b/mlir/lib/Analysis/DataFlow/IntegerRangeAnalysis.cpp
index 9e9411e5ede12c..60ae7d00c0bbdb 100644
--- a/mlir/lib/Analysis/DataFlow/IntegerRangeAnalysis.cpp
+++ b/mlir/lib/Analysis/DataFlow/IntegerRangeAnalysis.cpp
@@ -45,9 +45,13 @@ void IntegerValueRangeLattice::onUpdate(DataFlowSolver *solver) const {
std::optional<APInt> constant = getValue().getValue().getConstantValue();
auto value = cast<Value>(anchor);
auto *cv = solver->getOrCreateState<Lattice<ConstantValue>>(value);
- if (!constant)
- return solver->propagateIfChanged(
- cv, cv->join(ConstantValue::getUnknownConstant()));
+ if (!constant) {
+ auto changed = cv->join(ConstantValue::getUnknownConstant());
+ if (changed == ChangeResult::Change) {
+ cv->onUpdate(solver);
+ }
+ return;
+ }
Dialect *dialect;
if (auto *parent = value.getDefiningOp())
@@ -56,8 +60,11 @@ void IntegerValueRangeLattice::onUpdate(DataFlowSolver *solver) const {
dialect = value.getParentBlock()->getParentOp()->getDialect();
Type type = getElementTypeOrSelf(value);
- solver->propagateIfChanged(
- cv, cv->join(ConstantValue(IntegerAttr::get(type, *constant), dialect)));
+ auto changed =
+ cv->join(ConstantValue(IntegerAttr::get(type, *constant), dialect));
+ if (changed == ChangeResult::Change) {
+ cv->onUpdate(solver);
+ }
}
LogicalResult IntegerRangeAnalysis::visitOperation(
|
Mogball
left a comment
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.
Thanks for the PR and I agree that this being a public method is a bit of a footgun. However, directly invoking this method inside other dataflow variables is the intended usage of the method. Do you have a better idea on how an API can be exposed that way? propagateIfChanged is supposed to be a shorthand so that users don't forget to check the result of join, for example. Maybe it should be marked as nodiscard? (Unless it already is...). Alternatively, placing an assert inside propagateIfChanged checking that the solver is running might do the trick.
| if (!constant) | ||
| return solver->propagateIfChanged( | ||
| cv, cv->join(ConstantValue::getUnknownConstant())); | ||
| if (!constant) { |
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 actually the intended usage of the propagateIfChanged method. Data flow variables that depend on each other can issue updates to each other through this method.
66f5e03 to
2dc277b
Compare
Added a
I was thinking that adding class DataFlowSolver {
friend class AnalysisState;
};
class AnalysisState {
// existing API
virtual void onUpdate(DataFlowSolver *solver) const;
// NOTE: it is not virtual
void propagateIfChanged(DataFlowSolver *solver, AnalysisState *state, ChangeResult changed) {
solver->propagateIfChanged(state, changed);
}
};
|
|
I wonder if there is further feedback on this PR. |
Mogball
left a comment
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.
Thanks for the ping. I think this looks alright. Just one nit
2dc277b to
b51abb9
Compare
|
Comments addressed. |
Detailed writeup is in google/heir#1153. See also #120881. In short,
propagateIfChangedis used outside of theDataFlowAnalysisscope, because it is public, but it does not propagate as expected as theDataFlowSolverhas stopped running.To solve such misuse,
propagateIfChangedshould be made protected/private.For downstream users affected by this, to correctly propagate the change, the Analysis should be re-run (check #120881) instead of just a
propagateIfChangedThe change to
IntegerRangeAnalysisis just a expansion of thesolver->propagateIfChanged. TheLatticehas already been updated by thejoin. Propagation is done byonUpdate.Cc @Mogball for review