[libcxx-commits] [libcxxabi] [llvm] [ItaniumDemangle] Strip __alloc_token_ to transparently demangle allocation functions (PR #191048)

Marco Elver via libcxx-commits libcxx-commits at lists.llvm.org
Thu Apr 9 13:37:14 PDT 2026


https://github.com/melver updated https://github.com/llvm/llvm-project/pull/191048

>From 72728f323e206125798c1bc2e7835cde6ef46ab7 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Wed, 8 Apr 2026 22:16:44 +0200
Subject: [PATCH 1/2] [ItaniumDemangle] Strip __alloc_token_ to transparently
 demangle allocation functions

Update the Itanium demangler to recognize and strip __alloc_token_
prefixes introduced by AllocToken instrumentation [1]. This ensures that
instrumented allocation functions (e.g., `__alloc_token__Znwm`) demangle
back to their original source-level names (e.g., `operator new(unsigned
long)`).

Since AllocToken is intended to be transparent to users who continue to
use operator new as before, the demangled name should reflect this
reality instead of confusing users with internal instrumentation names
in stack traces or symbolization output.

Synchronize changes across llvm and libcxxabi copies.

[1] https://clang.llvm.org/docs/AllocToken.html
---
 libcxxabi/src/demangle/ItaniumDemangle.h         |  8 ++++++++
 libcxxabi/test/DemangleTestCases.inc             | 11 +++++++++++
 llvm/include/llvm/Demangle/ItaniumDemangle.h     |  8 ++++++++
 .../llvm/Testing/Demangle/DemangleTestCases.inc  | 11 +++++++++++
 llvm/lib/Demangle/Demangle.cpp                   | 10 ++++++++++
 llvm/test/tools/llvm-cxxfilt/alloc-token.test    | 16 ++++++++++++++++
 6 files changed, 64 insertions(+)
 create mode 100644 llvm/test/tools/llvm-cxxfilt/alloc-token.test

diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index b999438ff2ca8..bbedfb9a72618 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -6157,8 +6157,16 @@ AbstractManglingParser<Derived, Alloc>::parseTemplateArgs(bool TagTemplates) {
 // extension      ::= ___Z <encoding> _block_invoke
 // extension      ::= ___Z <encoding> _block_invoke<decimal-digit>+
 // extension      ::= ___Z <encoding> _block_invoke_<decimal-digit>+
+// extension      ::= __alloc_token__Z <encoding>
+// extension      ::= __alloc_token_<decimal-digit>+__Z <encoding>
 template <typename Derived, typename Alloc>
 Node *AbstractManglingParser<Derived, Alloc>::parse(bool ParseParams) {
+  if (consumeIf("__alloc_token_")) {
+    const char *Saved = First;
+    if (parseNumber().empty() || !consumeIf('_'))
+      First = Saved;
+  }
+
   if (consumeIf("_Z") || consumeIf("__Z")) {
     Node *Encoding = getDerived().parseEncoding(ParseParams);
     if (Encoding == nullptr)
diff --git a/libcxxabi/test/DemangleTestCases.inc b/libcxxabi/test/DemangleTestCases.inc
index 2721d2aa5504e..1be08d16b18ef 100644
--- a/libcxxabi/test/DemangleTestCases.inc
+++ b/libcxxabi/test/DemangleTestCases.inc
@@ -30219,4 +30219,15 @@
 {"_Z3fooIPU9__ptrauthILj1ELb0ELj64EEPiEvT_", "void foo<int* __ptrauth<1u, false, 64u>*>(int* __ptrauth<1u, false, 64u>*)"},
 
 {"_ZN1CpmEi", "C::operator->*(int)"},
+
+// AllocToken instrumentation
+{"__alloc_token__Znwm", "operator new(unsigned long)"},
+{"__alloc_token__Znam", "operator new[](unsigned long)"},
+{"__alloc_token__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&)"},
+{"__alloc_token__ZnamRKSt9nothrow_t", "operator new[](unsigned long, std::nothrow_t const&)"},
+{"__alloc_token_0__Znwm", "operator new(unsigned long)"},
+{"__alloc_token_1__Znam", "operator new[](unsigned long)"},
+{"__alloc_token_123__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&)"},
+{"__alloc_token__Znwm.llvm.1234", "operator new(unsigned long) (.llvm.1234)"},
+{"__alloc_token_123__Z3foov", "foo()"},
     // clang-format on
diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index 67de123fdbad5..c0378ed88e237 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -6157,8 +6157,16 @@ AbstractManglingParser<Derived, Alloc>::parseTemplateArgs(bool TagTemplates) {
 // extension      ::= ___Z <encoding> _block_invoke
 // extension      ::= ___Z <encoding> _block_invoke<decimal-digit>+
 // extension      ::= ___Z <encoding> _block_invoke_<decimal-digit>+
+// extension      ::= __alloc_token__Z <encoding>
+// extension      ::= __alloc_token_<decimal-digit>+__Z <encoding>
 template <typename Derived, typename Alloc>
 Node *AbstractManglingParser<Derived, Alloc>::parse(bool ParseParams) {
+  if (consumeIf("__alloc_token_")) {
+    const char *Saved = First;
+    if (parseNumber().empty() || !consumeIf('_'))
+      First = Saved;
+  }
+
   if (consumeIf("_Z") || consumeIf("__Z")) {
     Node *Encoding = getDerived().parseEncoding(ParseParams);
     if (Encoding == nullptr)
diff --git a/llvm/include/llvm/Testing/Demangle/DemangleTestCases.inc b/llvm/include/llvm/Testing/Demangle/DemangleTestCases.inc
index 2721d2aa5504e..1be08d16b18ef 100644
--- a/llvm/include/llvm/Testing/Demangle/DemangleTestCases.inc
+++ b/llvm/include/llvm/Testing/Demangle/DemangleTestCases.inc
@@ -30219,4 +30219,15 @@
 {"_Z3fooIPU9__ptrauthILj1ELb0ELj64EEPiEvT_", "void foo<int* __ptrauth<1u, false, 64u>*>(int* __ptrauth<1u, false, 64u>*)"},
 
 {"_ZN1CpmEi", "C::operator->*(int)"},
+
+// AllocToken instrumentation
+{"__alloc_token__Znwm", "operator new(unsigned long)"},
+{"__alloc_token__Znam", "operator new[](unsigned long)"},
+{"__alloc_token__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&)"},
+{"__alloc_token__ZnamRKSt9nothrow_t", "operator new[](unsigned long, std::nothrow_t const&)"},
+{"__alloc_token_0__Znwm", "operator new(unsigned long)"},
+{"__alloc_token_1__Znam", "operator new[](unsigned long)"},
+{"__alloc_token_123__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&)"},
+{"__alloc_token__Znwm.llvm.1234", "operator new(unsigned long) (.llvm.1234)"},
+{"__alloc_token_123__Z3foov", "foo()"},
     // clang-format on
diff --git a/llvm/lib/Demangle/Demangle.cpp b/llvm/lib/Demangle/Demangle.cpp
index f0f7eacac98e6..f311caa46b0fb 100644
--- a/llvm/lib/Demangle/Demangle.cpp
+++ b/llvm/lib/Demangle/Demangle.cpp
@@ -12,6 +12,7 @@
 
 #include "llvm/Demangle/Demangle.h"
 #include "llvm/Demangle/StringViewExtras.h"
+#include <cctype>
 #include <cstdlib>
 #include <string_view>
 
@@ -38,6 +39,15 @@ std::string llvm::demangle(std::string_view MangledName) {
 }
 
 static bool isItaniumEncoding(std::string_view S) {
+  if (starts_with(S, "__alloc_token_")) {
+    S.remove_prefix(sizeof("__alloc_token_") - 1);
+    if (!S.empty() && std::isdigit(S[0])) {
+      while (!S.empty() && std::isdigit(S[0]))
+        S.remove_prefix(1);
+      if (starts_with(S, "_"))
+        S.remove_prefix(1);
+    }
+  }
   // Itanium demangler supports prefixes with 1-4 underscores.
   const size_t Pos = S.find_first_not_of('_');
   return Pos > 0 && Pos <= 4 && S[Pos] == 'Z';
diff --git a/llvm/test/tools/llvm-cxxfilt/alloc-token.test b/llvm/test/tools/llvm-cxxfilt/alloc-token.test
new file mode 100644
index 0000000000000..af0cc55f2c454
--- /dev/null
+++ b/llvm/test/tools/llvm-cxxfilt/alloc-token.test
@@ -0,0 +1,16 @@
+## Show that llvm-cxxfilt can handle __alloc_token_ prefixed demangling.
+
+RUN: llvm-cxxfilt __alloc_token__Znwm \
+RUN:              __alloc_token__Znam \
+RUN:              __alloc_token_0__Znwm \
+RUN:              __alloc_token_1__Znam \
+RUN:              __alloc_token_123__Z3foov \
+RUN:              __alloc_token_malloc \
+RUN:   | FileCheck %s
+
+CHECK: operator new(unsigned long)
+CHECK-NEXT: operator new[](unsigned long)
+CHECK-NEXT: operator new(unsigned long)
+CHECK-NEXT: operator new[](unsigned long)
+CHECK-NEXT: foo()
+CHECK-NEXT: __alloc_token_malloc

>From ea23e0339ccccd35a7211db068dd8bbcd3c5a27e Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Thu, 9 Apr 2026 22:33:37 +0200
Subject: [PATCH 2/2] fixup! [ItaniumDemangle] Strip __alloc_token_ to
 transparently demangle allocation functions

Append (.alloc-token) suffix
---
 libcxxabi/src/demangle/ItaniumDemangle.h       |  5 ++++-
 libcxxabi/test/DemangleTestCases.inc           | 18 +++++++++---------
 llvm/include/llvm/Demangle/ItaniumDemangle.h   |  5 ++++-
 .../Testing/Demangle/DemangleTestCases.inc     | 18 +++++++++---------
 llvm/test/tools/llvm-cxxfilt/alloc-token.test  | 10 +++++-----
 5 files changed, 31 insertions(+), 25 deletions(-)

diff --git a/libcxxabi/src/demangle/ItaniumDemangle.h b/libcxxabi/src/demangle/ItaniumDemangle.h
index bbedfb9a72618..c001a8d33f227 100644
--- a/libcxxabi/src/demangle/ItaniumDemangle.h
+++ b/libcxxabi/src/demangle/ItaniumDemangle.h
@@ -6161,7 +6161,8 @@ AbstractManglingParser<Derived, Alloc>::parseTemplateArgs(bool TagTemplates) {
 // extension      ::= __alloc_token_<decimal-digit>+__Z <encoding>
 template <typename Derived, typename Alloc>
 Node *AbstractManglingParser<Derived, Alloc>::parse(bool ParseParams) {
-  if (consumeIf("__alloc_token_")) {
+  bool AllocToken = consumeIf("__alloc_token_");
+  if (AllocToken) {
     const char *Saved = First;
     if (parseNumber().empty() || !consumeIf('_'))
       First = Saved;
@@ -6176,6 +6177,8 @@ Node *AbstractManglingParser<Derived, Alloc>::parse(bool ParseParams) {
           make<DotSuffix>(Encoding, std::string_view(First, Last - First));
       First = Last;
     }
+    if (AllocToken)
+      Encoding = make<DotSuffix>(Encoding, ".alloc_token");
     if (numLeft() != 0)
       return nullptr;
     return Encoding;
diff --git a/libcxxabi/test/DemangleTestCases.inc b/libcxxabi/test/DemangleTestCases.inc
index 1be08d16b18ef..307d0a8c02a41 100644
--- a/libcxxabi/test/DemangleTestCases.inc
+++ b/libcxxabi/test/DemangleTestCases.inc
@@ -30221,13 +30221,13 @@
 {"_ZN1CpmEi", "C::operator->*(int)"},
 
 // AllocToken instrumentation
-{"__alloc_token__Znwm", "operator new(unsigned long)"},
-{"__alloc_token__Znam", "operator new[](unsigned long)"},
-{"__alloc_token__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&)"},
-{"__alloc_token__ZnamRKSt9nothrow_t", "operator new[](unsigned long, std::nothrow_t const&)"},
-{"__alloc_token_0__Znwm", "operator new(unsigned long)"},
-{"__alloc_token_1__Znam", "operator new[](unsigned long)"},
-{"__alloc_token_123__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&)"},
-{"__alloc_token__Znwm.llvm.1234", "operator new(unsigned long) (.llvm.1234)"},
-{"__alloc_token_123__Z3foov", "foo()"},
+{"__alloc_token__Znwm", "operator new(unsigned long) (.alloc_token)"},
+{"__alloc_token__Znam", "operator new[](unsigned long) (.alloc_token)"},
+{"__alloc_token__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&) (.alloc_token)"},
+{"__alloc_token__ZnamRKSt9nothrow_t", "operator new[](unsigned long, std::nothrow_t const&) (.alloc_token)"},
+{"__alloc_token_0__Znwm", "operator new(unsigned long) (.alloc_token)"},
+{"__alloc_token_1__Znam", "operator new[](unsigned long) (.alloc_token)"},
+{"__alloc_token_123__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&) (.alloc_token)"},
+{"__alloc_token__Znwm.llvm.1234", "operator new(unsigned long) (.llvm.1234) (.alloc_token)"},
+{"__alloc_token_123__Z3foov", "foo() (.alloc_token)"},
     // clang-format on
diff --git a/llvm/include/llvm/Demangle/ItaniumDemangle.h b/llvm/include/llvm/Demangle/ItaniumDemangle.h
index c0378ed88e237..6d7f189a6e44c 100644
--- a/llvm/include/llvm/Demangle/ItaniumDemangle.h
+++ b/llvm/include/llvm/Demangle/ItaniumDemangle.h
@@ -6161,7 +6161,8 @@ AbstractManglingParser<Derived, Alloc>::parseTemplateArgs(bool TagTemplates) {
 // extension      ::= __alloc_token_<decimal-digit>+__Z <encoding>
 template <typename Derived, typename Alloc>
 Node *AbstractManglingParser<Derived, Alloc>::parse(bool ParseParams) {
-  if (consumeIf("__alloc_token_")) {
+  bool AllocToken = consumeIf("__alloc_token_");
+  if (AllocToken) {
     const char *Saved = First;
     if (parseNumber().empty() || !consumeIf('_'))
       First = Saved;
@@ -6176,6 +6177,8 @@ Node *AbstractManglingParser<Derived, Alloc>::parse(bool ParseParams) {
           make<DotSuffix>(Encoding, std::string_view(First, Last - First));
       First = Last;
     }
+    if (AllocToken)
+      Encoding = make<DotSuffix>(Encoding, ".alloc_token");
     if (numLeft() != 0)
       return nullptr;
     return Encoding;
diff --git a/llvm/include/llvm/Testing/Demangle/DemangleTestCases.inc b/llvm/include/llvm/Testing/Demangle/DemangleTestCases.inc
index 1be08d16b18ef..307d0a8c02a41 100644
--- a/llvm/include/llvm/Testing/Demangle/DemangleTestCases.inc
+++ b/llvm/include/llvm/Testing/Demangle/DemangleTestCases.inc
@@ -30221,13 +30221,13 @@
 {"_ZN1CpmEi", "C::operator->*(int)"},
 
 // AllocToken instrumentation
-{"__alloc_token__Znwm", "operator new(unsigned long)"},
-{"__alloc_token__Znam", "operator new[](unsigned long)"},
-{"__alloc_token__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&)"},
-{"__alloc_token__ZnamRKSt9nothrow_t", "operator new[](unsigned long, std::nothrow_t const&)"},
-{"__alloc_token_0__Znwm", "operator new(unsigned long)"},
-{"__alloc_token_1__Znam", "operator new[](unsigned long)"},
-{"__alloc_token_123__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&)"},
-{"__alloc_token__Znwm.llvm.1234", "operator new(unsigned long) (.llvm.1234)"},
-{"__alloc_token_123__Z3foov", "foo()"},
+{"__alloc_token__Znwm", "operator new(unsigned long) (.alloc_token)"},
+{"__alloc_token__Znam", "operator new[](unsigned long) (.alloc_token)"},
+{"__alloc_token__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&) (.alloc_token)"},
+{"__alloc_token__ZnamRKSt9nothrow_t", "operator new[](unsigned long, std::nothrow_t const&) (.alloc_token)"},
+{"__alloc_token_0__Znwm", "operator new(unsigned long) (.alloc_token)"},
+{"__alloc_token_1__Znam", "operator new[](unsigned long) (.alloc_token)"},
+{"__alloc_token_123__ZnwmRKSt9nothrow_t", "operator new(unsigned long, std::nothrow_t const&) (.alloc_token)"},
+{"__alloc_token__Znwm.llvm.1234", "operator new(unsigned long) (.llvm.1234) (.alloc_token)"},
+{"__alloc_token_123__Z3foov", "foo() (.alloc_token)"},
     // clang-format on
diff --git a/llvm/test/tools/llvm-cxxfilt/alloc-token.test b/llvm/test/tools/llvm-cxxfilt/alloc-token.test
index af0cc55f2c454..761aa727d4db2 100644
--- a/llvm/test/tools/llvm-cxxfilt/alloc-token.test
+++ b/llvm/test/tools/llvm-cxxfilt/alloc-token.test
@@ -8,9 +8,9 @@ RUN:              __alloc_token_123__Z3foov \
 RUN:              __alloc_token_malloc \
 RUN:   | FileCheck %s
 
-CHECK: operator new(unsigned long)
-CHECK-NEXT: operator new[](unsigned long)
-CHECK-NEXT: operator new(unsigned long)
-CHECK-NEXT: operator new[](unsigned long)
-CHECK-NEXT: foo()
+CHECK: operator new(unsigned long) (.alloc_token)
+CHECK-NEXT: operator new[](unsigned long) (.alloc_token)
+CHECK-NEXT: operator new(unsigned long) (.alloc_token)
+CHECK-NEXT: operator new[](unsigned long) (.alloc_token)
+CHECK-NEXT: foo() (.alloc_token)
 CHECK-NEXT: __alloc_token_malloc



More information about the libcxx-commits mailing list