[llvm] [nfc][test][SampleProf] Add unittest coverage to getCanonicalFnName (PR #220319)

Vitaly Buka via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 1 14:51:05 PDT 2026


https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/220319

>From 6b8a42d5373106815b379ec1c83ac952d5323b55 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 1 Sep 2026 11:05:02 -0700
Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?=
 =?UTF-8?q?itial=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7
---
 llvm/unittests/ProfileData/SampleProfTest.cpp | 222 ++++++++++++++++++
 1 file changed, 222 insertions(+)

diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index 7e1f6ea64f571..62623e5be3043 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -10,9 +10,12 @@
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
 #include "llvm/ProfileData/SampleProfReader.h"
 #include "llvm/ProfileData/SampleProfWriter.h"
 #include "llvm/Support/Casting.h"
@@ -659,4 +662,223 @@ TEST_F(SampleProfTest, ProfileSymbolListMD5) {
   EXPECT_EQ(2u, List.size());
 }
 
+struct ScopedHasUniqSuffix {
+  bool OldVal;
+  ScopedHasUniqSuffix(bool NewVal) : OldVal(FunctionSamples::HasUniqSuffix) {
+    FunctionSamples::HasUniqSuffix = NewVal;
+  }
+  ~ScopedHasUniqSuffix() { FunctionSamples::HasUniqSuffix = OldVal; }
+};
+
+TEST(SampleProfCanonicalNameTest, SelectedPolicyDefault) {
+  // Plain names without dots.
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo"));
+  EXPECT_EQ("_Z3barv", FunctionSamples::getCanonicalFnName("_Z3barv"));
+  EXPECT_EQ("", FunctionSamples::getCanonicalFnName(""));
+
+  // Suffix elision policy "selected" is default.
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo", "selected"));
+
+  // Names with dot but no matching suffix.
+  EXPECT_EQ("foo.bar", FunctionSamples::getCanonicalFnName("foo.bar"));
+  EXPECT_EQ("foo.1", FunctionSamples::getCanonicalFnName("foo.1"));
+  EXPECT_EQ("foo.part", FunctionSamples::getCanonicalFnName("foo.part"));
+  EXPECT_EQ("foo.llvm", FunctionSamples::getCanonicalFnName("foo.llvm"));
+  EXPECT_EQ("foo.__uniq", FunctionSamples::getCanonicalFnName("foo.__uniq"));
+  EXPECT_EQ(".foo", FunctionSamples::getCanonicalFnName(".foo"));
+
+  // .llvm. suffix (ThinLTO promotion / renaming).
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.llvm.1234"));
+  EXPECT_EQ("_Z3foov",
+            FunctionSamples::getCanonicalFnName("_Z3foov.llvm.1234567890"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.llvm."));
+  EXPECT_EQ("foo.llvm.1234.bar",
+            FunctionSamples::getCanonicalFnName("foo.llvm.1234.bar"));
+  EXPECT_EQ("my_llvm_func",
+            FunctionSamples::getCanonicalFnName("my_llvm_func"));
+
+  // .part. suffix.
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.part.1"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.part.42"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.part."));
+  EXPECT_EQ("foo.part.1.bar",
+            FunctionSamples::getCanonicalFnName("foo.part.1.bar"));
+
+  // Combined suffixes in canonical order.
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.part.1.llvm.456"));
+
+  // Suffixes in non-canonical order: .llvm. cannot be stripped if .part.
+  // follows it, but .part. can be stripped leaving .llvm.
+  EXPECT_EQ("foo.llvm.1",
+            FunctionSamples::getCanonicalFnName("foo.llvm.1.part.2"));
+
+  // Repeated suffix: only the last one is stripped.
+  EXPECT_EQ("foo.llvm.1",
+            FunctionSamples::getCanonicalFnName("foo.llvm.1.llvm.2"));
+}
+
+TEST(SampleProfCanonicalNameTest, SelectedPolicyHasUniqSuffix) {
+  // When HasUniqSuffix is false, .__uniq. is stripped.
+  {
+    ScopedHasUniqSuffix Scope(false);
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.__uniq.123"));
+    EXPECT_EQ("foo",
+              FunctionSamples::getCanonicalFnName("foo.__uniq.123.llvm.456"));
+    EXPECT_EQ("foo",
+              FunctionSamples::getCanonicalFnName("foo.__uniq.123.part.1"));
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName(
+                         "foo.__uniq.123.part.1.llvm.456"));
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.llvm.456"));
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.part.1"));
+  }
+
+  // When HasUniqSuffix is true, .__uniq. is preserved.
+  {
+    ScopedHasUniqSuffix Scope(true);
+    EXPECT_EQ("foo.__uniq.123",
+              FunctionSamples::getCanonicalFnName("foo.__uniq.123"));
+    EXPECT_EQ("foo.__uniq.123",
+              FunctionSamples::getCanonicalFnName("foo.__uniq.123.llvm.456"));
+    EXPECT_EQ("foo.__uniq.123",
+              FunctionSamples::getCanonicalFnName("foo.__uniq.123.part.1"));
+    EXPECT_EQ("foo.__uniq.123", FunctionSamples::getCanonicalFnName(
+                                    "foo.__uniq.123.part.1.llvm.456"));
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.llvm.456"));
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.part.1"));
+  }
+}
+
+TEST(SampleProfCanonicalNameTest, AllPolicy) {
+  for (StringRef Attr : {"all", ""}) {
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo", Attr));
+    EXPECT_EQ("foo",
+              FunctionSamples::getCanonicalFnName("foo.llvm.1234", Attr));
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.part.1", Attr));
+    EXPECT_EQ("foo",
+              FunctionSamples::getCanonicalFnName("foo.__uniq.1234", Attr));
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.bar", Attr));
+    EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.1.2.3", Attr));
+    EXPECT_EQ("", FunctionSamples::getCanonicalFnName(".foo", Attr));
+    EXPECT_EQ("", FunctionSamples::getCanonicalFnName("", Attr));
+  }
+}
+
+TEST(SampleProfCanonicalNameTest, NonePolicy) {
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo", "none"));
+  EXPECT_EQ("foo.llvm.1234",
+            FunctionSamples::getCanonicalFnName("foo.llvm.1234", "none"));
+  EXPECT_EQ("foo.part.1",
+            FunctionSamples::getCanonicalFnName("foo.part.1", "none"));
+  EXPECT_EQ("foo.__uniq.1234",
+            FunctionSamples::getCanonicalFnName("foo.__uniq.1234", "none"));
+  EXPECT_EQ("foo.bar", FunctionSamples::getCanonicalFnName("foo.bar", "none"));
+  EXPECT_EQ("", FunctionSamples::getCanonicalFnName("", "none"));
+}
+
+TEST(SampleProfCanonicalNameTest, CoroFnName) {
+  // Default "selected" policy.
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalCoroFnName("foo.cleanup"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalCoroFnName("foo.destroy"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalCoroFnName("foo.resume"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalCoroFnName("foo.llvm.1234"));
+  EXPECT_EQ("_Zfoo",
+            FunctionSamples::getCanonicalCoroFnName("_Zfoo.llvm.1234.cleanup"));
+  EXPECT_EQ("_Zfoo",
+            FunctionSamples::getCanonicalCoroFnName("_Zfoo.llvm.1234.destroy"));
+  EXPECT_EQ("_Zfoo",
+            FunctionSamples::getCanonicalCoroFnName("_Zfoo.llvm.1234.resume"));
+  EXPECT_EQ("foo.other", FunctionSamples::getCanonicalCoroFnName("foo.other"));
+
+  // Policy "none".
+  EXPECT_EQ("_Zfoo.llvm.1234.cleanup", FunctionSamples::getCanonicalCoroFnName(
+                                           "_Zfoo.llvm.1234.cleanup", "none"));
+
+  // Policy "all".
+  EXPECT_EQ("_Zfoo", FunctionSamples::getCanonicalCoroFnName(
+                         "_Zfoo.llvm.1234.cleanup", "all"));
+}
+
+TEST(SampleProfCanonicalNameTest, CustomSuffixes) {
+  const SmallVector<StringRef, 2> Suffixes{".custom.", ".cfi"};
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.cfi", Suffixes));
+  EXPECT_EQ("foo",
+            FunctionSamples::getCanonicalFnName("foo.custom.123", Suffixes));
+  // In Suffixes {".custom.", ".cfi"}, .custom. is evaluated before .cfi.
+  // Because .cfi was appended after .custom.123, .custom. cannot be stripped
+  // first, leaving "foo.custom.123".
+  EXPECT_EQ("foo.custom.123", FunctionSamples::getCanonicalFnName(
+                                  "foo.custom.123.cfi", Suffixes));
+  // Conversely, when ordered {".cfi", ".custom."}, both are stripped.
+  const SmallVector<StringRef, 2> OrderedSuffixes{".cfi", ".custom."};
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.custom.123.cfi",
+                                                       OrderedSuffixes));
+
+  // Suffix without trailing dot matches prefix of the component if no dot
+  // follows.
+  EXPECT_EQ("foo",
+            FunctionSamples::getCanonicalFnName("foo.cfiUnrelated", Suffixes));
+  EXPECT_EQ("foo.cfiSomething.123", FunctionSamples::getCanonicalFnName(
+                                        "foo.cfiSomething.123", Suffixes));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.cfi_jt", Suffixes));
+  // Suffix not in the list is preserved.
+  EXPECT_EQ("foo.llvm.123",
+            FunctionSamples::getCanonicalFnName("foo.llvm.123", Suffixes));
+
+  // Empty suffixes list preserves the name under "selected".
+  EXPECT_EQ("foo.llvm.123", FunctionSamples::getCanonicalFnName(
+                                "foo.llvm.123", {}, "selected"));
+
+  // Policies "all" and "none" with custom suffixes.
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalFnName("foo.custom.123",
+                                                       Suffixes, "all"));
+  EXPECT_EQ("foo.custom.123", FunctionSamples::getCanonicalFnName(
+                                  "foo.custom.123", Suffixes, "none"));
+}
+
+TEST(SampleProfCanonicalNameTest, FromLLVMFunction) {
+  LLVMContext Ctx;
+  auto M = std::make_unique<Module>("test", Ctx);
+  auto *FTy = FunctionType::get(Type::getVoidTy(Ctx), false);
+
+  // Without attribute, default policy is "" (same as "all").
+  auto *FDefault = Function::Create(FTy, GlobalValue::ExternalLinkage,
+                                    "f_default.llvm.1234", M.get());
+  EXPECT_EQ("f_default", FunctionSamples::getCanonicalFnName(*FDefault));
+
+  auto *FDefaultDot = Function::Create(FTy, GlobalValue::ExternalLinkage,
+                                       "f_default_dot.bar", M.get());
+  EXPECT_EQ("f_default_dot", FunctionSamples::getCanonicalFnName(*FDefaultDot));
+
+  // With attribute "selected".
+  auto *FSelectedLLVM = Function::Create(FTy, GlobalValue::ExternalLinkage,
+                                         "f_selected.llvm.1234", M.get());
+  FSelectedLLVM->addFnAttr("sample-profile-suffix-elision-policy", "selected");
+  EXPECT_EQ("f_selected", FunctionSamples::getCanonicalFnName(*FSelectedLLVM));
+
+  auto *FSelectedDot = Function::Create(FTy, GlobalValue::ExternalLinkage,
+                                        "f_selected_dot.bar", M.get());
+  FSelectedDot->addFnAttr("sample-profile-suffix-elision-policy", "selected");
+  EXPECT_EQ("f_selected_dot.bar",
+            FunctionSamples::getCanonicalFnName(*FSelectedDot));
+
+  // With attribute "none".
+  auto *FNone = Function::Create(FTy, GlobalValue::ExternalLinkage,
+                                 "f_none.llvm.1234", M.get());
+  FNone->addFnAttr("sample-profile-suffix-elision-policy", "none");
+  EXPECT_EQ("f_none.llvm.1234", FunctionSamples::getCanonicalFnName(*FNone));
+
+  // With attribute "all".
+  auto *FAll = Function::Create(FTy, GlobalValue::ExternalLinkage,
+                                "f_all.bar.1234", M.get());
+  FAll->addFnAttr("sample-profile-suffix-elision-policy", "all");
+  EXPECT_EQ("f_all", FunctionSamples::getCanonicalFnName(*FAll));
+}
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+TEST(SampleProfCanonicalNameTest, InvalidPolicyDeathTest) {
+  EXPECT_DEATH(FunctionSamples::getCanonicalFnName("foo", "invalid_policy"),
+               "unknown suffix elision policy");
+}
+#endif
+
 } // end anonymous namespace

>From 885ec39cd7a43f6f3e5fabd233cee32bbfcd1f92 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 1 Sep 2026 11:20:17 -0700
Subject: [PATCH 2/3] more tests

Created using spr 1.3.7
---
 llvm/unittests/ProfileData/SampleProfTest.cpp | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index 62623e5be3043..69597687fb79b 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -787,6 +787,12 @@ TEST(SampleProfCanonicalNameTest, CoroFnName) {
             FunctionSamples::getCanonicalCoroFnName("_Zfoo.llvm.1234.destroy"));
   EXPECT_EQ("_Zfoo",
             FunctionSamples::getCanonicalCoroFnName("_Zfoo.llvm.1234.resume"));
+  EXPECT_EQ("foo",
+            FunctionSamples::getCanonicalCoroFnName("foo.cleanupper"));
+  EXPECT_EQ("foo",
+            FunctionSamples::getCanonicalCoroFnName("foo.destroyer"));
+  EXPECT_EQ("foo",
+            FunctionSamples::getCanonicalCoroFnName("foo.resumed"));
   EXPECT_EQ("foo.other", FunctionSamples::getCanonicalCoroFnName("foo.other"));
 
   // Policy "none".

>From 747f90acf2039f5029e4a32e7dc8673f793392f7 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Tue, 1 Sep 2026 14:50:47 -0700
Subject: [PATCH 3/3] format

Created using spr 1.3.7
---
 llvm/unittests/ProfileData/SampleProfTest.cpp | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/llvm/unittests/ProfileData/SampleProfTest.cpp b/llvm/unittests/ProfileData/SampleProfTest.cpp
index ca8e44d2ce605..07dbf84042c1a 100644
--- a/llvm/unittests/ProfileData/SampleProfTest.cpp
+++ b/llvm/unittests/ProfileData/SampleProfTest.cpp
@@ -784,12 +784,9 @@ TEST(SampleProfCanonicalNameTest, CoroFnName) {
             FunctionSamples::getCanonicalCoroFnName("_Zfoo.llvm.1234.destroy"));
   EXPECT_EQ("_Zfoo",
             FunctionSamples::getCanonicalCoroFnName("_Zfoo.llvm.1234.resume"));
-  EXPECT_EQ("foo",
-            FunctionSamples::getCanonicalCoroFnName("foo.cleanupper"));
-  EXPECT_EQ("foo",
-            FunctionSamples::getCanonicalCoroFnName("foo.destroyer"));
-  EXPECT_EQ("foo",
-            FunctionSamples::getCanonicalCoroFnName("foo.resumed"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalCoroFnName("foo.cleanupper"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalCoroFnName("foo.destroyer"));
+  EXPECT_EQ("foo", FunctionSamples::getCanonicalCoroFnName("foo.resumed"));
   EXPECT_EQ("foo.other", FunctionSamples::getCanonicalCoroFnName("foo.other"));
 
   // Policy "none".



More information about the llvm-commits mailing list