[llvm] [Coroutines] Only rematerialize when operands are available after the suspend (PR #209195)
Weibo He via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 17 04:59:18 PDT 2026
================
@@ -28,6 +32,83 @@ using namespace coro;
// "coro-frame", which results in leaner debug spew.
#define DEBUG_TYPE "coro-suspend-crossing"
+// Returns true if \p Root can be rematerialized in the resume function without
+// introducing a new spill. This holds when a value is:
+// 1. A constant: genuinely available at no cost, materialized as an immediate
+// in the resume function.
+// 2. An argument: treated as available heuristically. An argument crossing the
+// suspend is actually spilled, but treating it as available preserves
+// beneficial shared-operand rematerialization.
+// 3. A value that already crosses a suspend point for an independent use, i.e.,
+// it is spilled regardless.
+// 4. A materializable value all of whose operands are themselves available.
+static bool isAvailableAfterSuspend(
+ Value *Root, const std::function<bool(Instruction &)> &Materializable,
+ const SuspendCrossingInfo &Checker, SmallDenseMap<Value *, bool> &Memo) {
+ SmallVector<Value *> Stack;
+ // Materializable nodes whose operands have been scheduled but not yet folded.
+ // Distinguishes a node being visited a second time (operands ready) from the
+ // first visit, and lets an operand reached through a cycle resolve to false.
+ SmallPtrSet<Value *, 16> Opened;
+ Stack.push_back(Root);
+ while (!Stack.empty()) {
+ Value *V = Stack.back();
+ if (Memo.contains(V)) {
+ Stack.pop_back();
+ continue;
+ }
+
+ // Leaves that resolve without inspecting operands. Non-instructions
+ // (constants, arguments) are available; a value that already crosses a
+ // suspend independently is spilled regardless, so referencing its slot is
+ // available at no new cost.
+ auto *I = dyn_cast<Instruction>(V);
+ if (!I || Checker.isDefinitionAcrossSuspend(*I)) {
+ Memo[V] = true;
+ Stack.pop_back();
+ continue;
+ }
+ if (!Materializable(*I)) {
+ Memo[V] = false;
+ Stack.pop_back();
+ continue;
+ }
+
+ // Materializable: available iff all operands are available. On the first
+ // visit, schedule the unresolved operands above V and revisit V once they
+ // are folded.
+ if (Opened.insert(V).second) {
+ for (Use &U : I->operands())
+ if (!Memo.contains(U.get()))
+ Stack.push_back(U.get());
+ continue;
+ }
+
+ // Second visit: operands are resolved, except any reached through a cycle
+ // (only possible via non-materializable PHIs, which never open). An
+ // unresolved operand is treated as not available, conservatively.
+ Stack.pop_back();
+ Memo[V] = all_of(I->operands(), [&](Use &U) {
+ auto It = Memo.find(U.get());
+ return It != Memo.end() && It->second;
+ });
----------------
NewSigma wrote:
This two-lookup approach seems a bit clunky. Could we leverage something like `InstVisitor` to streamline it?
https://github.com/llvm/llvm-project/pull/209195
More information about the llvm-commits
mailing list