[Lldb-commits] [lldb] [LLDB][NativePDB] Add support for `S_REGREL32_INDIR` (PR #186124)

via lldb-commits lldb-commits at lists.llvm.org
Thu Mar 12 07:15:13 PDT 2026


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

In #183172, I added support for reading `S_REGREL32_INDIR` records. This adds support in LLDB. The record is emitted by MSVC for structured bindings and in the compiler generated coroutine stubs.

It describes a location at `*(Register + Offset) + OffsetInUdt`. Equivalent to
```
DW_OP_breg{reg} {Offset}
DW_OP_deref
DW_OP_plus_uconst {OffsetInUdt}
```

LLVM doesn't create this record - it only creates `S_LOCAL`s for local variables. We'll probably need `S_DEFRANGE_REGISTER_REL_INDIR` for this - should be simple to figure out the structure, but I haven't seen that record emitted yet.

>From 6ac0070b5baf38ffbe950af2a8e2e16c88293b26 Mon Sep 17 00:00:00 2001
From: Nerixyz <nerixdev at outlook.de>
Date: Wed, 25 Feb 2026 15:07:57 +0100
Subject: [PATCH] [LLDB][NativePDB] Add support for `S_REGREL32_INDIR`

---
 .../NativePDB/DWARFLocationExpression.cpp     | 25 +++++++++++
 .../NativePDB/DWARFLocationExpression.h       |  4 ++
 .../NativePDB/PdbAstBuilderClang.cpp          |  9 ++++
 .../Plugins/SymbolFile/NativePDB/PdbUtil.cpp  | 20 +++++++++
 .../NativePDB/SymbolFileNativePDB.cpp         |  1 +
 .../NativePDB/structured-bindings-msvc.test   | 43 +++++++++++++++++++
 6 files changed, 102 insertions(+)
 create mode 100644 lldb/test/Shell/SymbolFile/NativePDB/structured-bindings-msvc.test

diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
index 3e66c58df693e..28bd2f076b680 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.cpp
@@ -153,6 +153,21 @@ static bool MakeRegisterBasedLocationExpressionInternal(
   return true;
 }
 
+/// *(reg + indir_offset) + offset
+static bool MakeRegisterBasedIndirectLocationExpressionInternal(
+    Stream &stream, llvm::codeview::RegisterId reg, RegisterKind &register_kind,
+    int32_t indir_offset, int32_t offset, lldb::ModuleSP module) {
+  if (!MakeRegisterBasedLocationExpressionInternal(stream, reg, register_kind,
+                                                   indir_offset, module))
+    return false;
+
+  stream.PutHex8(llvm::dwarf::DW_OP_deref);
+  stream.PutHex8(llvm::dwarf::DW_OP_plus_uconst);
+  stream.PutSLEB128(offset);
+
+  return true;
+}
+
 static DWARFExpression MakeRegisterBasedLocationExpressionInternal(
     llvm::codeview::RegisterId reg, std::optional<int32_t> relative_offset,
     lldb::ModuleSP module) {
@@ -173,6 +188,16 @@ DWARFExpression lldb_private::npdb::MakeRegRelLocationExpression(
   return MakeRegisterBasedLocationExpressionInternal(reg, offset, module);
 }
 
+DWARFExpression lldb_private::npdb::MakeRegRelIndirLocationExpression(
+    llvm::codeview::RegisterId reg, int32_t offset, int32_t offset_in_udt,
+    lldb::ModuleSP module) {
+  return MakeLocationExpressionInternal(
+      module, [&](Stream &stream, RegisterKind &register_kind) -> bool {
+        return MakeRegisterBasedIndirectLocationExpressionInternal(
+            stream, reg, register_kind, offset, offset_in_udt, module);
+      });
+}
+
 static bool EmitVFrameEvaluationDWARFExpression(
     llvm::StringRef program, llvm::Triple::ArchType arch_type, Stream &stream) {
   // VFrame value always stored in $TO pseudo-register
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
index 2f12d8bf0dd78..4a024dbd7e1e0 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/DWARFLocationExpression.h
@@ -39,6 +39,10 @@ MakeEnregisteredLocationExpression(llvm::codeview::RegisterId reg,
 DWARFExpression MakeRegRelLocationExpression(llvm::codeview::RegisterId reg,
                                              int32_t offset,
                                              lldb::ModuleSP module);
+DWARFExpression
+MakeRegRelIndirLocationExpression(llvm::codeview::RegisterId reg,
+                                  int32_t offset, int32_t offset_in_udt,
+                                  lldb::ModuleSP module);
 DWARFExpression MakeVFrameRelLocationExpression(llvm::StringRef fpo_program,
                                                 int32_t offset,
                                                 lldb::ModuleSP module);
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
index de0d46cc1c9a2..b07302bc7c7bf 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilderClang.cpp
@@ -231,6 +231,7 @@ static bool isLocalVariableType(SymbolKind K) {
   switch (K) {
   case S_REGISTER:
   case S_REGREL32:
+  case S_REGREL32_INDIR:
   case S_LOCAL:
     return true;
   default:
@@ -1141,6 +1142,14 @@ void PdbAstBuilderClang::CreateFunctionParameters(
       param_name = reg.Name;
       break;
     }
+    case S_REGREL32_INDIR: {
+      RegRelativeIndirSym reg(SymbolRecordKind::RegRelativeIndirSym);
+      cantFail(
+          SymbolDeserializer::deserializeAs<RegRelativeIndirSym>(sym, reg));
+      param_type = reg.Type;
+      param_name = reg.Name;
+      break;
+    }
     case S_REGISTER: {
       RegisterSym reg(SymbolRecordKind::RegisterSym);
       cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
index 6c66d86d3e645..a90ca32fffac7 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbUtil.cpp
@@ -252,6 +252,7 @@ PDB_SymType lldb_private::npdb::CVSymToPDBSym(SymbolKind kind) {
   case S_LOCAL:
   case S_BPREL32:
   case S_REGREL32:
+  case S_REGREL32_INDIR:
   case S_MANCONSTANT:
   case S_CONSTANT:
   case S_LDATA32:
@@ -611,6 +612,14 @@ VariableInfo lldb_private::npdb::GetVariableNameInfo(CVSymbol sym) {
     return result;
   }
 
+  if (sym.kind() == S_REGREL32_INDIR) {
+    RegRelativeIndirSym reg(SymbolRecordKind::RegRelativeIndirSym);
+    cantFail(SymbolDeserializer::deserializeAs<RegRelativeIndirSym>(sym, reg));
+    result.type = reg.Type;
+    result.name = reg.Name;
+    return result;
+  }
+
   if (sym.kind() == S_REGISTER) {
     RegisterSym reg(SymbolRecordKind::RegisterSym);
     cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
@@ -748,6 +757,17 @@ VariableInfo lldb_private::npdb::GetVariableLocationInfo(
     return result;
   }
 
+  if (sym.kind() == S_REGREL32_INDIR) {
+    RegRelativeIndirSym reg(SymbolRecordKind::RegRelativeIndirSym);
+    cantFail(SymbolDeserializer::deserializeAs<RegRelativeIndirSym>(sym, reg));
+    result.location = DWARFExpressionList(
+        module,
+        MakeRegRelIndirLocationExpression(reg.Register, reg.Offset,
+                                          reg.OffsetInUdt, module),
+        nullptr);
+    return result;
+  }
+
   if (sym.kind() == S_REGISTER) {
     RegisterSym reg(SymbolRecordKind::RegisterSym);
     cantFail(SymbolDeserializer::deserializeAs<RegisterSym>(sym, reg));
diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
index bed15839be1cd..22c4f19242c4d 100644
--- a/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
+++ b/lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp
@@ -2405,6 +2405,7 @@ size_t SymbolFileNativePDB::ParseVariablesForBlock(PdbCompilandSymId block_id) {
     VariableSP variable;
     switch (variable_cvs.kind()) {
     case S_REGREL32:
+    case S_REGREL32_INDIR:
     case S_REGISTER:
     case S_LOCAL:
       variable = GetOrCreateLocalVariable(block_id, child_sym_id, is_param);
diff --git a/lldb/test/Shell/SymbolFile/NativePDB/structured-bindings-msvc.test b/lldb/test/Shell/SymbolFile/NativePDB/structured-bindings-msvc.test
new file mode 100644
index 0000000000000..678931475d0f1
--- /dev/null
+++ b/lldb/test/Shell/SymbolFile/NativePDB/structured-bindings-msvc.test
@@ -0,0 +1,43 @@
+# REQUIRES: lld
+
+# Test that LLDB can show variables introduced in C++ 17 structured bindings
+# when compiled with MSVC.
+
+# RUN: split-file %s %t
+
+# RUN: %build --compiler=msvc --arch=64 --std=c++17 --nodefaultlib -o %t.exe -- %t/main.cpp
+# RUN: lldb-test symbols %t.exe | FileCheck %s --check-prefix=SYMBOLS
+# RUN: %lldb -f %t.exe -s %t/commands.input | FileCheck %s --check-prefix=LLDB
+
+#--- main.cpp
+
+struct Foo { int a; int b; };
+
+int main() {
+    Foo f{1, 2};
+
+    auto&[a, b] = f;
+    return a + b; // break here
+}
+
+#--- commands.input
+
+br set -p "break here"
+r
+v f
+v a
+v b
+q
+
+# SYMBOLS:      Function{{.*}}, demangled = main, type =
+# SYMBOLS-NEXT: Block{{.*}}, ranges = 
+# SYMBOLS-NEXT:   Variable{{.*}}, name = "f", type = {{.*}} (Foo), scope = local, location =
+# SYMBOLS-NEXT:   Variable{{.*}}, name = "b", type = {{.*}} (int), scope = local, location =
+# SYMBOLS-NEXT:   Variable{{.*}}, name = "a", type = {{.*}} (int), scope = local, location =
+
+# LLDB:      (lldb) v f
+# LLDB-NEXT: (Foo) f = (a = 1, b = 2)
+# LLDB-NEXT: (lldb) v a
+# LLDB-NEXT: (int) a = 1
+# LLDB-NEXT: (lldb) v b
+# LLDB-NEXT: (int) b = 2



More information about the lldb-commits mailing list