[Lldb-commits] [lldb] [lldb][NativePDB] Route expression function calls through FunctionCallLabel (PR #227021)

Charles Zablit via lldb-commits lldb-commits at lists.llvm.org
Fri Oct 2 08:58:35 PDT 2026


================
@@ -3182,6 +3163,149 @@ SymbolFileNativePDB::StripMangledFunctionName(const llvm::StringRef mangled,
   return mangled;
 }
 
+std::optional<std::string>
+SymbolFileNativePDB::MakeFunctionCallLabel(PdbSymUid uid,
+                                           llvm::StringRef lookup_name) {
+  if (lookup_name.empty())
+    return std::nullopt;
+
+  lldb::ModuleSP module_sp = m_objfile_sp->GetModule();
+  if (!module_sp)
+    return std::nullopt;
+
+  return FunctionCallLabel{/*discriminator=*/{},
+                           /*module_id=*/module_sp->GetID(),
+                           /*symbol_id=*/uid.toOpaqueId(),
+                           /*lookup_name=*/lookup_name}
+      .toString();
+}
+
+std::optional<std::string>
+SymbolFileNativePDB::GetFunctionCallLabel(PdbCompilandSymId id) {
+  const CompilandIndexItem *cci = m_index->compilands().GetCompiland(id.modi);
+  if (!cci)
+    return std::nullopt;
+
+  CVSymbol sym_record = cci->m_debug_stream.readSymbolAtOffset(id.offset);
+  if (sym_record.kind() != S_LPROC32 && sym_record.kind() != S_GPROC32)
+    return std::nullopt;
+
+  ProcSym proc(static_cast<SymbolRecordKind>(sym_record.kind()));
+  if (auto err = SymbolDeserializer::deserializeAs<ProcSym>(sym_record, proc)) {
+    LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), std::move(err),
+                   "Failed to deserialize ProcSym record: {0}");
+    return std::nullopt;
+  }
+
+  // Functions with internal linkage have no public symbol, so fall back to the
+  // name in the procedure record.
+  llvm::StringRef lookup_name =
+      FindMangledSymbol(SegmentOffset(proc.Segment, proc.CodeOffset),
+                        proc.FunctionType)
+          .value_or(proc.Name);
+  return MakeFunctionCallLabel(id, lookup_name);
+}
+
+std::optional<std::string>
+SymbolFileNativePDB::GetMethodCallLabel(TypeIndex method_type,
+                                        llvm::StringRef qualified_name) {
+  if (std::optional<PdbCompilandSymId> func_id =
+          FindMethodDefinition(qualified_name, method_type))
+    return GetFunctionCallLabel(*func_id);
+  return std::nullopt;
+}
+
+std::optional<PdbCompilandSymId>
+SymbolFileNativePDB::FindMethodDefinition(llvm::StringRef qualified_name,
+                                          TypeIndex method_type) {
+  CacheGlobalBaseNames();
+
+  std::vector<uint32_t> ids;
+  m_func_full_names.GetValues(ConstString(qualified_name), ids);
+
+  for (uint32_t id : ids) {
+    CVSymbol sym = m_index->ReadSymbolRecord(PdbGlobalSymId{id, false});
+    if (sym.kind() != S_PROCREF && sym.kind() != S_LPROCREF)
+      continue;
+    auto ref_or_err = SymbolDeserializer::deserializeAs<ProcRefSym>(sym);
+    if (!ref_or_err) {
+      llvm::consumeError(ref_or_err.takeError());
+      continue;
+    }
+
+    PdbCompilandSymId func_id(ref_or_err->modi(), ref_or_err->SymOffset);
+    CVSymbol func_sym = m_index->ReadSymbolRecord(func_id);
+    if (func_sym.kind() != S_GPROC32 && func_sym.kind() != S_LPROC32)
+      continue;
+    auto proc_or_err = SymbolDeserializer::deserializeAs<ProcSym>(func_sym);
+    if (!proc_or_err) {
+      llvm::consumeError(proc_or_err.takeError());
+      continue;
+    }
+
+    // Overloads share the name, so only the type identifies the definition.
+    if (proc_or_err->FunctionType == method_type)
+      return func_id;
+  }
+  return std::nullopt;
+}
+
+/// Returns true if \p discriminator names a structor variant that is the
+/// function described by the procedure record.
+///
+/// In the Microsoft ABI, complete and base object structors share one
+/// definition, while closures and deleting destructors are separate functions
+/// without debug info.
+static bool IsDefinedStructorVariant(llvm::StringRef discriminator) {
+  if (discriminator.empty())
+    return true;
+
+  const bool is_ctor = discriminator.consume_front("C");
+  if (!is_ctor && !discriminator.consume_front("D"))
+    return false;
+
+  uint64_t kind;
+  if (!llvm::to_integer(discriminator, kind))
+    return false;
+
+  if (is_ctor)
+    return kind == clang::CXXCtorType::Ctor_Complete ||
+           kind == clang::CXXCtorType::Ctor_Base;
+  return kind == clang::CXXDtorType::Dtor_Complete ||
+         kind == clang::CXXDtorType::Dtor_Base;
+}
+
+llvm::Expected<SymbolContext>
+SymbolFileNativePDB::ResolveFunctionCallLabel(FunctionCallLabel &label) {
+  std::lock_guard<std::recursive_mutex> guard(GetModuleMutex());
+
+  if (!IsDefinedStructorVariant(label.discriminator))
----------------
charles-zablit wrote:

Clang fills it in when it mangles the label of a structor. I added a clarifying comment.
https://github.com/llvm/llvm-project/blob/fe1de64cb3c255ecf6f035ca3562c4550479f632/clang/lib/AST/Mangle.cpp#L227

https://github.com/llvm/llvm-project/pull/227021


More information about the lldb-commits mailing list