-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[Coroutines] Split buildCoroutineFrame into normalization and frame building #108076
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
[Coroutines] Split buildCoroutineFrame into normalization and frame building #108076
Conversation
* Split buildCoroutineFrame into code related to normalization and code related to actually building the coroutine frame. * This will enable future specialization of buildCoroutineFrame for different ABIs.
|
@llvm/pr-subscribers-llvm-transforms Author: Tyler Nowicki (TylerNowicki) Changes
See RFC for more info: https://discourse.llvm.org/t/rfc-abi-objects-for-coroutines/81057 Full diff: https://github.com/llvm/llvm-project/pull/108076.diff 3 Files Affected:
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 4b76fc79361008..8ee4bfa3b888df 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -1754,7 +1754,8 @@ static bool willLeaveFunctionImmediatelyAfter(BasicBlock *BB,
if (depth == 0) return false;
// If this is a suspend block, we're about to exit the resumption function.
- if (isSuspendBlock(BB)) return true;
+ if (isSuspendBlock(BB))
+ return true;
// Recurse into the successors.
for (auto *Succ : successors(BB)) {
@@ -2288,9 +2289,8 @@ static void doRematerializations(
rewriteMaterializableInstructions(AllRemats);
}
-void coro::buildCoroutineFrame(
- Function &F, Shape &Shape, TargetTransformInfo &TTI,
- const std::function<bool(Instruction &)> &MaterializableCallback) {
+void coro::normalizeCoroutine(Function &F, coro::Shape &Shape,
+ TargetTransformInfo &TTI) {
// Don't eliminate swifterror in async functions that won't be split.
if (Shape.ABI != coro::ABI::Async || !Shape.CoroSuspends.empty())
eliminateSwiftError(F, Shape);
@@ -2337,10 +2337,12 @@ void coro::buildCoroutineFrame(
// Transforms multi-edge PHI Nodes, so that any value feeding into a PHI will
// never have its definition separated from the PHI by the suspend point.
rewritePHIs(F);
+}
- // Build suspend crossing info.
+void coro::buildCoroutineFrame(
+ Function &F, Shape &Shape,
+ const std::function<bool(Instruction &)> &MaterializableCallback) {
SuspendCrossingInfo Checker(F, Shape.CoroSuspends, Shape.CoroEnds);
-
doRematerializations(F, Checker, MaterializableCallback);
const DominatorTree DT(F);
diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h
index be86f96525b677..698c21a797420a 100644
--- a/llvm/lib/Transforms/Coroutines/CoroInternal.h
+++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h
@@ -281,8 +281,10 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
};
bool defaultMaterializable(Instruction &V);
+void normalizeCoroutine(Function &F, coro::Shape &Shape,
+ TargetTransformInfo &TTI);
void buildCoroutineFrame(
- Function &F, Shape &Shape, TargetTransformInfo &TTI,
+ Function &F, Shape &Shape,
const std::function<bool(Instruction &)> &MaterializableCallback);
CallInst *createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
TargetTransformInfo &TTI,
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index 494c4d632de95f..dc3829d7f28eb1 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -2030,7 +2030,8 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
lowerAwaitSuspends(F, Shape);
simplifySuspendPoints(Shape);
- buildCoroutineFrame(F, Shape, TTI, MaterializableCallback);
+ normalizeCoroutine(F, Shape, TTI);
+ buildCoroutineFrame(F, Shape, MaterializableCallback);
replaceFrameSizeAndAlignment(Shape);
bool isNoSuspendCoroutine = Shape.CoroSuspends.empty();
|
|
@llvm/pr-subscribers-coroutines Author: Tyler Nowicki (TylerNowicki) Changes
See RFC for more info: https://discourse.llvm.org/t/rfc-abi-objects-for-coroutines/81057 Full diff: https://github.com/llvm/llvm-project/pull/108076.diff 3 Files Affected:
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 4b76fc79361008..8ee4bfa3b888df 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -1754,7 +1754,8 @@ static bool willLeaveFunctionImmediatelyAfter(BasicBlock *BB,
if (depth == 0) return false;
// If this is a suspend block, we're about to exit the resumption function.
- if (isSuspendBlock(BB)) return true;
+ if (isSuspendBlock(BB))
+ return true;
// Recurse into the successors.
for (auto *Succ : successors(BB)) {
@@ -2288,9 +2289,8 @@ static void doRematerializations(
rewriteMaterializableInstructions(AllRemats);
}
-void coro::buildCoroutineFrame(
- Function &F, Shape &Shape, TargetTransformInfo &TTI,
- const std::function<bool(Instruction &)> &MaterializableCallback) {
+void coro::normalizeCoroutine(Function &F, coro::Shape &Shape,
+ TargetTransformInfo &TTI) {
// Don't eliminate swifterror in async functions that won't be split.
if (Shape.ABI != coro::ABI::Async || !Shape.CoroSuspends.empty())
eliminateSwiftError(F, Shape);
@@ -2337,10 +2337,12 @@ void coro::buildCoroutineFrame(
// Transforms multi-edge PHI Nodes, so that any value feeding into a PHI will
// never have its definition separated from the PHI by the suspend point.
rewritePHIs(F);
+}
- // Build suspend crossing info.
+void coro::buildCoroutineFrame(
+ Function &F, Shape &Shape,
+ const std::function<bool(Instruction &)> &MaterializableCallback) {
SuspendCrossingInfo Checker(F, Shape.CoroSuspends, Shape.CoroEnds);
-
doRematerializations(F, Checker, MaterializableCallback);
const DominatorTree DT(F);
diff --git a/llvm/lib/Transforms/Coroutines/CoroInternal.h b/llvm/lib/Transforms/Coroutines/CoroInternal.h
index be86f96525b677..698c21a797420a 100644
--- a/llvm/lib/Transforms/Coroutines/CoroInternal.h
+++ b/llvm/lib/Transforms/Coroutines/CoroInternal.h
@@ -281,8 +281,10 @@ struct LLVM_LIBRARY_VISIBILITY Shape {
};
bool defaultMaterializable(Instruction &V);
+void normalizeCoroutine(Function &F, coro::Shape &Shape,
+ TargetTransformInfo &TTI);
void buildCoroutineFrame(
- Function &F, Shape &Shape, TargetTransformInfo &TTI,
+ Function &F, Shape &Shape,
const std::function<bool(Instruction &)> &MaterializableCallback);
CallInst *createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
TargetTransformInfo &TTI,
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index 494c4d632de95f..dc3829d7f28eb1 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -2030,7 +2030,8 @@ splitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
lowerAwaitSuspends(F, Shape);
simplifySuspendPoints(Shape);
- buildCoroutineFrame(F, Shape, TTI, MaterializableCallback);
+ normalizeCoroutine(F, Shape, TTI);
+ buildCoroutineFrame(F, Shape, MaterializableCallback);
replaceFrameSizeAndAlignment(Shape);
bool isNoSuspendCoroutine = Shape.CoroSuspends.empty();
|
yuxuanchen1997
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.
Overall I think a neutral/positive change. One thing I am worried was that how we are going to distinguish a "normalized" vs a "unnormalized" coroutine? i.e. How do we find out that we missed a normalization here?
Normalization performs these important steps:
Each of these things can individually be tested For 1) and 2) I believe the purpose of the transform is in part for suspend crossing info's analysis so it can specifically 'mark' the suspend blocks and identify the end of the coroutine. There are some existing places within suspend crossing info that visit the CoroSuspends and CoroEnds so we could check A) and B) there. For 3) I believe the purpose of this transform is for insertSpills to work properly. Infact there is already a check for the result of this transform! I think to verify the result of normalization we just need to add checks A) and B) to suspend crossing info. |
I posted a PR to add a couple of asserts that check for normalization #108096. The PR includes this change #108076 (I posted it too quickly), I will rebase after this change merges. |
ChuanqiXu9
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.
LGTM
| if (isSuspendBlock(BB)) | ||
| return true; |
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.
nit: generally we don't unrelated changes.
|
I think the concept |
* Add asserts to verify normalization of the coroutine happened. * This will be important when normalization becomes part of an ABI object. --- From a previous discussion here #108076 Normalization performs these important steps: split around each suspend, this adds BBs before/after each suspend so each suspend now exists in its own block. split around coro.end (similar to above) break critical edges and add single-edge phis in the new blocks (also removing other single-edge phis). Each of these things can individually be tested A) Check that each suspend is the only inst in its BB B) Check that coro.end is the only inst in its BB C) Check that each edge of a multi-edge phis is preceded by single-edge phi in an immediate pred For 1) and 2) I believe the purpose of the transform is in part for suspend crossing info's analysis so it can specifically 'mark' the suspend blocks and identify the end of the coroutine. There are some existing places within suspend crossing info that visit the CoroSuspends and CoroEnds so we could check A) and B) there. For 3) I believe the purpose of this transform is for insertSpills to work properly. Infact there is already a check for the result of this transform! assert(PN->getNumIncomingValues() == 1 && "unexpected number of incoming " "values in the PHINode"); I think to verify the result of normalization we just need to add checks A) and B) to suspend crossing info.
See RFC for more info: https://discourse.llvm.org/t/rfc-abi-objects-for-coroutines/81057