[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
Wed Apr 8 13:21:45 PDT 2026
https://github.com/melver created https://github.com/llvm/llvm-project/pull/191048
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
>From 84b356ddbc10d55c98231accb71c3df5aabf3d69 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] [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
More information about the libcxx-commits
mailing list