[llvm] [SampleProfileMatcher] Add direct basename early matching for orphan functions (PR #184409)

Grigory Pastukhov via llvm-commits llvm-commits at lists.llvm.org
Wed Mar 4 17:54:39 PST 2026


================
@@ -728,6 +732,121 @@ void SampleProfileMatcher::findFunctionsWithoutProfile() {
   }
 }
 
+// Demangle \p FName and return the base function name (stripping namespaces,
+// templates, and parameter types). Returns an empty string on failure.
+static std::string getDemangledBaseName(ItaniumPartialDemangler &Demangler,
+                                        StringRef FName) {
+  auto FunctionName = FName.str();
+  if (Demangler.partialDemangle(FunctionName.c_str()))
+    return std::string();
+  size_t BaseNameSize = 0;
+  // The demangler API follows the __cxa_demangle one, and thus needs a
+  // pointer that originates from malloc (or nullptr) and the caller is
+  // responsible for free()-ing the buffer.
+  char *BaseNamePtr = Demangler.getFunctionBaseName(nullptr, &BaseNameSize);
+  std::string Result = (BaseNamePtr && BaseNameSize)
+                           ? std::string(BaseNamePtr, BaseNameSize)
+                           : std::string();
+  free(BaseNamePtr);
+  // Trim trailing whitespace/null — getFunctionBaseName may include trailing
+  // characters in the reported size.
+  while (!Result.empty() && (Result.back() == ' ' || Result.back() == '\0'))
+    Result.pop_back();
+  return Result;
+}
+
+void SampleProfileMatcher::matchFunctionsWithoutProfileByBasename() {
+  if (FunctionsWithoutProfile.empty() || !LoadFuncProfileforCGMatching)
+    return;
+  auto *NameTable = Reader.getNameTable();
+  if (!NameTable)
+    return;
+
+  ItaniumPartialDemangler Demangler;
+
+  // Build a map from demangled basename to orphan function. Only keep
+  // basenames that map to exactly one orphan — ambiguous basenames like
+  // "get" or "operator()" would produce false positives.
+  StringMap<Function *> OrphansByBaseName;
+  StringSet<> AmbiguousBaseNames;
+  for (auto &[FuncId, Func] : FunctionsWithoutProfile) {
+    std::string BaseName = getDemangledBaseName(Demangler, Func->getName());
+    if (BaseName.empty() || AmbiguousBaseNames.count(BaseName))
+      continue;
+    auto [It, Inserted] = OrphansByBaseName.try_emplace(BaseName, Func);
+    if (!Inserted) {
+      // More than one orphan shares this basename — mark ambiguous.
+      OrphansByBaseName.erase(It);
+      AmbiguousBaseNames.insert(BaseName);
+    }
+  }
+  if (OrphansByBaseName.empty())
+    return;
+
+  // Scan the profile NameTable for candidates whose demangled basename matches
+  // a unique orphan. Use a quick substring check to avoid demangling every
+  // entry. Only keep 1:1 basename matches (exactly one profile candidate).
+  // Maps basename -> profile FunctionId; entries with multiple candidates are
+  // removed.
+  StringMap<FunctionId> CandidateByBaseName;
+  for (auto &ProfileFuncId : *NameTable) {
+    StringRef ProfName = ProfileFuncId.stringRef();
+    if (ProfName.empty())
+      continue;
+    for (auto &[BaseName, _] : OrphansByBaseName) {
----------------
grigorypas wrote:

I wonder what is the build time impact given the nested for loops. Did you measure on any binaries?  

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


More information about the llvm-commits mailing list