[llvm] [CodeView] Add `CoroutineKind` to `S_FRAMEPROC` flags (PR #223018)

via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 12:11:20 PDT 2026


https://github.com/Nerixyz created https://github.com/llvm/llvm-project/pull/223018

[`IDiaSymbol8`](https://learn.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/idiasymbol8?view=visualstudio) adds coroutine related getters. I added the associated symbols in #221034. This adds the coroutine kind (what's behind `get_coroutineKind`).

It's stored in three bits inside the flags of `S_FRAMEPROC`. The values are identical to those of [`CV_CoroutineKind_e`](https://learn.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/cv-coroutinekind-e?view=visualstudio).

I tested this on an MSVC generated PDB.

>From 71ab67220e1125e5e6e83142fe2b40a83cf402c4 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Fri, 11 Sep 2026 21:01:36 +0200
Subject: [PATCH] [CodeView] Add `CoroutineKind` to `S_FRAMEPROC` flags

---
 .../llvm/DebugInfo/CodeView/CodeView.h        |  19 ++-
 .../llvm/DebugInfo/CodeView/EnumTables.h      |   1 +
 .../llvm/DebugInfo/CodeView/SymbolRecord.h    |   9 +
 llvm/lib/DebugInfo/CodeView/EnumTables.cpp    |  13 ++
 llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp   |  12 ++
 .../llvm-pdbutil/frameproc-coro-kind.yaml     | 156 ++++++++++++++++++
 .../llvm-pdbutil/MinimalSymbolDumper.cpp      |  15 ++
 7 files changed, 224 insertions(+), 1 deletion(-)
 create mode 100644 llvm/test/tools/llvm-pdbutil/frameproc-coro-kind.yaml

diff --git a/llvm/include/llvm/DebugInfo/CodeView/CodeView.h b/llvm/include/llvm/DebugInfo/CodeView/CodeView.h
index 3ce3d538d0adc..bb0af5165e3c9 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/CodeView.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/CodeView.h
@@ -223,7 +223,8 @@ enum class FrameProcedureOptions : uint32_t {
   ValidProfileCounts = 0x00080000,
   OptimizedForSpeed = 0x00100000,
   GuardCfg = 0x00200000,
-  GuardCfw = 0x00400000
+  GuardCfw = 0x00400000,
+  CoroutineKindMask = 0x0E000000,
 };
 CV_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(FrameProcedureOptions)
 
@@ -630,6 +631,22 @@ enum class AssociationKind : uint16_t {
   /// Associated symbol is the primary coroutine function.
   Coroutine,
 };
+
+/// Three bit value indicating the coroutine kind of a function.
+///
+/// From `CV_CoroutineKind_e` (cvconst.h)
+enum class CoroutineKind : uint8_t {
+  /// Not a coroutine.
+  None = 0,
+  /// The original coroutine function.
+  Primary = 1,
+  /// Initialization function, sets up the coroutine frame.
+  Init = 2,
+  /// Resume function, contains the coroutine body code.
+  Resume = 3,
+  /// Destroy function, tears down the coroutine frame.
+  Destroy = 4,
+};
 }
 }
 
diff --git a/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h b/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h
index 3e128f70381ce..e957c72882dcf 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/EnumTables.h
@@ -51,6 +51,7 @@ LLVM_ABI EnumStrings<uint8_t, 1> getFunctionOptionEnum();
 LLVM_ABI EnumStrings<uint16_t, 1> getLabelTypeEnum();
 LLVM_ABI EnumStrings<uint16_t, 1> getJumpTableEntrySizeNames();
 LLVM_ABI EnumStrings<uint16_t, 1> getAssociationKindNames();
+LLVM_ABI EnumStrings<uint16_t, 1> getCoroutineKindNames();
 
 } // end namespace codeview
 } // end namespace llvm
diff --git a/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h b/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h
index fe8e66f067553..45a33b3c6b4c3 100644
--- a/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h
+++ b/llvm/include/llvm/DebugInfo/CodeView/SymbolRecord.h
@@ -902,6 +902,15 @@ class FrameProcSym : public SymbolRecord {
     return decodeFramePtrReg(getEncodedParamFramePtrReg(), CPU);
   }
 
+  CoroutineKind getCoroutineKind() const {
+    return CoroutineKind((uint32_t(Flags) >> 25U) & 7U);
+  }
+
+  void setCoroutineKind(CoroutineKind K) {
+    FrameProcedureOptions CoroFlags{(static_cast<uint32_t>(K) & 7U) << 25U};
+    Flags = (Flags & ~FrameProcedureOptions::CoroutineKindMask) | CoroFlags;
+  }
+
   uint32_t RecordOffset = 0;
 
 private:
diff --git a/llvm/lib/DebugInfo/CodeView/EnumTables.cpp b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp
index 92fa1ad8beff9..d5a496a688cf0 100644
--- a/llvm/lib/DebugInfo/CodeView/EnumTables.cpp
+++ b/llvm/lib/DebugInfo/CodeView/EnumTables.cpp
@@ -644,5 +644,18 @@ EnumStrings<uint16_t> getAssociationKindNames() {
   return AssociationKindNames;
 }
 
+EnumStrings<uint16_t> getCoroutineKindNames() {
+  constexpr EnumStringDef<uint16_t> CoroutineKindNameDefs[] = {
+      CV_ENUM_CLASS_ENT(CoroutineKind, None),
+      CV_ENUM_CLASS_ENT(CoroutineKind, Primary),
+      CV_ENUM_CLASS_ENT(CoroutineKind, Init),
+      CV_ENUM_CLASS_ENT(CoroutineKind, Resume),
+      CV_ENUM_CLASS_ENT(CoroutineKind, Destroy),
+  };
+  static constexpr auto CoroutineKindNames =
+      BUILD_ENUM_STRINGS(CoroutineKindNameDefs);
+  return CoroutineKindNames;
+}
+
 } // end namespace codeview
 } // end namespace llvm
diff --git a/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp b/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp
index 9acb38f5f33ed..2ba6e4f91ccbe 100644
--- a/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp
+++ b/llvm/lib/ObjectYAML/CodeViewYAMLSymbols.cpp
@@ -65,6 +65,7 @@ LLVM_YAML_DECLARE_ENUM_TRAITS(JumpTableEntrySize)
 LLVM_YAML_DECLARE_ENUM_TRAITS(SourceLanguage)
 LLVM_YAML_DECLARE_ENUM_TRAITS(EncodedFramePtrReg)
 LLVM_YAML_DECLARE_ENUM_TRAITS(AssociationKind)
+LLVM_YAML_DECLARE_ENUM_TRAITS(CoroutineKind)
 
 LLVM_YAML_STRONG_TYPEDEF(StringRef, TypeName)
 
@@ -234,6 +235,14 @@ void ScalarEnumerationTraits<AssociationKind>::enumeration(
   IO.enumFallback<Hex16>(Kind);
 }
 
+void ScalarEnumerationTraits<CoroutineKind>::enumeration(IO &IO,
+                                                         CoroutineKind &Kind) {
+  auto Names = getCoroutineKindNames();
+  for (const auto &E : Names) {
+    IO.enumCase(Kind, E.name(), static_cast<CoroutineKind>(E.value()));
+  }
+}
+
 namespace llvm {
 namespace yaml {
 template <> struct MappingTraits<LocalVariableAddrRange> {
@@ -545,13 +554,16 @@ template <> void SymbolRecordImpl<FrameProcSym>::map(IO &IO) {
   FrameProcedureOptions Flags = Symbol.getFlags();
   EncodedFramePtrReg LocalFP = Symbol.getEncodedLocalFramePtrReg();
   EncodedFramePtrReg ParamFP = Symbol.getEncodedParamFramePtrReg();
+  CoroutineKind CoroKind = Symbol.getCoroutineKind();
   IO.mapRequired("Flags", Flags);
   IO.mapOptional("LocalFramePtrReg", LocalFP, EncodedFramePtrReg::None);
   IO.mapOptional("ParamFramePtrReg", ParamFP, EncodedFramePtrReg::None);
+  IO.mapOptional("CoroutineKind", CoroKind, CoroutineKind::None);
   if (!IO.outputting()) {
     Symbol.setFlags(Flags);
     Symbol.setEncodedLocalFramePtrReg(LocalFP);
     Symbol.setEncodedParamFramePtrReg(ParamFP);
+    Symbol.setCoroutineKind(CoroKind);
   }
 }
 
diff --git a/llvm/test/tools/llvm-pdbutil/frameproc-coro-kind.yaml b/llvm/test/tools/llvm-pdbutil/frameproc-coro-kind.yaml
new file mode 100644
index 0000000000000..7730b1b8061ca
--- /dev/null
+++ b/llvm/test/tools/llvm-pdbutil/frameproc-coro-kind.yaml
@@ -0,0 +1,156 @@
+# RUN: llvm-pdbutil yaml2pdb %s --pdb=%t.pdb
+# RUN: llvm-pdbutil dump --symbols %t.pdb | FileCheck --check-prefix=YAML2PDB %s
+
+# RUN: llvm-pdbutil pdb2yaml --module-syms --minimal %t.pdb > %t.yaml
+# RUN: FileCheck --input-file=%t.yaml --check-prefix=PDB2YAML %s
+
+# YAML2PDB:            4 | S_FRAMEPROC [size = 32]
+# YAML2PDB-NEXT:           size = 1, padding size = 0, offset to padding = 0
+# YAML2PDB-NEXT:           bytes of callee saved registers = 0, exception handler addr = 0000:0000
+# YAML2PDB-NEXT:           local fp reg = RSP, param fp reg = RSP
+# YAML2PDB-NEXT:           flags = has async eh | opt speed
+# YAML2PDB-NEXT:      36 | S_FRAMEPROC [size = 32]
+# YAML2PDB-NEXT:           size = 2, padding size = 0, offset to padding = 0
+# YAML2PDB-NEXT:           bytes of callee saved registers = 0, exception handler addr = 0000:0000
+# YAML2PDB-NEXT:           local fp reg = RSP, param fp reg = RSP
+# YAML2PDB-NEXT:           flags = has async eh | opt speed
+# YAML2PDB-NEXT:      68 | S_FRAMEPROC [size = 32]
+# YAML2PDB-NEXT:           size = 3, padding size = 0, offset to padding = 0
+# YAML2PDB-NEXT:           bytes of callee saved registers = 0, exception handler addr = 0000:0000
+# YAML2PDB-NEXT:           local fp reg = RSP, param fp reg = RSP
+# YAML2PDB-NEXT:           flags = has async eh | opt speed, coroutine kind = primary
+# YAML2PDB-NEXT:     100 | S_FRAMEPROC [size = 32]
+# YAML2PDB-NEXT:           size = 4, padding size = 0, offset to padding = 0
+# YAML2PDB-NEXT:           bytes of callee saved registers = 0, exception handler addr = 0000:0000
+# YAML2PDB-NEXT:           local fp reg = RSP, param fp reg = RSP
+# YAML2PDB-NEXT:           flags = has async eh | opt speed, coroutine kind = init
+# YAML2PDB-NEXT:     132 | S_FRAMEPROC [size = 32]
+# YAML2PDB-NEXT:           size = 5, padding size = 0, offset to padding = 0
+# YAML2PDB-NEXT:           bytes of callee saved registers = 0, exception handler addr = 0000:0000
+# YAML2PDB-NEXT:           local fp reg = RSP, param fp reg = RSP
+# YAML2PDB-NEXT:           flags = has async eh | opt speed, coroutine kind = resume
+# YAML2PDB-NEXT:     164 | S_FRAMEPROC [size = 32]
+# YAML2PDB-NEXT:           size = 6, padding size = 0, offset to padding = 0
+# YAML2PDB-NEXT:           bytes of callee saved registers = 0, exception handler addr = 0000:0000
+# YAML2PDB-NEXT:           local fp reg = RSP, param fp reg = RSP
+# YAML2PDB-NEXT:           flags = has async eh | opt speed, coroutine kind = destroy
+
+# PDB2YAML:          - Kind:            S_FRAMEPROC
+# PDB2YAML:            FrameProcSym:
+# PDB2YAML:              TotalFrameBytes: 1
+# Created with --minimal and None is the default.
+# PDB2YAML-NOT: CoroutineKind:   None
+
+# PDB2YAML:          - Kind:            S_FRAMEPROC
+# PDB2YAML:            FrameProcSym:
+# PDB2YAML:              TotalFrameBytes: 2
+# Created with --minimal and None is the default.
+# PDB2YAML-NOT: CoroutineKind:   None
+
+# PDB2YAML:          - Kind:            S_FRAMEPROC
+# PDB2YAML:            FrameProcSym:
+# PDB2YAML:              TotalFrameBytes: 3
+# PDB2YAML:              CoroutineKind:   Primary
+
+# PDB2YAML:          - Kind:            S_FRAMEPROC
+# PDB2YAML:            FrameProcSym:
+# PDB2YAML:              TotalFrameBytes: 4
+# PDB2YAML:              CoroutineKind:   Init
+
+# PDB2YAML:          - Kind:            S_FRAMEPROC
+# PDB2YAML:            FrameProcSym:
+# PDB2YAML:              TotalFrameBytes: 5
+# PDB2YAML:              CoroutineKind:   Resume
+
+# PDB2YAML:          - Kind:            S_FRAMEPROC
+# PDB2YAML:            FrameProcSym:
+# PDB2YAML:              TotalFrameBytes: 6
+# PDB2YAML:              CoroutineKind:   Destroy
+
+---
+DbiStream:
+  VerHeader:       V70
+  Age:             1
+  BuildNumber:     36403
+  PdbDllVersion:   36257
+  PdbDllRbld:      0
+  Flags:           1
+  MachineType:     Amd64
+  Modules:
+    - Module:          '/tmp/test.obj'
+      ObjFile:         '/tmp/test.obj'
+      Modi:
+        Signature:       4
+        Records:
+          - Kind:            S_FRAMEPROC
+            FrameProcSym:
+              TotalFrameBytes: 1
+              PaddingFrameBytes: 0
+              OffsetToPadding: 0
+              BytesOfCalleeSavedRegisters: 0
+              OffsetOfExceptionHandler: 0
+              SectionIdOfExceptionHandler: 0
+              Flags:           [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+              LocalFramePtrReg: StackPtr
+              ParamFramePtrReg: StackPtr
+          - Kind:            S_FRAMEPROC
+            FrameProcSym:
+              TotalFrameBytes: 2
+              PaddingFrameBytes: 0
+              OffsetToPadding: 0
+              BytesOfCalleeSavedRegisters: 0
+              OffsetOfExceptionHandler: 0
+              SectionIdOfExceptionHandler: 0
+              Flags:           [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+              LocalFramePtrReg: StackPtr
+              ParamFramePtrReg: StackPtr
+              CoroutineKind:   None
+          - Kind:            S_FRAMEPROC
+            FrameProcSym:
+              TotalFrameBytes: 3
+              PaddingFrameBytes: 0
+              OffsetToPadding: 0
+              BytesOfCalleeSavedRegisters: 0
+              OffsetOfExceptionHandler: 0
+              SectionIdOfExceptionHandler: 0
+              Flags:           [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+              LocalFramePtrReg: StackPtr
+              ParamFramePtrReg: StackPtr
+              CoroutineKind:   Primary
+          - Kind:            S_FRAMEPROC
+            FrameProcSym:
+              TotalFrameBytes: 4
+              PaddingFrameBytes: 0
+              OffsetToPadding: 0
+              BytesOfCalleeSavedRegisters: 0
+              OffsetOfExceptionHandler: 0
+              SectionIdOfExceptionHandler: 0
+              Flags:           [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+              LocalFramePtrReg: StackPtr
+              ParamFramePtrReg: StackPtr
+              CoroutineKind:   Init
+          - Kind:            S_FRAMEPROC
+            FrameProcSym:
+              TotalFrameBytes: 5
+              PaddingFrameBytes: 0
+              OffsetToPadding: 0
+              BytesOfCalleeSavedRegisters: 0
+              OffsetOfExceptionHandler: 0
+              SectionIdOfExceptionHandler: 0
+              Flags:           [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+              LocalFramePtrReg: StackPtr
+              ParamFramePtrReg: StackPtr
+              CoroutineKind:   Resume
+          - Kind:            S_FRAMEPROC
+            FrameProcSym:
+              TotalFrameBytes: 6
+              PaddingFrameBytes: 0
+              OffsetToPadding: 0
+              BytesOfCalleeSavedRegisters: 0
+              OffsetOfExceptionHandler: 0
+              SectionIdOfExceptionHandler: 0
+              Flags:           [ AsynchronousExceptionHandling, OptimizedForSpeed ]
+              LocalFramePtrReg: StackPtr
+              ParamFramePtrReg: StackPtr
+              CoroutineKind:   Destroy
+...
diff --git a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
index f3abc000430e2..6e83249efe2be 100644
--- a/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
+++ b/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -138,6 +138,17 @@ static std::string formatFrameProcedureOptions(uint32_t IndentLevel,
   return typesetItemList(Opts, 4, IndentLevel, " | ");
 }
 
+static std::string formatCoroutineKind(CoroutineKind Kind) {
+  switch (Kind) {
+    RETURN_CASE(CoroutineKind, None, "none");
+    RETURN_CASE(CoroutineKind, Primary, "primary");
+    RETURN_CASE(CoroutineKind, Init, "init");
+    RETURN_CASE(CoroutineKind, Resume, "resume");
+    RETURN_CASE(CoroutineKind, Destroy, "destroy");
+  }
+  return formatUnknownEnum(Kind);
+}
+
 static std::string formatPublicSymFlags(uint32_t IndentLevel,
                                         PublicSymFlags Flags) {
   std::vector<std::string> Opts;
@@ -727,6 +738,10 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameProcSym &FP) {
       formatRegisterId(FP.getParamFramePtrReg(CompilationCPU), CompilationCPU));
   P.formatLine("flags = {0}",
                formatFrameProcedureOptions(P.getIndentLevel() + 9, FP.Flags));
+  if (FP.getCoroutineKind() != CoroutineKind::None) {
+    P.format(", coroutine kind = {0}",
+             formatCoroutineKind(FP.getCoroutineKind()));
+  }
   return Error::success();
 }
 



More information about the llvm-commits mailing list