[llvm] [DRAFT] coros: suspend metadata preservation (PR #150077)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 22 23:25:02 PDT 2025


https://github.com/yonillasky updated https://github.com/llvm/llvm-project/pull/150077

>From bf33e5b62e8ae304d25003dbb0d4c579cbfe3b36 Mon Sep 17 00:00:00 2001
From: Yoni Lavi <yoni.lavi at nextsilicon.com>
Date: Tue, 22 Jul 2025 21:11:28 +0300
Subject: [PATCH] coros: suspend metadata preservation

---
 llvm/lib/Transforms/Coroutines/CoroSplit.cpp  | 24 +++++++
 .../Transforms/Coroutines/coro-split-md.ll    | 69 +++++++++++++++++++
 2 files changed, 93 insertions(+)
 create mode 100644 llvm/test/Transforms/Coroutines/coro-split-md.ll

diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index 64b33e46404f0..6f0a43ca7cd80 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -79,6 +79,18 @@ using namespace llvm;
 
 #define DEBUG_TYPE "coro-split"
 
+/// If set, ensures that all metadata from CoroSuspendInst's is preserved in the
+/// containing function.
+static cl::opt<bool> CoroSplitPreservesSuspendMD(
+    "coro-split-preserves-suspend-md", cl::Hidden,
+    cl::desc(
+        "llvm.coro.suspend_md metadata from all suspend point instructions "
+        "will be preserved inside llvm.coro.suspend_md_table metadata on the "
+        "containing coroutine"));
+
+static StringRef CoroSuspendMDName = "llvm.coro.suspend_md";
+static StringRef CoroSuspendMDTableName = "llvm.coro.suspend_md_table";
+
 // FIXME:
 // Lower the intrinisc in CoroEarly phase if coroutine frame doesn't escape
 // and it is known that other transformations, for example, sanitizers
@@ -1503,10 +1515,18 @@ struct SwitchCoroutineSplitter {
     Shape.SwitchLowering.ResumeSwitch = Switch;
 
     // Split all coro.suspend calls
+    SmallVector<Metadata *> SuspendMdEntries;
     size_t SuspendIndex = 0;
     for (auto *AnyS : Shape.CoroSuspends) {
       auto *S = cast<CoroSuspendInst>(AnyS);
       ConstantInt *IndexVal = Shape.getIndex(SuspendIndex);
+      if (CoroSplitPreservesSuspendMD) {
+        MDNode *SuspendMD = S->getMetadata(CoroSuspendMDName);
+        if (SuspendMD) {
+          Metadata *KeyMD = ConstantAsMetadata::get(IndexVal);
+          SuspendMdEntries.push_back(MDNode::get(C, {KeyMD, SuspendMD}));
+        }
+      }
 
       // Replace CoroSave with a store to Index:
       //    %index.addr = getelementptr %f.frame... (index field number)
@@ -1582,6 +1602,10 @@ struct SwitchCoroutineSplitter {
       ++SuspendIndex;
     }
 
+    if (CoroSplitPreservesSuspendMD)
+      if (!SuspendMdEntries.empty())
+        F.setMetadata(CoroSuspendMDTableName, MDNode::get(C, SuspendMdEntries));
+
     Builder.SetInsertPoint(UnreachBB);
     Builder.CreateUnreachable();
     DBuilder.finalize();
diff --git a/llvm/test/Transforms/Coroutines/coro-split-md.ll b/llvm/test/Transforms/Coroutines/coro-split-md.ll
new file mode 100644
index 0000000000000..edf0a43e11685
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-md.ll
@@ -0,0 +1,69 @@
+; Tests that coro-split pass preserves metadata on suspend points.
+; RUN: opt < %s -coro-split-preserves-suspend-md -passes='cgscc(coro-split)' -S | FileCheck %s
+
+define ptr @f() presplitcoroutine {
+entry:
+  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
+  %need.alloc = call i1 @llvm.coro.alloc(token %id)
+  br i1 %need.alloc, label %dyn.alloc, label %begin
+
+dyn.alloc:
+  %size = call i32 @llvm.coro.size.i32()
+  %alloc = call ptr @malloc(i32 %size)
+  br label %begin
+
+begin:
+  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr %phi)
+  call void @print(i32 0)
+  %0 = call i8 @llvm.coro.suspend(token none, i1 false), !llvm.coro.suspend_md !0
+  switch i8 %0, label %suspend [i8 0, label %resume1
+                                i8 1, label %cleanup]
+resume1:
+  call void @print(i32 1)
+  %1 = call i8 @llvm.coro.suspend(token none, i1 false)
+  switch i8 %1, label %suspend [i8 0, label %resume2
+                                i8 1, label %cleanup]
+resume2:
+  call void @print(i32 2)
+  %2 = call i8 @llvm.coro.suspend(token none, i1 true), !llvm.coro.suspend_md !1
+  switch i8 %2, label %suspend [i8 0, label %trap
+                                i8 1, label %cleanup]
+trap:
+  call void @llvm.trap()
+  unreachable
+cleanup:
+  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
+  call void @free(ptr %mem)
+  br label %suspend
+suspend:
+  call i1 @llvm.coro.end(ptr %hdl, i1 0, token none)
+  ret ptr %hdl
+}
+
+; CHECK: define ptr @f() !llvm.coro.suspend_md_table ![[MD_TABLE:[0-9]+]]
+
+; CHECK-DAG: ![[MD_TABLE]] = !{![[S0_ROW:[0-9]+]], ![[S1_ROW:[0-9]+]]}
+; CHECK-DAG: ![[S0_ROW]] = !{i2 0, ![[S0_MD:[0-9]+]]}
+; CHECK-DAG: ![[S0_MD]] = !{!"waiting"}
+; CHECK-DAG: ![[S1_ROW]] = !{i2 -2, ![[S1_MD:[0-9]+]]}
+; CHECK-DAG: ![[S1_MD]] = !{!"done"}
+
+declare ptr @llvm.coro.free(token, ptr)
+declare i32 @llvm.coro.size.i32()
+declare i8  @llvm.coro.suspend(token, i1)
+declare void @llvm.coro.resume(ptr)
+declare void @llvm.coro.destroy(ptr)
+
+declare token @llvm.coro.id(i32, ptr, ptr, ptr)
+declare i1 @llvm.coro.alloc(token)
+declare ptr @llvm.coro.begin(token, ptr)
+declare i1 @llvm.coro.end(ptr, i1, token)
+
+declare noalias ptr @malloc(i32) allockind("alloc,uninitialized") "alloc-family"="malloc"
+declare void @print(i32)
+declare void @free(ptr) willreturn allockind("free") "alloc-family"="malloc"
+
+!0 = !{!"waiting"}
+!1 = !{!"done"}
+attributes #1 = { coro_elide_safe }



More information about the llvm-commits mailing list