[clang] [llvm] [AllocToken, Clang] Infer type hints from sizeof expressions and casts (PR #156841)

Marco Elver via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 8 23:57:23 PDT 2025


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

>From c7963578229c37b4de8d1ff920a8048e8b19d41d Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Thu, 4 Sep 2025 11:43:28 +0200
Subject: [PATCH 01/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/docs/AllocToken.rst                     | 177 ++++++
 clang/docs/ReleaseNotes.rst                   |   4 +
 clang/docs/UsersManual.rst                    |   2 +
 clang/include/clang/Basic/CodeGenOptions.def  |   2 +
 clang/include/clang/Basic/CodeGenOptions.h    |   3 +
 clang/include/clang/Basic/Sanitizers.def      |   3 +
 clang/include/clang/Driver/Options.td         |  17 +
 clang/include/clang/Driver/SanitizerArgs.h    |   4 +-
 clang/lib/CodeGen/BackendUtil.cpp             |  20 +
 clang/lib/CodeGen/CGExpr.cpp                  |  70 +++
 clang/lib/CodeGen/CGExprCXX.cpp               |  15 +-
 clang/lib/CodeGen/CodeGenFunction.cpp         |   2 +
 clang/lib/CodeGen/CodeGenFunction.h           |   3 +
 clang/lib/Driver/SanitizerArgs.cpp            |  31 +-
 clang/lib/Driver/ToolChains/BareMetal.cpp     |   1 +
 clang/lib/Driver/ToolChains/Clang.cpp         |   2 +
 clang/lib/Driver/ToolChains/Linux.cpp         |   1 +
 clang/lib/Frontend/CompilerInvocation.cpp     |  14 +
 clang/lib/Frontend/InitPreprocessor.cpp       |   2 +
 clang/test/CodeGen/alloc-token-ignorelist.c   |  27 +
 clang/test/CodeGen/alloc-token.c              |  45 ++
 clang/test/CodeGenCXX/alloc-token-pointer.cpp | 177 ++++++
 clang/test/CodeGenCXX/alloc-token.cpp         | 157 ++++++
 clang/test/Driver/fsanitize-alloc-token.c     |  43 ++
 clang/test/Preprocessor/alloc_token.cpp       |  10 +
 llvm/docs/LangRef.rst                         |   3 +
 llvm/docs/ReleaseNotes.md                     |   4 +
 llvm/include/llvm/Bitcode/LLVMBitCodes.h      |   1 +
 llvm/include/llvm/IR/Attributes.td            |   3 +
 .../Transforms/Instrumentation/AllocToken.h   |  46 ++
 llvm/lib/Bitcode/Reader/BitcodeReader.cpp     |   2 +
 llvm/lib/Bitcode/Writer/BitcodeWriter.cpp     |   2 +
 llvm/lib/Passes/PassBuilder.cpp               |   1 +
 llvm/lib/Passes/PassRegistry.def              |   1 +
 .../Transforms/Instrumentation/AllocToken.cpp | 529 ++++++++++++++++++
 .../Transforms/Instrumentation/CMakeLists.txt |   1 +
 llvm/lib/Transforms/Utils/CodeExtractor.cpp   |   1 +
 llvm/test/Bitcode/attributes.ll               |   6 +
 llvm/test/Bitcode/compatibility.ll            |   8 +-
 llvm/test/Instrumentation/AllocToken/basic.ll |  84 +++
 .../AllocToken/extralibfuncs.ll               |  32 ++
 llvm/test/Instrumentation/AllocToken/fast.ll  |  39 ++
 .../test/Instrumentation/AllocToken/ignore.ll |  30 +
 .../test/Instrumentation/AllocToken/invoke.ll |  86 +++
 .../Instrumentation/AllocToken/nonlibcalls.ll |  63 +++
 .../test/Instrumentation/AllocToken/remark.ll |  27 +
 llvm/test/Transforms/Inline/attributes.ll     |  42 ++
 llvm/utils/emacs/llvm-mode.el                 |   2 +-
 .../lib/Transforms/Instrumentation/BUILD.gn   |   1 +
 llvm/utils/llvm.grm                           |   1 +
 llvm/utils/vim/syntax/llvm.vim                |   1 +
 .../vscode/llvm/syntaxes/ll.tmLanguage.yaml   |   1 +
 52 files changed, 1836 insertions(+), 13 deletions(-)
 create mode 100644 clang/docs/AllocToken.rst
 create mode 100644 clang/test/CodeGen/alloc-token-ignorelist.c
 create mode 100644 clang/test/CodeGen/alloc-token.c
 create mode 100644 clang/test/CodeGenCXX/alloc-token-pointer.cpp
 create mode 100644 clang/test/CodeGenCXX/alloc-token.cpp
 create mode 100644 clang/test/Driver/fsanitize-alloc-token.c
 create mode 100644 clang/test/Preprocessor/alloc_token.cpp
 create mode 100644 llvm/include/llvm/Transforms/Instrumentation/AllocToken.h
 create mode 100644 llvm/lib/Transforms/Instrumentation/AllocToken.cpp
 create mode 100644 llvm/test/Instrumentation/AllocToken/basic.ll
 create mode 100644 llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
 create mode 100644 llvm/test/Instrumentation/AllocToken/fast.ll
 create mode 100644 llvm/test/Instrumentation/AllocToken/ignore.ll
 create mode 100644 llvm/test/Instrumentation/AllocToken/invoke.ll
 create mode 100644 llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
 create mode 100644 llvm/test/Instrumentation/AllocToken/remark.ll

diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst
new file mode 100644
index 0000000000000..fb354d6738ea3
--- /dev/null
+++ b/clang/docs/AllocToken.rst
@@ -0,0 +1,177 @@
+=================
+Allocation Tokens
+=================
+
+.. contents::
+   :local:
+
+Introduction
+============
+
+Clang provides support for allocation tokens to enable allocator-level heap
+organization strategies. Clang assigns mode-dependent token IDs to allocation
+calls; the runtime behavior depends entirely on the implementation of a
+compatible memory allocator.
+
+Possible allocator strategies include:
+
+* **Security Hardening**: Placing allocations into separate, isolated heap
+  partitions. For example, separating pointer-containing types from raw data
+  can mitigate exploits that rely on overflowing a primitive buffer to corrupt
+  object metadata.
+
+* **Memory Layout Optimization**: Grouping related allocations to improve data
+  locality and cache utilization.
+
+* **Custom Allocation Policies**: Applying different management strategies to
+  different partitions.
+
+Token Assignment Mode
+=====================
+
+The default mode to calculate tokens is:
+
+* *TypeHashPointerSplit* (mode=3): This mode assigns a token ID based on
+  the hash of the allocated type's name, where the top half ID-space is
+  reserved for types that contain pointers and the bottom half for types that
+  do not contain pointers.
+
+Other token ID assignment modes are supported, but they may be subject to
+change or removal. These may (experimentally) be selected with ``-mllvm
+-alloc-token-mode=<mode>``:
+
+* *TypeHash* (mode=2): This mode assigns a token ID based on the hash of
+  the allocated type's name.
+
+* *Random* (mode=1): This mode assigns a statically-determined random token ID
+  to each allocation site.
+
+* *Increment* (mode=0): This mode assigns a simple, incrementally increasing
+  token ID to each allocation site.
+
+Allocation Token Instrumentation
+================================
+
+To enable instrumentation of allocation functions, code can be compiled with
+the ``-fsanitize=alloc-token`` flag:
+
+.. code-block:: console
+
+    % clang++ -fsanitize=alloc-token example.cc
+
+The instrumentation transforms allocation calls to include a token ID. For
+example:
+
+.. code-block:: c
+
+    // Original:
+    ptr = malloc(size);
+
+    // Instrumented:
+    ptr = __alloc_token_malloc(size, token_id);
+
+In addition, it is typically recommended to configure the following:
+
+* ``-falloc-token-max=<N>``
+    Configures the maximum number of tokens. No max by default (tokens bounded
+    by ``UINT64_MAX``).
+
+    .. code-block:: console
+
+        % clang++ -fsanitize=alloc-token -falloc-token-max=512 example.cc
+
+Runtime Interface
+-----------------
+
+A compatible runtime must be provided that implements the token-enabled
+allocation functions. The instrumentation generates calls to functions that
+take a final ``uint64_t token_id`` argument.
+
+.. code-block:: c
+
+    // C standard library functions
+    void *__alloc_token_malloc(size_t size, uint64_t token_id);
+    void *__alloc_token_calloc(size_t count, size_t size, uint64_t token_id);
+    void *__alloc_token_realloc(void *ptr, size_t size, uint64_t token_id);
+    // ...
+
+    // C++ operators (mangled names)
+    // operator new(size_t, uint64_t)
+    void *__alloc_token_Znwm(size_t size, uint64_t token_id);
+    // operator new[](size_t, uint64_t)
+    void *__alloc_token_Znam(size_t size, uint64_t token_id);
+    // ... other variants like nothrow, etc., are also instrumented.
+
+Fast ABI
+--------
+
+An alternative ABI can be enabled with ``-fsanitize-alloc-token-fast-abi``,
+which encodes the token ID hint in the allocation function name.
+
+.. code-block:: c
+
+    void *__alloc_token_0_malloc(size_t size);
+    void *__alloc_token_1_malloc(size_t size);
+    void *__alloc_token_2_malloc(size_t size);
+    ...
+    void *__alloc_token_0_Znwm(size_t size);
+    void *__alloc_token_1_Znwm(size_t size);
+    void *__alloc_token_2_Znwm(size_t size);
+    ...
+
+This ABI provides a more efficient alternative where
+``-falloc-token-max`` is small.
+
+Disabling Instrumentation
+-------------------------
+
+To exclude specific functions from instrumentation, you can use the
+``no_sanitize("alloc-token")`` attribute:
+
+.. code-block:: c
+
+    __attribute__((no_sanitize("alloc-token")))
+    void* custom_allocator(size_t size) {
+        return malloc(size);  // Uses original malloc
+    }
+
+Note: Independent of any given allocator support, the instrumentation aims to
+remain performance neutral. As such, ``no_sanitize("alloc-token")``
+functions may be inlined into instrumented functions and vice-versa. If
+correctness is affected, such functions should explicitly be marked
+``noinline``.
+
+The ``__attribute__((disable_sanitizer_instrumentation))`` is also supported to
+disable this and other sanitizer instrumentations.
+
+Suppressions File (Ignorelist)
+------------------------------
+
+AllocToken respects the ``src`` and ``fun`` entity types in the
+:doc:`SanitizerSpecialCaseList`, which can be used to omit specified source
+files or functions from instrumentation.
+
+.. code-block:: bash
+
+    # Exclude specific source files
+    src:third_party/allocator.c
+    # Exclude function name patterns
+    fun:*custom_malloc*
+    fun:LowLevel::*
+
+.. code-block:: console
+
+    % clang++ -fsanitize=alloc-token -fsanitize-ignorelist=my_ignorelist.txt example.cc
+
+Conditional Compilation with ``__SANITIZE_ALLOC_TOKEN__``
+-----------------------------------------------------------
+
+In some cases, one may need to execute different code depending on whether
+AllocToken instrumentation is enabled. The ``__SANITIZE_ALLOC_TOKEN__`` macro
+can be used for this purpose.
+
+.. code-block:: c
+
+    #ifdef __SANITIZE_ALLOC_TOKEN__
+    // Code specific to -fsanitize=alloc-token builds
+    #endif
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index dd53b4d46f3cc..193b356631995 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -203,11 +203,15 @@ Non-comprehensive list of changes in this release
   Currently, the use of ``__builtin_dedup_pack`` is limited to template arguments and base
   specifiers, it also must be used within a template context.
 
+- Introduce support for allocation tokens to enable allocator-level heap
+  organization strategies. A feature to instrument all allocation functions
+  with a token ID can be enabled via the ``-fsanitize=alloc-token`` flag.
 
 New Compiler Flags
 ------------------
 - New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
 - New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
+- New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``.
 
 
 Lanai Support
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index 0e85c8109fd5e..f1bd348e4e22d 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -2194,6 +2194,8 @@ are listed below.
       protection against stack-based memory corruption errors.
    -  ``-fsanitize=realtime``: :doc:`RealtimeSanitizer`,
       a real-time safety checker.
+   -  ``-fsanitize=alloc-token``: :doc:`AllocToken`,
+      allocation token instrumentation (requires compatible allocator).
 
    There are more fine-grained checks available: see
    the :ref:`list <ubsan-checks>` of specific kinds of
diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def
index fda0da99b60c0..b54876fdf29dd 100644
--- a/clang/include/clang/Basic/CodeGenOptions.def
+++ b/clang/include/clang/Basic/CodeGenOptions.def
@@ -306,6 +306,8 @@ CODEGENOPT(SanitizeBinaryMetadataCovered, 1, 0, Benign) ///< Emit PCs for covere
 CODEGENOPT(SanitizeBinaryMetadataAtomics, 1, 0, Benign) ///< Emit PCs for atomic operations.
 CODEGENOPT(SanitizeBinaryMetadataUAR, 1, 0, Benign) ///< Emit PCs for start of functions
                                                     ///< that are subject for use-after-return checking.
+CODEGENOPT(SanitizeAllocTokenFastABI, 1, 0, Benign) ///< Use the AllocToken fast ABI.
+CODEGENOPT(SanitizeAllocTokenExtended, 1, 0, Benign) ///< Extend coverage to custom allocation functions.
 CODEGENOPT(SanitizeStats     , 1, 0, Benign) ///< Collect statistics for sanitizers.
 ENUM_CODEGENOPT(SanitizeDebugTrapReasons, SanitizeDebugTrapReasonKind, 2, SanitizeDebugTrapReasonKind::Detailed, Benign) ///< Control how "trap reasons" are emitted in debug info
 CODEGENOPT(SimplifyLibCalls  , 1, 1, Benign) ///< Set when -fbuiltin is enabled.
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 5d5cf250b56b9..eb283a27ed95a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -447,6 +447,9 @@ class CodeGenOptions : public CodeGenOptionsBase {
 
   std::optional<double> AllowRuntimeCheckSkipHotCutoff;
 
+  /// Maximum number of allocation tokens (0 = no max).
+  std::optional<uint64_t> AllocTokenMax;
+
   /// List of backend command-line options for -fembed-bitcode.
   std::vector<uint8_t> CmdArgs;
 
diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def
index 1d0e97cc7fb4c..da85431625026 100644
--- a/clang/include/clang/Basic/Sanitizers.def
+++ b/clang/include/clang/Basic/Sanitizers.def
@@ -195,6 +195,9 @@ SANITIZER_GROUP("bounds", Bounds, ArrayBounds | LocalBounds)
 // Scudo hardened allocator
 SANITIZER("scudo", Scudo)
 
+// AllocToken
+SANITIZER("alloc-token", AllocToken)
+
 // Magic group, containing all sanitizers. For example, "-fno-sanitize=all"
 // can be used to disable all the sanitizers.
 SANITIZER_GROUP("all", All, ~SanitizerMask())
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 49e917a6a0786..064d55d14dcc0 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2730,8 +2730,25 @@ def fsanitize_skip_hot_cutoff_EQ
           "(0.0 [default] = skip none; 1.0 = skip all). "
           "Argument format: <sanitizer1>=<value1>,<sanitizer2>=<value2>,...">;
 
+defm sanitize_alloc_token_fast_abi : BoolOption<"f", "sanitize-alloc-token-fast-abi",
+  CodeGenOpts<"SanitizeAllocTokenFastABI">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption], "Use the AllocToken fast ABI">,
+  NegFlag<SetFalse, [], [ClangOption], "Use the default AllocToken ABI">>,
+  Group<f_clang_Group>;
+defm sanitize_alloc_token_extended : BoolOption<"f", "sanitize-alloc-token-extended",
+  CodeGenOpts<"SanitizeAllocTokenExtended">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption], "Enable">,
+  NegFlag<SetFalse, [], [ClangOption], "Disable">,
+  BothFlags<[], [ClangOption], " extended coverage to custom allocation functions">>,
+  Group<f_clang_Group>;
+
 } // end -f[no-]sanitize* flags
 
+def falloc_token_max_EQ : Joined<["-"], "falloc-token-max=">,
+  Group<f_Group>, Visibility<[ClangOption, CC1Option, CLOption]>,
+  MetaVarName<"<N>">,
+  HelpText<"Limit to maximum N allocation tokens (0 = no max)">;
+
 def fallow_runtime_check_skip_hot_cutoff_EQ
     : Joined<["-"], "fallow-runtime-check-skip-hot-cutoff=">,
       Group<f_clang_Group>,
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 2b72268c8606c..ed51009654d4e 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -13,6 +13,7 @@
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h"
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -73,8 +74,9 @@ class SanitizerArgs {
   bool HwasanUseAliases = false;
   llvm::AsanDetectStackUseAfterReturnMode AsanUseAfterReturn =
       llvm::AsanDetectStackUseAfterReturnMode::Invalid;
-
   std::string MemtagMode;
+  bool AllocTokenFastABI = false;
+  bool AllocTokenExtended = false;
 
 public:
   /// Parses the sanitizer arguments from an argument list.
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 3f095c03397fd..8b297134de4e7 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -59,11 +59,13 @@
 #include "llvm/TargetParser/Triple.h"
 #include "llvm/Transforms/HipStdPar/HipStdPar.h"
 #include "llvm/Transforms/IPO/EmbedBitcodePass.h"
+#include "llvm/Transforms/IPO/InferFunctionAttrs.h"
 #include "llvm/Transforms/IPO/LowerTypeTests.h"
 #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h"
 #include "llvm/Transforms/InstCombine/InstCombine.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h"
+#include "llvm/Transforms/Instrumentation/AllocToken.h"
 #include "llvm/Transforms/Instrumentation/BoundsChecking.h"
 #include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h"
 #include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
@@ -231,6 +233,14 @@ class EmitAssemblyHelper {
 };
 } // namespace
 
+static AllocTokenOptions getAllocTokenOptions(const CodeGenOptions &CGOpts) {
+  AllocTokenOptions Opts;
+  Opts.MaxTokens = CGOpts.AllocTokenMax;
+  Opts.Extended = CGOpts.SanitizeAllocTokenExtended;
+  Opts.FastABI = CGOpts.SanitizeAllocTokenFastABI;
+  return Opts;
+}
+
 static SanitizerCoverageOptions
 getSancovOptsFromCGOpts(const CodeGenOptions &CGOpts) {
   SanitizerCoverageOptions Opts;
@@ -784,6 +794,16 @@ static void addSanitizers(const Triple &TargetTriple,
     if (LangOpts.Sanitize.has(SanitizerKind::DataFlow)) {
       MPM.addPass(DataFlowSanitizerPass(LangOpts.NoSanitizeFiles));
     }
+
+    if (LangOpts.Sanitize.has(SanitizerKind::AllocToken)) {
+      if (Level == OptimizationLevel::O0) {
+        // The default pass builder only infers libcall function attrs when
+        // optimizing, so we insert it here because we need it for accurate
+        // memory allocation function detection.
+        MPM.addPass(InferFunctionAttrsPass());
+      }
+      MPM.addPass(AllocTokenPass(getAllocTokenOptions(CodeGenOpts)));
+    }
   };
   if (ClSanitizeOnOptimizerEarlyEP) {
     PB.registerOptimizerEarlyEPCallback(
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 26fba751e6f9d..3edc1e898114f 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1272,6 +1272,76 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound,
   EmitCheck(std::make_pair(Check, CheckKind), CheckHandler, StaticData, Index);
 }
 
+void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
+                                         QualType AllocType) {
+  assert(SanOpts.has(SanitizerKind::AllocToken) &&
+         "Only needed with -fsanitize=alloc-token");
+
+  llvm::MDBuilder MDB(getLLVMContext());
+
+  // Get unique type name.
+  PrintingPolicy Policy(CGM.getContext().getLangOpts());
+  Policy.SuppressTagKeyword = true;
+  Policy.FullyQualifiedName = true;
+  std::string TypeName = AllocType.getCanonicalType().getAsString(Policy);
+  auto *TypeNameMD = MDB.createString(TypeName);
+
+  // Check if QualType contains a pointer. Implements a simple DFS to
+  // recursively check if a type contains a pointer type.
+  llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD;
+  auto TypeContainsPtr = [&](auto &&self, QualType T) -> bool {
+    QualType CanonicalType = T.getCanonicalType();
+    if (CanonicalType->isPointerType())
+      return true; // base case
+
+    // Look through typedef chain to check for special types.
+    for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>();
+         CurrentT = TT->getDecl()->getUnderlyingType()) {
+      const IdentifierInfo *II = TT->getDecl()->getIdentifier();
+      if (!II)
+        continue;
+      // Special Case: Syntactically uintptr_t is not a pointer; semantically,
+      // however, very likely used as such. Therefore, classify uintptr_t as a
+      // pointer, too.
+      if (II->isStr("uintptr_t"))
+        return true;
+    }
+
+    // The type is an array; check the element type.
+    if (const ArrayType *AT = CanonicalType->getAsArrayTypeUnsafe())
+      return self(self, AT->getElementType());
+    // The type is a struct, class, or union.
+    if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) {
+      if (!VisitedRD.insert(RD).second)
+        return false; // already visited
+      // Check all fields.
+      for (const FieldDecl *Field : RD->fields()) {
+        if (self(self, Field->getType()))
+          return true;
+      }
+      // For C++ classes, also check base classes.
+      if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+        // Polymorphic types require a vptr.
+        if (CXXRD->isPolymorphic())
+          return true;
+        for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
+          if (self(self, Base.getType()))
+            return true;
+        }
+      }
+    }
+    return false;
+  };
+  const bool ContainsPtr = TypeContainsPtr(TypeContainsPtr, AllocType);
+  auto *ContainsPtrC = Builder.getInt1(ContainsPtr);
+  auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC);
+
+  // Format: !{<type-name>, <contains-pointer>}
+  auto *MDN =
+      llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD});
+  CB->setMetadata("alloc_token_hint", MDN);
+}
+
 CodeGenFunction::ComplexPairTy CodeGenFunction::
 EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV,
                          bool isInc, bool isPre) {
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 1e4c72a210f9a..6bf3332b425fa 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1707,11 +1707,16 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
     RValue RV =
       EmitNewDeleteCall(*this, allocator, allocatorType, allocatorArgs);
 
-    // Set !heapallocsite metadata on the call to operator new.
-    if (getDebugInfo())
-      if (auto *newCall = dyn_cast<llvm::CallBase>(RV.getScalarVal()))
-        getDebugInfo()->addHeapAllocSiteMetadata(newCall, allocType,
-                                                 E->getExprLoc());
+    if (auto *newCall = dyn_cast<llvm::CallBase>(RV.getScalarVal())) {
+      if (auto *CGDI = getDebugInfo()) {
+        // Set !heapallocsite metadata on the call to operator new.
+        CGDI->addHeapAllocSiteMetadata(newCall, allocType, E->getExprLoc());
+      }
+      if (SanOpts.has(SanitizerKind::AllocToken)) {
+        // Set !alloc_token_hint metadata.
+        EmitAllocTokenHint(newCall, allocType);
+      }
+    }
 
     // If this was a call to a global replaceable allocation function that does
     // not take an alignment argument, the allocator is known to produce
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index b2fe9171372d8..acf8de4dee147 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -846,6 +846,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
       Fn->addFnAttr(llvm::Attribute::SanitizeNumericalStability);
     if (SanOpts.hasOneOf(SanitizerKind::Memory | SanitizerKind::KernelMemory))
       Fn->addFnAttr(llvm::Attribute::SanitizeMemory);
+    if (SanOpts.has(SanitizerKind::AllocToken))
+      Fn->addFnAttr(llvm::Attribute::SanitizeAllocToken);
   }
   if (SanOpts.has(SanitizerKind::SafeStack))
     Fn->addFnAttr(llvm::Attribute::SafeStack);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 123cb4f51f828..fd7ec36183c2d 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3348,6 +3348,9 @@ class CodeGenFunction : public CodeGenTypeCache {
   SanitizerAnnotateDebugInfo(ArrayRef<SanitizerKind::SanitizerOrdinal> Ordinals,
                              SanitizerHandler Handler);
 
+  /// Emit additional metadata used by the AllocToken instrumentation.
+  void EmitAllocTokenHint(llvm::CallBase *CB, QualType AllocType);
+
   llvm::Value *GetCountedByFieldExprGEP(const Expr *Base, const FieldDecl *FD,
                                         const FieldDecl *CountDecl);
 
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 7ce1afe6f2e6a..5dd48f53b9069 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -61,8 +61,9 @@ static const SanitizerMask RecoverableByDefault =
     SanitizerKind::ImplicitConversion | SanitizerKind::Nullability |
     SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast |
     SanitizerKind::Vptr;
-static const SanitizerMask Unrecoverable =
-    SanitizerKind::Unreachable | SanitizerKind::Return;
+static const SanitizerMask Unrecoverable = SanitizerKind::Unreachable |
+                                           SanitizerKind::Return |
+                                           SanitizerKind::AllocToken;
 static const SanitizerMask AlwaysRecoverable = SanitizerKind::KernelAddress |
                                                SanitizerKind::KernelHWAddress |
                                                SanitizerKind::KCFI;
@@ -84,7 +85,8 @@ static const SanitizerMask CFIClasses =
 static const SanitizerMask CompatibleWithMinimalRuntime =
     TrappingSupported | SanitizerKind::Scudo | SanitizerKind::ShadowCallStack |
     SanitizerKind::MemtagStack | SanitizerKind::MemtagHeap |
-    SanitizerKind::MemtagGlobals | SanitizerKind::KCFI;
+    SanitizerKind::MemtagGlobals | SanitizerKind::KCFI |
+    SanitizerKind::AllocToken;
 
 enum CoverageFeature {
   CoverageFunc = 1 << 0,
@@ -203,6 +205,7 @@ static void addDefaultIgnorelists(const Driver &D, SanitizerMask Kinds,
                      {"tysan_blacklist.txt", SanitizerKind::Type},
                      {"dfsan_abilist.txt", SanitizerKind::DataFlow},
                      {"cfi_ignorelist.txt", SanitizerKind::CFI},
+                     {"alloc_token_ignorelist.txt", SanitizerKind::AllocToken},
                      {"ubsan_ignorelist.txt",
                       SanitizerKind::Undefined | SanitizerKind::Vptr |
                           SanitizerKind::Integer | SanitizerKind::Nullability |
@@ -650,7 +653,12 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
       std::make_pair(SanitizerKind::KCFI, SanitizerKind::Function),
       std::make_pair(SanitizerKind::Realtime,
                      SanitizerKind::Address | SanitizerKind::Thread |
-                         SanitizerKind::Undefined | SanitizerKind::Memory)};
+                         SanitizerKind::Undefined | SanitizerKind::Memory),
+      std::make_pair(SanitizerKind::AllocToken,
+                     SanitizerKind::Address | SanitizerKind::HWAddress |
+                         SanitizerKind::KernelAddress |
+                         SanitizerKind::KernelHWAddress |
+                         SanitizerKind::Memory)};
 
   // Enable toolchain specific default sanitizers if not explicitly disabled.
   SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove;
@@ -1159,6 +1167,15 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
         !TC.getTriple().isAndroid() && !TC.getTriple().isOSFuchsia();
   }
 
+  if (AllAddedKinds & SanitizerKind::AllocToken) {
+    AllocTokenFastABI = Args.hasFlag(
+        options::OPT_fsanitize_alloc_token_fast_abi,
+        options::OPT_fno_sanitize_alloc_token_fast_abi, AllocTokenFastABI);
+    AllocTokenExtended = Args.hasFlag(
+        options::OPT_fsanitize_alloc_token_extended,
+        options::OPT_fno_sanitize_alloc_token_extended, AllocTokenExtended);
+  }
+
   LinkRuntimes = Args.hasFlag(options::OPT_fsanitize_link_runtime,
                               options::OPT_fno_sanitize_link_runtime,
                               !Args.hasArg(options::OPT_r));
@@ -1527,6 +1544,12 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
       Sanitizers.has(SanitizerKind::Address))
     CmdArgs.push_back("-fno-assume-sane-operator-new");
 
+  // Flags for -fsanitize=alloc-token.
+  if (AllocTokenFastABI)
+    CmdArgs.push_back("-fsanitize-alloc-token-fast-abi");
+  if (AllocTokenExtended)
+    CmdArgs.push_back("-fsanitize-alloc-token-extended");
+
   // libFuzzer wants to intercept calls to certain library functions, so the
   // following -fno-builtin-* flags force the compiler to emit interposable
   // libcalls to these functions. Other sanitizers effectively do the same thing
diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp
index 9b7f58c392885..3319cdd93b5e2 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.cpp
+++ b/clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -726,6 +726,7 @@ SanitizerMask BareMetal::getSupportedSanitizers() const {
   Res |= SanitizerKind::SafeStack;
   Res |= SanitizerKind::Thread;
   Res |= SanitizerKind::Scudo;
+  Res |= SanitizerKind::AllocToken;
   if (IsX86_64 || IsAArch64 || IsRISCV64) {
     Res |= SanitizerKind::HWAddress;
     Res |= SanitizerKind::KernelHWAddress;
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 21e45c6b56bbb..94b9f1ce3e260 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7663,6 +7663,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
   // features enabled through -Xclang -target-feature flags.
   SanitizeArgs.addArgs(TC, Args, CmdArgs, InputType);
 
+  Args.AddLastArg(CmdArgs, options::OPT_falloc_token_max_EQ);
+
 #if CLANG_ENABLE_CIR
   // Forward -mmlir arguments to to the MLIR option parser.
   for (const Arg *A : Args.filtered(options::OPT_mmlir)) {
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
index 16e35b08cfbd6..76070b063678f 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -819,6 +819,7 @@ SanitizerMask Linux::getSupportedSanitizers() const {
   Res |= SanitizerKind::KernelAddress;
   Res |= SanitizerKind::Vptr;
   Res |= SanitizerKind::SafeStack;
+  Res |= SanitizerKind::AllocToken;
   if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64)
     Res |= SanitizerKind::DataFlow;
   if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64 ||
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 8411d00cc7812..eab2da4ddcc98 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1857,6 +1857,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
        serializeSanitizerKinds(Opts.SanitizeAnnotateDebugInfo))
     GenerateArg(Consumer, OPT_fsanitize_annotate_debug_info_EQ, Sanitizer);
 
+  if (Opts.AllocTokenMax)
+    GenerateArg(Consumer, OPT_falloc_token_max_EQ,
+                std::to_string(*Opts.AllocTokenMax));
+
   if (!Opts.EmitVersionIdentMetadata)
     GenerateArg(Consumer, OPT_Qn);
 
@@ -2367,6 +2371,16 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
     }
   }
 
+  if (const auto *Arg = Args.getLastArg(options::OPT_falloc_token_max_EQ)) {
+    StringRef S = Arg->getValue();
+    uint64_t Value = 0;
+    if (S.getAsInteger(0, Value)) {
+      Diags.Report(diag::err_drv_invalid_value) << Arg->getAsString(Args) << S;
+    } else {
+      Opts.AllocTokenMax = Value;
+    }
+  }
+
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
 
   if (!LangOpts->CUDAIsDevice)
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 4865c0b889044..28a173f9fe426 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1525,6 +1525,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
     Builder.defineMacro("__SANITIZE_HWADDRESS__");
   if (LangOpts.Sanitize.has(SanitizerKind::Thread))
     Builder.defineMacro("__SANITIZE_THREAD__");
+  if (LangOpts.Sanitize.has(SanitizerKind::AllocToken))
+    Builder.defineMacro("__SANITIZE_ALLOC_TOKEN__");
 
   // Target OS macro definitions.
   if (PPOpts.DefineTargetOSMacros) {
diff --git a/clang/test/CodeGen/alloc-token-ignorelist.c b/clang/test/CodeGen/alloc-token-ignorelist.c
new file mode 100644
index 0000000000000..f6f82fe3b83e6
--- /dev/null
+++ b/clang/test/CodeGen/alloc-token-ignorelist.c
@@ -0,0 +1,27 @@
+// Test AllocToken respects ignorelist for functions and files.
+//
+// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ALLOW
+//
+// RUN: echo "fun:excluded_by_all" > %t.func.ignorelist
+// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.func.ignorelist -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-FUN
+//
+// RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t.file.ignorelist
+// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.file.ignorelist -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-SRC
+
+extern void* malloc(unsigned long size);
+
+// CHECK-LABEL: define{{.*}} @excluded_by_all(
+void* excluded_by_all(unsigned long size) {
+    // CHECK-ALLOW: call ptr @__alloc_token_malloc(
+    // CHECK-FUN: call ptr @malloc(
+    // CHECK-SRC: call ptr @malloc(
+    return malloc(size);
+}
+
+// CHECK-LABEL: define{{.*}} @excluded_by_src(
+void* excluded_by_src(unsigned long size) {
+    // CHECK-ALLOW: call ptr @__alloc_token_malloc(
+    // CHECK-FUN: call ptr @__alloc_token_malloc(
+    // CHECK-SRC: call ptr @malloc(
+    return malloc(size);
+}
diff --git a/clang/test/CodeGen/alloc-token.c b/clang/test/CodeGen/alloc-token.c
new file mode 100644
index 0000000000000..de9b3f48c995f
--- /dev/null
+++ b/clang/test/CodeGen/alloc-token.c
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1    -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s
+
+typedef __typeof(sizeof(int)) size_t;
+
+void *aligned_alloc(size_t alignment, size_t size);
+void *malloc(size_t size);
+void *calloc(size_t num, size_t size);
+void *realloc(void *ptr, size_t size);
+void *reallocarray(void *ptr, size_t nmemb, size_t size);
+void *memalign(size_t alignment, size_t size);
+void *valloc(size_t size);
+void *pvalloc(size_t size);
+int posix_memalign(void **memptr, size_t alignment, size_t size);
+
+void *sink;
+
+// CHECK-LABEL: @test_malloc_like(
+void test_malloc_like() {
+  // FIXME: Should not be token ID 0! Currently fail to infer the type.
+  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0)
+  sink = malloc(sizeof(int));
+  // CHECK: call{{.*}} ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0)
+  sink = calloc(3, sizeof(int));
+  // CHECK: call{{.*}} ptr @__alloc_token_realloc(ptr noundef {{[^,]*}}, i64 noundef 8, i64 0)
+  sink = realloc(sink, sizeof(long));
+  // CHECK: call{{.*}} ptr @__alloc_token_reallocarray(ptr noundef {{[^,]*}}, i64 noundef 5, i64 noundef 8, i64 0)
+  sink = reallocarray(sink, 5, sizeof(long));
+  // CHECK: call{{.*}} i32 @__alloc_token_posix_memalign(ptr noundef {{[^,]*}}, i64 noundef 64, i64 noundef 4, i64 0)
+  posix_memalign(&sink, 64, sizeof(int));
+  // CHECK: call align 128{{.*}} ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0)
+  sink = aligned_alloc(128, 1024);
+  // CHECK: call align 16{{.*}} ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0)
+  sink = memalign(16, 256);
+  // CHECK: call{{.*}} ptr @__alloc_token_valloc(i64 noundef 4096, i64 0)
+  sink = valloc(4096);
+  // CHECK: call{{.*}} ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0)
+  sink = pvalloc(8192);
+}
+
+// CHECK-LABEL: @no_sanitize_malloc(
+void *no_sanitize_malloc(size_t size) __attribute__((no_sanitize("alloc-token"))) {
+  // CHECK: call ptr @malloc(
+  return malloc(size);
+}
diff --git a/clang/test/CodeGenCXX/alloc-token-pointer.cpp b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
new file mode 100644
index 0000000000000..7adb75c7afebb
--- /dev/null
+++ b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
@@ -0,0 +1,177 @@
+// Check -fsanitize=alloc-token TypeHashPointerSplit mode with only 2
+// tokens so we effectively only test the contains-pointer logic.
+//
+// RUN: %clang_cc1    -fsanitize=alloc-token -falloc-token-max=2 -triple x86_64-linux-gnu -std=c++20 -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2 -triple x86_64-linux-gnu -std=c++20 -emit-llvm %s -o - | FileCheck %s
+
+#include "../Analysis/Inputs/system-header-simulator-cxx.h"
+
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+extern "C" {
+void *malloc(size_t size);
+}
+
+// CHECK-LABEL: @_Z15test_malloc_intv(
+void *test_malloc_int() {
+  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0)
+  int *a = (int *)malloc(sizeof(int));
+  *a = 42;
+  return a;
+}
+
+// CHECK-LABEL: @_Z15test_malloc_ptrv(
+int **test_malloc_ptr() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 8, i64 0)
+  int **a = (int **)malloc(sizeof(int*));
+  *a = nullptr;
+  return a;
+}
+
+// CHECK-LABEL: @_Z12test_new_intv(
+int *test_new_int() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0){{.*}} !alloc_token_hint
+  return new int;
+}
+
+// CHECK-LABEL: @_Z20test_new_ulong_arrayv(
+unsigned long *test_new_ulong_array() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 80, i64 0){{.*}} !alloc_token_hint
+  return new unsigned long[10];
+}
+
+// CHECK-LABEL: @_Z12test_new_ptrv(
+int **test_new_ptr() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 8, i64 1){{.*}} !alloc_token_hint
+  return new int*;
+}
+
+// CHECK-LABEL: @_Z18test_new_ptr_arrayv(
+int **test_new_ptr_array() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 80, i64 1){{.*}} !alloc_token_hint
+  return new int*[10];
+}
+
+struct ContainsPtr {
+  int a;
+  char *buf;
+};
+
+// CHECK-LABEL: @_Z27test_malloc_struct_with_ptrv(
+ContainsPtr *test_malloc_struct_with_ptr() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 16, i64 0)
+  ContainsPtr *c = (ContainsPtr *)malloc(sizeof(ContainsPtr));
+  return c;
+}
+
+// CHECK-LABEL: @_Z33test_malloc_struct_array_with_ptrv(
+ContainsPtr *test_malloc_struct_array_with_ptr() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 160, i64 0)
+  ContainsPtr *c = (ContainsPtr *)malloc(10 * sizeof(ContainsPtr));
+  return c;
+}
+
+// CHECK-LABEL: @_Z32test_operatornew_struct_with_ptrv(
+ContainsPtr *test_operatornew_struct_with_ptr() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 0)
+  ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(ContainsPtr));
+  return c;
+}
+
+// CHECK-LABEL: @_Z38test_operatornew_struct_array_with_ptrv(
+ContainsPtr *test_operatornew_struct_array_with_ptr() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 160, i64 0)
+  ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(ContainsPtr));
+  return c;
+}
+
+// CHECK-LABEL: @_Z33test_operatornew_struct_with_ptr2v(
+ContainsPtr *test_operatornew_struct_with_ptr2() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 0)
+  ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(*c));
+  return c;
+}
+
+// CHECK-LABEL: @_Z39test_operatornew_struct_array_with_ptr2v(
+ContainsPtr *test_operatornew_struct_array_with_ptr2() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 160, i64 0)
+  ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(*c));
+  return c;
+}
+
+// CHECK-LABEL: @_Z24test_new_struct_with_ptrv(
+ContainsPtr *test_new_struct_with_ptr() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 1){{.*}} !alloc_token_hint
+  return new ContainsPtr;
+}
+
+// CHECK-LABEL: @_Z30test_new_struct_array_with_ptrv(
+ContainsPtr *test_new_struct_array_with_ptr() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 160, i64 1){{.*}} !alloc_token_hint
+  return new ContainsPtr[10];
+}
+
+class TestClass {
+public:
+  void Foo();
+  ~TestClass();
+  int data[16];
+};
+
+// CHECK-LABEL: @_Z14test_new_classv(
+TestClass *test_new_class() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 64, i64 0){{.*}} !alloc_token_hint
+  return new TestClass();
+}
+
+// CHECK-LABEL: @_Z20test_new_class_arrayv(
+TestClass *test_new_class_array() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 648, i64 0){{.*}} !alloc_token_hint
+  return new TestClass[10];
+}
+
+// Test that we detect that virtual classes have implicit vtable pointer.
+class VirtualTestClass {
+public:
+  virtual void Foo();
+  virtual ~VirtualTestClass();
+  int data[16];
+};
+
+// CHECK-LABEL: @_Z22test_new_virtual_classv(
+VirtualTestClass *test_new_virtual_class() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 1){{.*}} !alloc_token_hint
+  return new VirtualTestClass();
+}
+
+// CHECK-LABEL: @_Z28test_new_virtual_class_arrayv(
+VirtualTestClass *test_new_virtual_class_array() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 1){{.*}} !alloc_token_hint
+  return new VirtualTestClass[10];
+}
+
+// uintptr_t is treated as a pointer.
+struct MyStructUintptr {
+  int a;
+  uintptr_t ptr;
+};
+
+// CHECK-LABEL: @_Z18test_uintptr_isptrv(
+MyStructUintptr *test_uintptr_isptr() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 1)
+  return new MyStructUintptr;
+}
+
+using uptr = uintptr_t;
+// CHECK-LABEL: @_Z19test_uintptr_isptr2v(
+uptr *test_uintptr_isptr2() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 8, i64 1)
+  return new uptr;
+}
diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp
new file mode 100644
index 0000000000000..180c771e43ae9
--- /dev/null
+++ b/clang/test/CodeGenCXX/alloc-token.cpp
@@ -0,0 +1,157 @@
+// RUN: %clang_cc1    -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s
+
+#include "../Analysis/Inputs/system-header-simulator-cxx.h"
+extern "C" {
+void *aligned_alloc(size_t alignment, size_t size);
+void *malloc(size_t size);
+void *calloc(size_t num, size_t size);
+void *realloc(void *ptr, size_t size);
+void *reallocarray(void *ptr, size_t nmemb, size_t size);
+void *memalign(size_t alignment, size_t size);
+void *valloc(size_t size);
+void *pvalloc(size_t size);
+int posix_memalign(void **memptr, size_t alignment, size_t size);
+
+struct __sized_ptr_t {
+  void *p;
+  size_t n;
+};
+enum class __hot_cold_t : uint8_t;
+__sized_ptr_t __size_returning_new(size_t size);
+__sized_ptr_t __size_returning_new_hot_cold(size_t, __hot_cold_t);
+__sized_ptr_t __size_returning_new_aligned(size_t, std::align_val_t);
+__sized_ptr_t __size_returning_new_aligned_hot_cold(size_t, std::align_val_t,  __hot_cold_t);
+}
+
+void *sink; // prevent optimizations from removing the calls
+
+// CHECK-LABEL: @_Z16test_malloc_likev(
+void test_malloc_like() {
+  // FIXME: Should not be token ID 0! Currently fail to infer the type.
+  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0)
+  sink = malloc(sizeof(int));
+  // CHECK: call{{.*}} ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0)
+  sink = calloc(3, sizeof(int));
+  // CHECK: call{{.*}} ptr @__alloc_token_realloc(ptr noundef {{[^,]*}}, i64 noundef 8, i64 0)
+  sink = realloc(sink, sizeof(long));
+  // CHECK: call{{.*}} ptr @__alloc_token_reallocarray(ptr noundef {{[^,]*}}, i64 noundef 5, i64 noundef 8, i64 0)
+  sink = reallocarray(sink, 5, sizeof(long));
+  // CHECK: call{{.*}} i32 @__alloc_token_posix_memalign(ptr noundef {{[^,]*}}, i64 noundef 64, i64 noundef 4, i64 0)
+  posix_memalign(&sink, 64, sizeof(int));
+  // CHECK: call align 128{{.*}} ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0)
+  sink = aligned_alloc(128, 1024);
+  // CHECK: call{{.*}} ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0)
+  sink = memalign(16, 256);
+  // CHECK: call{{.*}} ptr @__alloc_token_valloc(i64 noundef 4096, i64 0)
+  sink = valloc(4096);
+  // CHECK: call{{.*}} ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0)
+  sink = pvalloc(8192);
+}
+
+// CHECK-LABEL: @_Z17test_operator_newv(
+void test_operator_new() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0)
+  sink = __builtin_operator_new(sizeof(int));
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0)
+  sink = ::operator new(sizeof(int));
+}
+
+// CHECK-LABEL: @_Z25test_operator_new_nothrowv(
+void test_operator_new_nothrow() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0)
+  sink = __builtin_operator_new(sizeof(int), std::nothrow);
+  // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0)
+  sink = ::operator new(sizeof(int), std::nothrow);
+}
+
+// CHECK-LABEL: @_Z8test_newv(
+int *test_new() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  return new int;
+}
+
+// CHECK-LABEL: @_Z14test_new_arrayv(
+int *test_new_array() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  return new int[10];
+}
+
+// CHECK-LABEL: @_Z16test_new_nothrowv(
+int *test_new_nothrow() {
+  // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  return new (std::nothrow) int;
+}
+
+// CHECK-LABEL: @_Z22test_new_array_nothrowv(
+int *test_new_array_nothrow() {
+  // CHECK: call {{.*}} ptr @__alloc_token_ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  return new (std::nothrow) int[10];
+}
+
+// CHECK-LABEL: @_Z15no_sanitize_newv(
+__attribute__((no_sanitize("alloc-token"))) int *no_sanitize_new() {
+  // CHECK: call {{.*}} ptr @_Znwm(i64 noundef 4)
+  return new int;
+}
+
+// CHECK-LABEL: @_Z23test_size_returning_newv(
+void test_size_returning_new() {
+  // FIXME: This should not be token ID 0!
+  // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new(i64 noundef 8, i64 0)
+  // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1, i64 0)
+  // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_aligned(i64 noundef 8, i64 noundef 32, i64 0)
+  // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1, i64 0)
+  sink = __size_returning_new(sizeof(long)).p;
+  sink = __size_returning_new_hot_cold(sizeof(long), __hot_cold_t{1}).p;
+  sink = __size_returning_new_aligned(sizeof(long), std::align_val_t{32}).p;
+  sink = __size_returning_new_aligned_hot_cold(sizeof(long), std::align_val_t{32}, __hot_cold_t{1}).p;
+}
+
+class TestClass {
+public:
+  virtual void Foo();
+  virtual ~TestClass();
+  int data[16];
+};
+
+void may_throw();
+
+// CHECK-LABEL: @_Z27test_exception_handling_newv(
+TestClass *test_exception_handling_new() {
+  try {
+    // CHECK: invoke {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}})
+    // CHECK-NEXT: !alloc_token_hint
+    TestClass *obj = new TestClass();
+    may_throw();
+    return obj;
+  } catch (...) {
+    return nullptr;
+  }
+}
+
+// CHECK-LABEL: @_Z14test_new_classv(
+TestClass *test_new_class() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  TestClass *obj = new TestClass();
+  obj->data[0] = 42;
+  return obj;
+}
+
+// CHECK-LABEL: @_Z20test_new_class_arrayv(
+TestClass *test_new_class_array() {
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  TestClass* arr = new TestClass[10];
+  arr[0].data[0] = 123;
+  return arr;
+}
+
+// CHECK-LABEL: @_Z21test_delete_unchangedPiS_(
+void test_delete_unchanged(int *x, int *y) {
+  // CHECK: call void @_ZdlPvm
+  // CHECK: call void @_ZdaPv
+  delete x;
+  delete [] y;
+}
diff --git a/clang/test/Driver/fsanitize-alloc-token.c b/clang/test/Driver/fsanitize-alloc-token.c
new file mode 100644
index 0000000000000..2964f60c4f26f
--- /dev/null
+++ b/clang/test/Driver/fsanitize-alloc-token.c
@@ -0,0 +1,43 @@
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOKEN-ALLOC
+// CHECK-TOKEN-ALLOC: "-fsanitize=alloc-token"
+
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fno-sanitize=alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-TOKEN-ALLOC
+// CHECK-NO-TOKEN-ALLOC-NOT: "-fsanitize=alloc-token"
+
+// RUN: %clang --target=x86_64-linux-gnu -flto -fvisibility=hidden -fno-sanitize-ignorelist -fsanitize=alloc-token,undefined,cfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-COMPATIBLE
+// CHECK-COMPATIBLE: "-fsanitize={{.*}}alloc-token"
+
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MINIMAL
+// CHECK-MINIMAL: "-fsanitize=alloc-token"
+// CHECK-MINIMAL: "-fsanitize-minimal-runtime"
+
+// RUN: %clang --target=arm-arm-non-eabi -fsanitize=alloc-token %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-BAREMETAL
+// RUN: %clang --target=aarch64-none-elf -fsanitize=alloc-token %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-BAREMETAL
+// CHECK-BAREMETAL: "-fsanitize=alloc-token"
+
+// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token,address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-ADDRESS
+// CHECK-INCOMPATIBLE-ADDRESS: error: invalid argument '-fsanitize=alloc-token' not allowed with '-fsanitize=address'
+
+// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token,memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-MEMORY
+// CHECK-INCOMPATIBLE-MEMORY: error: invalid argument '-fsanitize=alloc-token' not allowed with '-fsanitize=memory'
+
+// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-trap=alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-TRAP
+// CHECK-INCOMPATIBLE-TRAP: error: unsupported argument 'alloc-token' to option '-fsanitize-trap='
+
+// RUN: not %clang --target=x86_64-linux-gnu %s -fsanitize=alloc-token -fsanitize-recover=alloc-token -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-RECOVER
+// CHECK-INCOMPATIBLE-RECOVER: unsupported argument 'alloc-token' to option '-fsanitize-recover='
+
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-alloc-token-fast-abi %s -### 2>&1 | FileCheck -check-prefix=CHECK-FASTABI %s
+// CHECK-FASTABI: "-fsanitize-alloc-token-fast-abi"
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-alloc-token-fast-abi -fno-sanitize-alloc-token-fast-abi %s -### 2>&1 | FileCheck -check-prefix=CHECK-NOFASTABI %s
+// CHECK-NOFASTABI-NOT: "-fsanitize-alloc-token-fast-abi"
+
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-alloc-token-extended %s -### 2>&1 | FileCheck -check-prefix=CHECK-EXTENDED %s
+// CHECK-EXTENDED: "-fsanitize-alloc-token-extended"
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-alloc-token-extended -fno-sanitize-alloc-token-extended %s -### 2>&1 | FileCheck -check-prefix=CHECK-NOEXTENDED %s
+// CHECK-NOEXTENDED-NOT: "-fsanitize-alloc-token-extended"
+
+// RUN: %clang --target=x86_64-linux-gnu -falloc-token-max=0 -falloc-token-max=42 %s -### 2>&1 | FileCheck -check-prefix=CHECK-MAX %s
+// CHECK-MAX: "-falloc-token-max=42"
+// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -falloc-token-max=-1 %s 2>&1 | FileCheck -check-prefix=CHECK-INVALID-MAX %s
+// CHECK-INVALID-MAX: error: invalid value
diff --git a/clang/test/Preprocessor/alloc_token.cpp b/clang/test/Preprocessor/alloc_token.cpp
new file mode 100644
index 0000000000000..0c51bfb9405f2
--- /dev/null
+++ b/clang/test/Preprocessor/alloc_token.cpp
@@ -0,0 +1,10 @@
+// RUN: %clang_cc1 -E -fsanitize=alloc-token %s -o - | FileCheck --check-prefix=CHECK-SANITIZE %s
+// RUN: %clang_cc1 -E  %s -o - | FileCheck --check-prefix=CHECK-DEFAULT %s
+
+#if __SANITIZE_ALLOC_TOKEN__
+// CHECK-SANITIZE: has_sanitize_alloc_token
+int has_sanitize_alloc_token();
+#else
+// CHECK-DEFAULT: no_sanitize_alloc_token
+int no_sanitize_alloc_token();
+#endif
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index e64b9343b7622..4791527a4b86b 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2427,6 +2427,9 @@ For example:
     if the attributed function is called during invocation of a function
     attributed with ``sanitize_realtime``.
     This attribute is incompatible with the ``sanitize_realtime`` attribute.
+``sanitize_alloc_token``
+    This attributes indicates that implicit allocation token instrumentation
+    is enabled for this function.
 ``speculative_load_hardening``
     This attribute indicates that
     `Speculative Load Hardening <https://llvm.org/docs/SpeculativeLoadHardening.html>`_
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index ff92d7390ecfd..eae9a73bedc34 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -166,6 +166,10 @@ Changes to Sanitizers
 Other Changes
 -------------
 
+* Introduces the `AllocToken` pass, an instrumentation pass designed to provide
+  tokens to memory allocators enabling various heap organization strategies,
+  such as heap partitioning.
+
 External Open Source Projects Using LLVM {{env.config.release}}
 ===============================================================
 
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 1c7d3462b6bae..464f475098ec5 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -800,6 +800,7 @@ enum AttributeKindCodes {
   ATTR_KIND_SANITIZE_TYPE = 101,
   ATTR_KIND_CAPTURES = 102,
   ATTR_KIND_DEAD_ON_RETURN = 103,
+  ATTR_KIND_SANITIZE_ALLOC_TOKEN = 104,
 };
 
 enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index ef816fb86ed1d..8e7d9dcebfe2a 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -342,6 +342,9 @@ def SanitizeRealtime : EnumAttr<"sanitize_realtime", IntersectPreserve, [FnAttr]
 /// during a real-time sanitized function (see `sanitize_realtime`).
 def SanitizeRealtimeBlocking : EnumAttr<"sanitize_realtime_blocking", IntersectPreserve, [FnAttr]>;
 
+/// Allocation token instrumentation is on.
+def SanitizeAllocToken : EnumAttr<"sanitize_alloc_token", IntersectPreserve, [FnAttr]>;
+
 /// Speculative Load Hardening is enabled.
 ///
 /// Note that this uses the default compatibility (always compatible during
diff --git a/llvm/include/llvm/Transforms/Instrumentation/AllocToken.h b/llvm/include/llvm/Transforms/Instrumentation/AllocToken.h
new file mode 100644
index 0000000000000..b1391cb04302c
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Instrumentation/AllocToken.h
@@ -0,0 +1,46 @@
+//===- AllocToken.h - Allocation token instrumentation --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file declares the AllocTokenPass, an instrumentation pass that
+// replaces allocation calls with ones including an allocation token.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_ALLOCTOKEN_H
+#define LLVM_TRANSFORMS_INSTRUMENTATION_ALLOCTOKEN_H
+
+#include "llvm/IR/Analysis.h"
+#include "llvm/IR/PassManager.h"
+#include <optional>
+
+namespace llvm {
+
+class Module;
+
+struct AllocTokenOptions {
+  std::optional<uint64_t> MaxTokens;
+  bool FastABI = false;
+  bool Extended = false;
+  AllocTokenOptions() = default;
+};
+
+/// A module pass that rewrites heap allocations to use token-enabled
+/// allocation functions based on various source-level properties.
+class AllocTokenPass : public PassInfoMixin<AllocTokenPass> {
+public:
+  LLVM_ABI explicit AllocTokenPass(AllocTokenOptions Opts = {});
+  LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
+  static bool isRequired() { return true; }
+
+private:
+  const AllocTokenOptions Options;
+};
+
+} // namespace llvm
+
+#endif // LLVM_TRANSFORMS_INSTRUMENTATION_ALLOCTOKEN_H
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 22a0d0ffdbaab..67ad4a2655ecd 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2203,6 +2203,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
     return Attribute::SanitizeRealtime;
   case bitc::ATTR_KIND_SANITIZE_REALTIME_BLOCKING:
     return Attribute::SanitizeRealtimeBlocking;
+  case bitc::ATTR_KIND_SANITIZE_ALLOC_TOKEN:
+    return Attribute::SanitizeAllocToken;
   case bitc::ATTR_KIND_SPECULATIVE_LOAD_HARDENING:
     return Attribute::SpeculativeLoadHardening;
   case bitc::ATTR_KIND_SWIFT_ERROR:
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index a1d5b36bde64d..742c1836c32cd 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -883,6 +883,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
     return bitc::ATTR_KIND_STRUCT_RET;
   case Attribute::SanitizeAddress:
     return bitc::ATTR_KIND_SANITIZE_ADDRESS;
+  case Attribute::SanitizeAllocToken:
+    return bitc::ATTR_KIND_SANITIZE_ALLOC_TOKEN;
   case Attribute::SanitizeHWAddress:
     return bitc::ATTR_KIND_SANITIZE_HWADDRESS;
   case Attribute::SanitizeThread:
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index d75304b5e11f6..221c6a6363613 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -237,6 +237,7 @@
 #include "llvm/Transforms/IPO/WholeProgramDevirt.h"
 #include "llvm/Transforms/InstCombine/InstCombine.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
+#include "llvm/Transforms/Instrumentation/AllocToken.h"
 #include "llvm/Transforms/Instrumentation/BoundsChecking.h"
 #include "llvm/Transforms/Instrumentation/CGProfile.h"
 #include "llvm/Transforms/Instrumentation/ControlHeightReduction.h"
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 4b462b9c6845c..00896b2a8f3fc 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -124,6 +124,7 @@ MODULE_PASS("openmp-opt", OpenMPOptPass())
 MODULE_PASS("openmp-opt-postlink",
             OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink))
 MODULE_PASS("partial-inliner", PartialInlinerPass())
+MODULE_PASS("alloc-token", AllocTokenPass())
 MODULE_PASS("pgo-icall-prom", PGOIndirectCallPromotion())
 MODULE_PASS("pgo-instr-gen", PGOInstrumentationGen())
 MODULE_PASS("pgo-instr-use", PGOInstrumentationUse())
diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
new file mode 100644
index 0000000000000..fc3beba4a86fd
--- /dev/null
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -0,0 +1,529 @@
+//===- AllocToken.cpp - Allocation token instrumentation ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements AllocToken, an instrumentation pass that
+// replaces allocation calls with token-enabled versions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Instrumentation/AllocToken.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Analysis/MemoryBuiltins.h"
+#include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/IR/Analysis.h"
+#include "llvm/IR/Attributes.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/IR/Type.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/xxhash.h"
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <limits>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+#include <variant>
+
+using namespace llvm;
+
+#define DEBUG_TYPE "alloc-token"
+
+namespace {
+
+//===--- Constants --------------------------------------------------------===//
+
+enum class TokenMode : unsigned {
+  /// Incrementally increasing token ID.
+  Increment = 0,
+
+  /// Simple mode that returns a statically-assigned random token ID.
+  Random = 1,
+
+  /// Token ID based on allocated type hash.
+  TypeHash = 2,
+
+  /// Token ID based on allocated type hash, where the top half ID-space is
+  /// reserved for types that contain pointers and the bottom half for types
+  /// that do not contain pointers.
+  TypeHashPointerSplit = 3,
+
+  // Mode count - keep last
+  ModeCount
+};
+
+//===--- Command-line options ---------------------------------------------===//
+
+struct ModeParser : public cl::parser<unsigned> {
+  ModeParser(cl::Option &O) : cl::parser<unsigned>(O) {}
+  bool parse(cl::Option &O, StringRef ArgName, StringRef Arg, unsigned &Value) {
+    if (cl::parser<unsigned>::parse(O, ArgName, Arg, Value))
+      return true;
+    if (Value >= static_cast<unsigned>(TokenMode::ModeCount))
+      return O.error("'" + Arg + "' value invalid");
+    return false;
+  }
+};
+
+cl::opt<unsigned, false, ModeParser>
+    ClMode("alloc-token-mode", cl::desc("Token assignment mode"), cl::Hidden,
+           cl::init(static_cast<unsigned>(TokenMode::TypeHashPointerSplit)));
+
+cl::opt<std::string> ClFuncPrefix("alloc-token-prefix",
+                                  cl::desc("The allocation function prefix"),
+                                  cl::Hidden, cl::init("__alloc_token_"));
+
+cl::opt<uint64_t> ClMaxTokens("alloc-token-max",
+                              cl::desc("Maximum number of tokens (0 = no max)"),
+                              cl::Hidden, cl::init(0));
+
+cl::opt<bool>
+    ClFastABI("alloc-token-fast-abi",
+              cl::desc("The token ID is encoded in the function name"),
+              cl::Hidden, cl::init(false));
+
+// Instrument libcalls only by default - compatible allocators only need to take
+// care of providing standard allocation functions. With extended coverage, also
+// instrument non-libcall allocation function calls with !alloc_token_hint
+// metadata.
+cl::opt<bool>
+    ClExtended("alloc-token-extended",
+               cl::desc("Extend coverage to custom allocation functions"),
+               cl::Hidden, cl::init(false));
+
+// C++ defines ::operator new (and variants) as replaceable (vs. standard
+// library versions), which are nobuiltin, and are therefore not covered by
+// isAllocationFn(). Cover by default, as users of AllocToken are already
+// required to provide token-aware allocation functions (no defaults).
+cl::opt<bool> ClCoverReplaceableNew("alloc-token-cover-replaceable-new",
+                                    cl::desc("Cover replaceable operator new"),
+                                    cl::Hidden, cl::init(true));
+
+// strdup-family functions only operate on strings, covering them does not make
+// sense in most cases.
+cl::opt<bool>
+    ClCoverStrdup("alloc-token-cover-strdup",
+                  cl::desc("Cover strdup-family allocation functions"),
+                  cl::Hidden, cl::init(false));
+
+cl::opt<uint64_t> ClFallbackToken(
+    "alloc-token-fallback",
+    cl::desc("The default fallback token where none could be determined"),
+    cl::Hidden, cl::init(0));
+
+//===--- Statistics -------------------------------------------------------===//
+
+STATISTIC(NumFunctionsInstrumented, "Functions instrumented");
+STATISTIC(NumAllocations, "Allocations found");
+
+//===----------------------------------------------------------------------===//
+
+/// Returns the !alloc_token_hint metadata if available.
+///
+/// Expected format is: !{<type-name>, <contains-pointer>}
+MDNode *getAllocTokenHintMetadata(const CallBase &CB) {
+  MDNode *Ret = CB.getMetadata("alloc_token_hint");
+  if (!Ret)
+    return nullptr;
+  assert(Ret->getNumOperands() == 2 && "bad !alloc_token_hint");
+  assert(isa<MDString>(Ret->getOperand(0)));
+  assert(isa<ConstantAsMetadata>(Ret->getOperand(1)));
+  return Ret;
+}
+
+bool containsPointer(const MDNode *MD) {
+  ConstantAsMetadata *C = cast<ConstantAsMetadata>(MD->getOperand(1));
+  auto *CI = cast<ConstantInt>(C->getValue());
+  return CI->getValue().getBoolValue();
+}
+
+class ModeBase {
+public:
+  explicit ModeBase(uint64_t MaxTokens) : MaxTokens(MaxTokens) {}
+
+protected:
+  uint64_t boundedToken(uint64_t Val) const {
+    return MaxTokens ? Val % MaxTokens : Val;
+  }
+
+  const uint64_t MaxTokens;
+};
+
+/// Implementation for TokenMode::Increment.
+class IncrementMode : public ModeBase {
+public:
+  using ModeBase::ModeBase;
+
+  uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &) {
+    return boundedToken(Counter++);
+  }
+
+private:
+  uint64_t Counter = 0;
+};
+
+/// Implementation for TokenMode::Random.
+class RandomMode : public ModeBase {
+public:
+  RandomMode(uint64_t MaxTokens, std::unique_ptr<RandomNumberGenerator> RNG)
+      : ModeBase(MaxTokens), RNG(std::move(RNG)) {}
+  uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &) {
+    return boundedToken((*RNG)());
+  }
+
+private:
+  std::unique_ptr<RandomNumberGenerator> RNG;
+};
+
+/// Implementation for TokenMode::TypeHash. The implementation ensures
+/// hashes are stable across different compiler invocations. Uses xxHash as the
+/// hash function.
+class TypeHashMode : public ModeBase {
+public:
+  using ModeBase::ModeBase;
+
+  uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
+    const auto [N, H] = getHash(CB, ORE);
+    return N ? boundedToken(H) : H;
+  }
+
+protected:
+  std::pair<MDNode *, uint64_t> getHash(const CallBase &CB,
+                                        OptimizationRemarkEmitter &ORE) {
+    if (MDNode *N = getAllocTokenHintMetadata(CB)) {
+      MDString *S = cast<MDString>(N->getOperand(0));
+      return {N, xxHash64(S->getString())};
+    }
+    // Fallback.
+    remarkNoHint(CB, ORE);
+    return {nullptr, ClFallbackToken};
+  }
+
+  /// Remark that there was no precise type information.
+  void remarkNoHint(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
+    ORE.emit([&] {
+      ore::NV FuncNV("Function", CB.getParent()->getParent());
+      const Function *Callee = CB.getCalledFunction();
+      ore::NV CalleeNV("Callee", Callee ? Callee->getName() : "<unknown>");
+      return OptimizationRemark(DEBUG_TYPE, "NoAllocTokenHint", &CB)
+             << "Call to '" << CalleeNV << "' in '" << FuncNV
+             << "' without source-level type token";
+    });
+  }
+};
+
+/// Implementation for TokenMode::TypeHashPointerSplit.
+class TypeHashPointerSplitMode : public TypeHashMode {
+public:
+  using TypeHashMode::TypeHashMode;
+
+  uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
+    if (MaxTokens == 1)
+      return 0;
+    const uint64_t HalfTokens =
+        (MaxTokens ? MaxTokens : std::numeric_limits<uint64_t>::max()) / 2;
+    const auto [N, H] = getHash(CB, ORE);
+    if (!N)
+      return H;                     // fallback token
+    uint64_t Hash = H % HalfTokens; // base hash
+    if (containsPointer(N))
+      Hash += HalfTokens;
+    return Hash;
+  }
+};
+
+// Apply opt overrides.
+AllocTokenOptions &&transformOptionsFromCl(AllocTokenOptions &&Opts) {
+  if (!Opts.MaxTokens.has_value())
+    Opts.MaxTokens = ClMaxTokens;
+  Opts.FastABI |= ClFastABI;
+  Opts.Extended |= ClExtended;
+  return std::move(Opts);
+}
+
+class AllocToken {
+public:
+  explicit AllocToken(AllocTokenOptions Opts, Module &M,
+                      ModuleAnalysisManager &MAM)
+      : Options(transformOptionsFromCl(std::move(Opts))), Mod(M),
+        FAM(MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager()),
+        Mode(IncrementMode(*Options.MaxTokens)) {
+    switch (static_cast<TokenMode>(ClMode.getValue())) {
+    case TokenMode::Increment:
+      break;
+    case TokenMode::Random:
+      Mode.emplace<RandomMode>(*Options.MaxTokens, M.createRNG(DEBUG_TYPE));
+      break;
+    case TokenMode::TypeHash:
+      Mode.emplace<TypeHashMode>(*Options.MaxTokens);
+      break;
+    case TokenMode::TypeHashPointerSplit:
+      Mode.emplace<TypeHashPointerSplitMode>(*Options.MaxTokens);
+      break;
+    case TokenMode::ModeCount:
+      llvm_unreachable("");
+      break;
+    }
+  }
+
+  bool instrumentFunction(Function &F);
+
+private:
+  /// Returns true for !isAllocationFn() functions that are also eligible for
+  /// instrumentation.
+  bool isInstrumentableLibFunc(LibFunc Func) const;
+
+  /// Returns true for isAllocationFn() functions that we should ignore.
+  bool ignoreInstrumentableLibFunc(LibFunc Func) const;
+
+  /// Replace a call/invoke with a call/invoke to the allocation function
+  /// with token ID.
+  void replaceAllocationCall(CallBase *CB, LibFunc Func,
+                             OptimizationRemarkEmitter &ORE,
+                             const TargetLibraryInfo &TLI);
+
+  /// Return replacement function for a LibFunc that takes a token ID.
+  FunctionCallee getTokenAllocFunction(const CallBase &CB, uint64_t TokenID,
+                                       LibFunc OriginalFunc);
+
+  /// Return the token ID from metadata in the call.
+  uint64_t getToken(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
+    return std::visit([&](auto &&Mode) { return Mode(CB, ORE); }, Mode);
+  }
+
+  const AllocTokenOptions Options;
+  Module &Mod;
+  FunctionAnalysisManager &FAM;
+  // Cache for replacement functions.
+  DenseMap<std::pair<LibFunc, uint64_t>, FunctionCallee> TokenAllocFunctions;
+  // Selected mode.
+  std::variant<IncrementMode, RandomMode, TypeHashMode,
+               TypeHashPointerSplitMode>
+      Mode;
+};
+
+bool AllocToken::instrumentFunction(Function &F) {
+  // Do not apply any instrumentation for naked functions.
+  if (F.hasFnAttribute(Attribute::Naked))
+    return false;
+  if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation))
+    return false;
+  // Don't touch available_externally functions, their actual body is elsewhere.
+  if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage)
+    return false;
+  // Only instrument functions that have the sanitize_alloc_token attribute.
+  if (!F.hasFnAttribute(Attribute::SanitizeAllocToken))
+    return false;
+
+  auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(F);
+  auto &TLI = FAM.getResult<TargetLibraryAnalysis>(F);
+  SmallVector<std::pair<CallBase *, LibFunc>, 4> AllocCalls;
+
+  // Collect all allocation calls to avoid iterator invalidation.
+  for (Instruction &I : instructions(F)) {
+    auto *CB = dyn_cast<CallBase>(&I);
+    if (!CB)
+      continue;
+    const Function *Callee = CB->getCalledFunction();
+    if (!Callee)
+      continue;
+    // Ignore nobuiltin of the CallBase, so that we can cover nobuiltin libcalls
+    // if requested via isInstrumentableLibFunc(). Note that isAllocationFn() is
+    // returning false for nobuiltin calls.
+    LibFunc Func;
+    if (TLI.getLibFunc(*Callee, Func)) {
+      if (ignoreInstrumentableLibFunc(Func))
+        continue;
+      if (isInstrumentableLibFunc(Func) || isAllocationFn(CB, &TLI))
+        AllocCalls.emplace_back(CB, Func);
+    } else if (Options.Extended && getAllocTokenHintMetadata(*CB)) {
+      AllocCalls.emplace_back(CB, NotLibFunc);
+    }
+  }
+
+  bool Modified = false;
+
+  if (!AllocCalls.empty()) {
+    for (auto &[CB, Func] : AllocCalls) {
+      replaceAllocationCall(CB, Func, ORE, TLI);
+    }
+    NumAllocations += AllocCalls.size();
+    NumFunctionsInstrumented++;
+    Modified = true;
+  }
+
+  return Modified;
+}
+
+bool AllocToken::isInstrumentableLibFunc(LibFunc Func) const {
+  switch (Func) {
+  case LibFunc_posix_memalign:
+  case LibFunc_size_returning_new:
+  case LibFunc_size_returning_new_hot_cold:
+  case LibFunc_size_returning_new_aligned:
+  case LibFunc_size_returning_new_aligned_hot_cold:
+    return true;
+  case LibFunc_Znwj:
+  case LibFunc_ZnwjRKSt9nothrow_t:
+  case LibFunc_ZnwjSt11align_val_t:
+  case LibFunc_ZnwjSt11align_val_tRKSt9nothrow_t:
+  case LibFunc_Znwm:
+  case LibFunc_Znwm12__hot_cold_t:
+  case LibFunc_ZnwmRKSt9nothrow_t:
+  case LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t:
+  case LibFunc_ZnwmSt11align_val_t:
+  case LibFunc_ZnwmSt11align_val_t12__hot_cold_t:
+  case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t:
+  case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
+  case LibFunc_Znaj:
+  case LibFunc_ZnajRKSt9nothrow_t:
+  case LibFunc_ZnajSt11align_val_t:
+  case LibFunc_ZnajSt11align_val_tRKSt9nothrow_t:
+  case LibFunc_Znam:
+  case LibFunc_Znam12__hot_cold_t:
+  case LibFunc_ZnamRKSt9nothrow_t:
+  case LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t:
+  case LibFunc_ZnamSt11align_val_t:
+  case LibFunc_ZnamSt11align_val_t12__hot_cold_t:
+  case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t:
+  case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
+    return ClCoverReplaceableNew;
+  default:
+    return false;
+  }
+}
+
+bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) const {
+  switch (Func) {
+  case LibFunc_strdup:
+  case LibFunc_dunder_strdup:
+  case LibFunc_strndup:
+  case LibFunc_dunder_strndup:
+    return !ClCoverStrdup;
+  default:
+    return false;
+  }
+}
+
+void AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func,
+                                       OptimizationRemarkEmitter &ORE,
+                                       const TargetLibraryInfo &TLI) {
+  uint64_t TokenID = getToken(*CB, ORE);
+
+  FunctionCallee TokenAlloc = getTokenAllocFunction(*CB, TokenID, Func);
+  if (!TokenAlloc)
+    return;
+
+  IRBuilder<> IRB(CB);
+
+  // Original args.
+  SmallVector<Value *, 4> NewArgs{CB->args()};
+  if (!Options.FastABI) {
+    // Add token ID.
+    NewArgs.push_back(
+        ConstantInt::get(Type::getInt64Ty(Mod.getContext()), TokenID));
+  }
+  assert(TokenAlloc.getFunctionType()->getNumParams() == NewArgs.size());
+
+  // Preserve invoke vs call semantics for exception handling.
+  CallBase *NewCall;
+  if (auto *II = dyn_cast<InvokeInst>(CB)) {
+    NewCall = IRB.CreateInvoke(TokenAlloc, II->getNormalDest(),
+                               II->getUnwindDest(), NewArgs);
+  } else {
+    NewCall = IRB.CreateCall(TokenAlloc, NewArgs);
+    cast<CallInst>(NewCall)->setTailCall(CB->isTailCall());
+  }
+  NewCall->setCallingConv(CB->getCallingConv());
+  NewCall->copyMetadata(*CB);
+  NewCall->setAttributes(CB->getAttributes());
+
+  // Replace all uses and delete the old call.
+  CB->replaceAllUsesWith(NewCall);
+  CB->eraseFromParent();
+}
+
+FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB,
+                                                 uint64_t TokenID,
+                                                 LibFunc OriginalFunc) {
+  std::optional<std::pair<LibFunc, uint64_t>> Key;
+  if (OriginalFunc != NotLibFunc) {
+    Key = std::make_pair(OriginalFunc, Options.FastABI ? TokenID : 0);
+    auto It = TokenAllocFunctions.find(*Key);
+    if (LLVM_LIKELY(It != TokenAllocFunctions.end()))
+      return It->second;
+  }
+
+  const Function *Callee = CB.getCalledFunction();
+  if (!Callee)
+    return FunctionCallee();
+  const FunctionType *OldFTy = Callee->getFunctionType();
+  if (OldFTy->isVarArg())
+    return FunctionCallee();
+  // Copy params, and append token ID type.
+  LLVMContext &C = Mod.getContext();
+  Type *RetTy = OldFTy->getReturnType();
+  SmallVector<Type *, 4> NewParams{OldFTy->params()};
+  std::string TokenAllocName = ClFuncPrefix;
+  if (Options.FastABI) {
+    TokenAllocName += utostr(TokenID) + "_";
+  } else {
+    NewParams.push_back(Type::getInt64Ty(C)); // token ID
+  }
+  FunctionType *NewFTy = FunctionType::get(RetTy, NewParams, false);
+  // Remove leading '_' - we add our own.
+  StringRef No_ = Callee->getName().drop_while([](char C) { return C == '_'; });
+  TokenAllocName += No_;
+  FunctionCallee TokenAlloc = Mod.getOrInsertFunction(TokenAllocName, NewFTy);
+  if (Function *F = dyn_cast<Function>(TokenAlloc.getCallee()))
+    F->copyAttributesFrom(Callee); // preserve attrs
+
+  if (Key.has_value())
+    TokenAllocFunctions[*Key] = TokenAlloc;
+  return TokenAlloc;
+}
+
+} // namespace
+
+AllocTokenPass::AllocTokenPass(AllocTokenOptions Opts)
+    : Options(std::move(Opts)) {}
+
+PreservedAnalyses AllocTokenPass::run(Module &M, ModuleAnalysisManager &MAM) {
+  AllocToken Pass(Options, M, MAM);
+  bool Modified = false;
+
+  for (Function &F : M) {
+    if (LLVM_LIKELY(F.empty()))
+      continue; // declaration
+    Modified |= Pass.instrumentFunction(F);
+  }
+
+  return Modified ? PreservedAnalyses::none() : PreservedAnalyses::all();
+}
diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
index 15fd421a41b0f..80576c61fd80c 100644
--- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
+++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_llvm_component_library(LLVMInstrumentation
   AddressSanitizer.cpp
+  AllocToken.cpp
   BoundsChecking.cpp
   CGProfile.cpp
   ControlHeightReduction.cpp
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index bbd1ed6a3ab2d..5ba6f95f5fae8 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -970,6 +970,7 @@ Function *CodeExtractor::constructFunctionDeclaration(
       case Attribute::SanitizeMemTag:
       case Attribute::SanitizeRealtime:
       case Attribute::SanitizeRealtimeBlocking:
+      case Attribute::SanitizeAllocToken:
       case Attribute::SpeculativeLoadHardening:
       case Attribute::StackProtect:
       case Attribute::StackProtectReq:
diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll
index 8c1a76365e1b4..aef7810fe2c3b 100644
--- a/llvm/test/Bitcode/attributes.ll
+++ b/llvm/test/Bitcode/attributes.ll
@@ -516,6 +516,11 @@ define void @f93() sanitize_realtime_blocking {
         ret void;
 }
 
+; CHECK: define void @f_sanitize_alloc_token() #55
+define void @f_sanitize_alloc_token() sanitize_alloc_token {
+        ret void;
+}
+
 ; CHECK: define void @f87() [[FNRETTHUNKEXTERN:#[0-9]+]]
 define void @f87() fn_ret_thunk_extern { ret void }
 
@@ -627,6 +632,7 @@ define void @dead_on_return(ptr dead_on_return %p) {
 ; CHECK: attributes #52 = { nosanitize_bounds }
 ; CHECK: attributes #53 = { sanitize_realtime }
 ; CHECK: attributes #54 = { sanitize_realtime_blocking }
+; CHECK: attributes #55 = { sanitize_alloc_token }
 ; CHECK: attributes [[FNRETTHUNKEXTERN]] = { fn_ret_thunk_extern }
 ; CHECK: attributes [[SKIPPROFILE]] = { skipprofile }
 ; CHECK: attributes [[OPTDEBUG]] = { optdebug }
diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll
index 0b5ce08c00a23..e21786e5ee330 100644
--- a/llvm/test/Bitcode/compatibility.ll
+++ b/llvm/test/Bitcode/compatibility.ll
@@ -1718,7 +1718,7 @@ exit:
   ; CHECK: select <2 x i1> <i1 true, i1 false>, <2 x i8> <i8 2, i8 3>, <2 x i8> <i8 3, i8 2>
 
   call void @f.nobuiltin() builtin
-  ; CHECK: call void @f.nobuiltin() #54
+  ; CHECK: call void @f.nobuiltin() #55
 
   call fastcc noalias ptr @f.noalias() noinline
   ; CHECK: call fastcc noalias ptr @f.noalias() #12
@@ -2151,6 +2151,9 @@ declare void @f.sanitize_realtime() sanitize_realtime
 declare void @f.sanitize_realtime_blocking() sanitize_realtime_blocking
 ; CHECK: declare void @f.sanitize_realtime_blocking() #53
 
+declare void @f.sanitize_alloc_token() sanitize_alloc_token
+; CHECK: declare void @f.sanitize_alloc_token() #54
+
 ; CHECK: declare nofpclass(snan) float @nofpclass_snan(float nofpclass(snan))
 declare nofpclass(snan) float @nofpclass_snan(float nofpclass(snan))
 
@@ -2284,7 +2287,8 @@ define float @nofpclass_callsites(float %arg, { float } %arg1) {
 ; CHECK: attributes #51 = { sanitize_numerical_stability }
 ; CHECK: attributes #52 = { sanitize_realtime }
 ; CHECK: attributes #53 = { sanitize_realtime_blocking }
-; CHECK: attributes #54 = { builtin }
+; CHECK: attributes #54 = { sanitize_alloc_token }
+; CHECK: attributes #55 = { builtin }
 
 ;; Metadata
 
diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll
new file mode 100644
index 0000000000000..94f5ef7ac5511
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/basic.ll
@@ -0,0 +1,84 @@
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare ptr @malloc(i64)
+declare ptr @calloc(i64, i64)
+declare ptr @realloc(ptr, i64)
+declare ptr @_Znwm(i64)
+declare ptr @_Znam(i64)
+declare void @free(ptr)
+declare void @_ZdlPv(ptr)
+declare i32 @foobar(i64)
+
+; Test basic allocation call rewriting
+; CHECK-LABEL: @test_basic_rewriting
+define ptr @test_basic_rewriting() sanitize_alloc_token {
+entry:
+  ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_malloc(i64 64, i64 0)
+  ; CHECK: call ptr @__alloc_token_calloc(i64 8, i64 8, i64 1)
+  ; CHECK: call ptr @__alloc_token_realloc(ptr [[PTR1]], i64 128, i64 2)
+  ; CHECK-NOT: call ptr @malloc(
+  ; CHECK-NOT: call ptr @calloc(
+  ; CHECK-NOT: call ptr @realloc(
+  %ptr1 = call ptr @malloc(i64 64)
+  %ptr2 = call ptr @calloc(i64 8, i64 8)
+  %ptr3 = call ptr @realloc(ptr %ptr1, i64 128)
+  ret ptr %ptr3
+}
+
+; Test C++ operator rewriting
+; CHECK-LABEL: @test_cpp_operators
+define ptr @test_cpp_operators() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_Znwm(i64 32, i64 3)
+  ; CHECK: call ptr @__alloc_token_Znam(i64 64, i64 4)
+  ; CHECK-NOT: call ptr @_Znwm(
+  ; CHECK-NOT: call ptr @_Znam(
+  %ptr1 = call ptr @_Znwm(i64 32)
+  %ptr2 = call ptr @_Znam(i64 64)
+  ret ptr %ptr1
+}
+
+; Functions without sanitize_alloc_token do not get instrumented
+; CHECK-LABEL: @without_attribute
+define ptr @without_attribute() {
+entry:
+  ; CHECK: call ptr @malloc(i64 16)
+  ; CHECK-NOT: call ptr @__alloc_token_malloc
+  %ptr = call ptr @malloc(i64 16)
+  ret ptr %ptr
+}
+
+; Test that free/delete are untouched
+; CHECK-LABEL: @test_free_untouched
+define void @test_free_untouched(ptr %ptr) sanitize_alloc_token {
+entry:
+  ; CHECK: call void @free(ptr %ptr)
+  ; CHECK: call void @_ZdlPv(ptr %ptr)
+  ; CHECK-NOT: call ptr @__alloc_token_
+  call void @free(ptr %ptr)
+  call void @_ZdlPv(ptr %ptr)
+  ret void
+}
+
+; Non-allocation functions are untouched
+; CHECK-LABEL: @no_allocations
+define i32 @no_allocations(i32 %x) sanitize_alloc_token {
+entry:
+  ; CHECK: call i32 @foobar
+  ; CHECK-NOT: call i32 @__alloc_token_
+  %result = call i32 @foobar(i64 42)
+  ret i32 %result
+}
+
+; Test that tail calls are preserved
+; CHECK-LABEL: @test_tail_call_preserved
+define ptr @test_tail_call_preserved() sanitize_alloc_token {
+entry:
+  ; CHECK: tail call ptr @__alloc_token_malloc(i64 42, i64 5)
+  ; CHECK-NOT: tail call ptr @malloc(
+  %result = tail call ptr @malloc(i64 42)
+  ret ptr %result
+}
diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
new file mode 100644
index 0000000000000..94fc12b20caff
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
@@ -0,0 +1,32 @@
+; Test for special libfuncs not automatically considered allocation functions.
+;
+; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare {ptr, i64} @__size_returning_new(i64)
+
+; CHECK-LABEL: @test_extra_libfuncs
+define ptr @test_extra_libfuncs() sanitize_alloc_token {
+entry:
+  ; CHECK: call {{.*}} @__alloc_token_size_returning_new(
+  %srn = call {ptr, i64} @__size_returning_new(i64 10), !alloc_token_hint !0
+  %ptr1  = extractvalue {ptr, i64} %srn, 0
+  ret ptr %ptr1
+}
+
+declare ptr @_Znwm(i64) nobuiltin allocsize(0)
+declare ptr @_Znam(i64) nobuiltin allocsize(0)
+
+; CHECK-LABEL: @test_replaceable_new
+define ptr @test_replaceable_new() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_Znwm(
+  %ptr1 = call ptr @_Znwm(i64 32), !alloc_token_hint !0
+  ; CHECK: call ptr @__alloc_token_Znam(
+  %ptr2 = call ptr @_Znam(i64 64), !alloc_token_hint !0
+  ret ptr %ptr1
+}
+
+!0 = !{!"int", i1 0}
diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll
new file mode 100644
index 0000000000000..c691cdcdc37c6
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/fast.ll
@@ -0,0 +1,39 @@
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare ptr @malloc(i64)
+declare ptr @calloc(i64, i64)
+declare ptr @realloc(ptr, i64)
+declare ptr @_Znwm(i64)
+declare ptr @_Znam(i64)
+
+; Test basic allocation call rewriting
+; CHECK-LABEL: @test_basic_rewriting
+define ptr @test_basic_rewriting() sanitize_alloc_token {
+entry:
+  ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_0_malloc(i64 64)
+  ; CHECK: call ptr @__alloc_token_1_calloc(i64 8, i64 8)
+  ; CHECK: call ptr @__alloc_token_2_realloc(ptr [[PTR1]], i64 128)
+  ; CHECK-NOT: call ptr @malloc(
+  ; CHECK-NOT: call ptr @calloc(
+  ; CHECK-NOT: call ptr @realloc(
+  %ptr1 = call ptr @malloc(i64 64)
+  %ptr2 = call ptr @calloc(i64 8, i64 8)
+  %ptr3 = call ptr @realloc(ptr %ptr1, i64 128)
+  ret ptr %ptr3
+}
+
+; Test C++ operator rewriting
+; CHECK-LABEL: @test_cpp_operators
+define ptr @test_cpp_operators() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_0_Znwm(i64 32)
+  ; CHECK: call ptr @__alloc_token_1_Znam(i64 64)
+  ; CHECK-NOT: call ptr @_Znwm(
+  ; CHECK-NOT: call ptr @_Znam(
+  %ptr1 = call ptr @_Znwm(i64 32)
+  %ptr2 = call ptr @_Znam(i64 64)
+  ret ptr %ptr1
+}
diff --git a/llvm/test/Instrumentation/AllocToken/ignore.ll b/llvm/test/Instrumentation/AllocToken/ignore.ll
new file mode 100644
index 0000000000000..65921685d70a0
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/ignore.ll
@@ -0,0 +1,30 @@
+; Test for all allocation functions that should be ignored by default.
+;
+; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s --check-prefixes=CHECK,DEFAULT
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-cover-strdup -S | FileCheck %s --check-prefixes=CHECK,COVER
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare ptr @strdup(ptr)
+declare ptr @__strdup(ptr)
+declare ptr @strndup(ptr, i64)
+declare ptr @__strndup(ptr, i64)
+
+; CHECK-LABEL: @test_ignorable_allocation_functions
+define ptr @test_ignorable_allocation_functions(ptr %ptr) sanitize_alloc_token {
+entry:
+  ; COVER:   call ptr @__alloc_token_strdup(
+  ; DEFAULT: call ptr @strdup(
+  %ptr1 = call ptr @strdup(ptr %ptr)
+  ; COVER:   call ptr @__alloc_token_strdup(
+  ; DEFAULT: call ptr @__strdup(
+  %ptr2 = call ptr @__strdup(ptr %ptr)
+  ; COVER:   call ptr @__alloc_token_strndup(
+  ; DEFAULT: call ptr @strndup(
+  %ptr3 = call ptr @strndup(ptr %ptr, i64 42)
+  ; COVER:   call ptr @__alloc_token_strndup(
+  ; DEFAULT: call ptr @__strndup(
+  %ptr4 = call ptr @__strndup(ptr %ptr, i64 42)
+  ret ptr %ptr1
+}
diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll
new file mode 100644
index 0000000000000..243462a54968f
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/invoke.ll
@@ -0,0 +1,86 @@
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; CHECK-LABEL: @test_invoke_malloc
+define ptr @test_invoke_malloc() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
+entry:
+  ; CHECK: invoke ptr @__alloc_token_malloc(i64 64, i64 0)
+  ; CHECK-NEXT: to label %normal unwind label %cleanup
+  ; CHECK-NOT: call ptr @__alloc_token_malloc
+  ; CHECK-NOT: call ptr @malloc
+  %ptr = invoke ptr @malloc(i64 64) to label %normal unwind label %cleanup
+
+normal:
+  ret ptr %ptr
+
+cleanup:
+  %lp = landingpad { ptr, i32 } cleanup
+  ret ptr null
+}
+
+; CHECK-LABEL: @test_invoke_operator_new
+define ptr @test_invoke_operator_new() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
+entry:
+  ; CHECK: invoke ptr @__alloc_token_Znwm(i64 32, i64 1)
+  ; CHECK-NEXT: to label %normal unwind label %cleanup
+  ; CHECK-NOT: call ptr @__alloc_token_Znwm
+  ; CHECK-NOT: call ptr @_Znwm
+  %ptr = invoke ptr @_Znwm(i64 32) to label %normal unwind label %cleanup
+
+normal:
+  ret ptr %ptr
+
+cleanup:
+  %lp = landingpad { ptr, i32 } cleanup
+  ret ptr null
+}
+
+; Test complex exception flow with multiple invoke allocations
+; CHECK-LABEL: @test_complex_invoke_flow
+define ptr @test_complex_invoke_flow() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
+entry:
+  ; CHECK: invoke ptr @__alloc_token_malloc(i64 16, i64 2)
+  ; CHECK-NEXT: to label %first_ok unwind label %cleanup1
+  %ptr1 = invoke ptr @malloc(i64 16) to label %first_ok unwind label %cleanup1
+
+first_ok:
+  ; CHECK: invoke ptr @__alloc_token_Znwm(i64 32, i64 3)
+  ; CHECK-NEXT: to label %second_ok unwind label %cleanup2
+  %ptr2 = invoke ptr @_Znwm(i64 32) to label %second_ok unwind label %cleanup2
+
+second_ok:
+  ret ptr %ptr1
+
+cleanup1:
+  %lp1 = landingpad { ptr, i32 } cleanup
+  ret ptr null
+
+cleanup2:
+  %lp2 = landingpad { ptr, i32 } cleanup
+  ret ptr null
+}
+
+; Test mixed call/invoke
+; CHECK-LABEL: @test_mixed_call_invoke
+define ptr @test_mixed_call_invoke() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
+entry:
+  ; CHECK: call ptr @__alloc_token_malloc(i64 8, i64 4)
+  %ptr1 = call ptr @malloc(i64 8)
+
+  ; CHECK: invoke ptr @__alloc_token_malloc(i64 16, i64 5)
+  ; CHECK-NEXT: to label %normal unwind label %cleanup
+  %ptr2 = invoke ptr @malloc(i64 16) to label %normal unwind label %cleanup
+
+normal:
+  ret ptr %ptr1
+
+cleanup:
+  %lp = landingpad { ptr, i32 } cleanup
+  ret ptr null
+}
+
+declare ptr @malloc(i64)
+declare ptr @_Znwm(i64)
+declare i32 @__gxx_personality_v0(...)
diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
new file mode 100644
index 0000000000000..b933c0bd352e9
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
@@ -0,0 +1,63 @@
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -alloc-token-extended -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare ptr @malloc(i64)
+declare ptr @custom_malloc(i64)
+declare ptr @kmalloc(i64, i64)
+
+; CHECK-LABEL: @test_libcall
+define ptr @test_libcall() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 0)
+  %ptr1 = call ptr @malloc(i64 64)
+  ret ptr %ptr1
+}
+
+; CHECK-LABEL: @test_libcall_hint
+define ptr @test_libcall_hint() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 1)
+  %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0
+  ret ptr %ptr1
+}
+
+; CHECK-LABEL: @test_nonlibcall_nohint
+define ptr @test_nonlibcall_nohint() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @custom_malloc(i64 8)
+  ; CHECK: call ptr @kmalloc(i64 32, i64 0)
+  %ptr1 = call ptr @custom_malloc(i64 8)
+  %ptr2 = call ptr @kmalloc(i64 32, i64 0)
+  ret ptr %ptr1
+}
+
+; CHECK-LABEL: @test_nonlibcall_hint
+define ptr @test_nonlibcall_hint() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_custom_malloc(i64 8, i64 2)
+  ; CHECK: call ptr @__alloc_token_kmalloc(i64 32, i64 0, i64 3)
+  ; CHECK: call ptr @__alloc_token_custom_malloc(i64 64, i64 4)
+  ; CHECK: call ptr @__alloc_token_kmalloc(i64 128, i64 2, i64 5)
+  %ptr1 = call ptr @custom_malloc(i64 8), !alloc_token_hint !0
+  %ptr2 = call ptr @kmalloc(i64 32, i64 0), !alloc_token_hint !0
+  %ptr3 = call ptr @custom_malloc(i64 64), !alloc_token_hint !0
+  %ptr4 = call ptr @kmalloc(i64 128, i64 2), !alloc_token_hint !0
+  ret ptr %ptr1
+}
+
+; Functions without sanitize_alloc_token do not get instrumented
+; CHECK-LABEL: @without_attribute
+define ptr @without_attribute() {
+entry:
+  ; CHECK: call ptr @malloc(i64 64)
+  ; CHECK: call ptr @custom_malloc(i64 8)
+  ; CHECK: call ptr @kmalloc(i64 32, i64 0)
+  %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0
+  %ptr2 = call ptr @custom_malloc(i64 8), !alloc_token_hint !0
+  %ptr3 = call ptr @kmalloc(i64 32, i64 0), !alloc_token_hint !0
+  ret ptr %ptr1
+}
+
+!0 = !{!"int", i1 0}
diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll
new file mode 100644
index 0000000000000..d2bc273060403
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/remark.ll
@@ -0,0 +1,27 @@
+; RUN: opt < %s -passes=inferattrs,alloc-token -pass-remarks=alloc-token -S 2>&1 | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+declare ptr @malloc(i64)
+
+; CHECK-NOT: remark: <unknown>:0:0: Call to 'malloc' in 'test_has_metadata' without source-level type token
+; CHECK: remark: <unknown>:0:0: Call to 'malloc' in 'test_no_metadata' without source-level type token
+
+; CHECK-LABEL: @test_has_metadata
+define ptr @test_has_metadata() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_malloc(
+  %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0
+  ret ptr %ptr1
+}
+
+; CHECK-LABEL: @test_no_metadata
+define ptr @test_no_metadata() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_malloc(
+  %ptr1 = call ptr @malloc(i64 32)
+  ret ptr %ptr1
+}
+
+!0 = !{!"int", i1 0}
diff --git a/llvm/test/Transforms/Inline/attributes.ll b/llvm/test/Transforms/Inline/attributes.ll
index 42b1a3a29aec4..55ab430f201d6 100644
--- a/llvm/test/Transforms/Inline/attributes.ll
+++ b/llvm/test/Transforms/Inline/attributes.ll
@@ -26,6 +26,10 @@ define i32 @sanitize_memtag_callee(i32 %i) sanitize_memtag {
   ret i32 %i
 }
 
+define i32 @sanitize_alloc_token_callee(i32 %i) sanitize_alloc_token {
+  ret i32 %i
+}
+
 define i32 @safestack_callee(i32 %i) safestack {
   ret i32 %i
 }
@@ -58,6 +62,10 @@ define i32 @alwaysinline_sanitize_memtag_callee(i32 %i) alwaysinline sanitize_me
   ret i32 %i
 }
 
+define i32 @alwaysinline_sanitize_alloc_token_callee(i32 %i) alwaysinline sanitize_alloc_token {
+  ret i32 %i
+}
+
 define i32 @alwaysinline_safestack_callee(i32 %i) alwaysinline safestack {
   ret i32 %i
 }
@@ -184,6 +192,39 @@ define i32 @test_sanitize_memtag(i32 %arg) sanitize_memtag {
 ; CHECK-NEXT: ret i32
 }
 
+; ---------------------------------------------------------------------------- ;
+
+; Can inline sanitize_alloc_token functions into a noattr function. The
+; attribute is *not* viral, otherwise may break code.
+define i32 @test_no_sanitize_alloc_token(i32 %arg) {
+; CHECK-LABEL: @test_no_sanitize_alloc_token(
+; CHECK-SAME: ) {
+; CHECK-NOT: call
+; CHECK: ret i32
+entry:
+  %x1 = call i32 @noattr_callee(i32 %arg)
+  %x2 = call i32 @sanitize_alloc_token_callee(i32 %x1)
+  %x3 = call i32 @alwaysinline_callee(i32 %x2)
+  %x4 = call i32 @alwaysinline_sanitize_alloc_token_callee(i32 %x3)
+  ret i32 %x4
+}
+
+; Can inline noattr functions into a sanitize_alloc_token function. If
+; inlinable noattr functions cannot be instrumented, they should be marked with
+; explicit noinline.
+define i32 @test_sanitize_alloc_token(i32 %arg) sanitize_alloc_token {
+; CHECK-LABEL: @test_sanitize_alloc_token(
+; CHECK-SAME: ) [[SANITIZE_ALLOC_TOKEN:.*]] {
+; CHECK-NOT: call
+; CHECK: ret i32
+entry:
+  %x1 = call i32 @noattr_callee(i32 %arg)
+  %x2 = call i32 @sanitize_alloc_token_callee(i32 %x1)
+  %x3 = call i32 @alwaysinline_callee(i32 %x2)
+  %x4 = call i32 @alwaysinline_sanitize_alloc_token_callee(i32 %x3)
+  ret i32 %x4
+}
+
 define i32 @test_safestack(i32 %arg) safestack {
   %x1 = call i32 @noattr_callee(i32 %arg)
   %x2 = call i32 @safestack_callee(i32 %x1)
@@ -639,6 +680,7 @@ define i32 @loader_replaceable_caller() {
   ret i32 %1
 }
 
+; CHECK: attributes [[SANITIZE_ALLOC_TOKEN]] = { sanitize_alloc_token }
 ; CHECK: attributes [[SLH]] = { speculative_load_hardening }
 ; CHECK: attributes [[FPMAD_FALSE]] = { "less-precise-fpmad"="false" }
 ; CHECK: attributes [[FPMAD_TRUE]] = { "less-precise-fpmad"="true" }
diff --git a/llvm/utils/emacs/llvm-mode.el b/llvm/utils/emacs/llvm-mode.el
index 660d0718f098c..240c13319f634 100644
--- a/llvm/utils/emacs/llvm-mode.el
+++ b/llvm/utils/emacs/llvm-mode.el
@@ -34,7 +34,7 @@
          "inaccessiblemem_or_argmemonly" "inalloca" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "nonnull" "nocapture"
          "nocallback" "nocf_check" "noduplicate" "noext" "nofree" "noimplicitfloat" "noinline" "nomerge" "nonlazybind" "noprofile" "noredzone" "noreturn"
          "norecurse" "nosync" "noundef" "nounwind" "nosanitize_bounds" "nosanitize_coverage" "null_pointer_is_valid" "optdebug" "optforfuzzing" "optnone" "optsize" "preallocated" "readnone" "readonly" "returned" "returns_twice"
-         "shadowcallstack" "signext" "speculatable" "speculative_load_hardening" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag"
+         "shadowcallstack" "signext" "speculatable" "speculative_load_hardening" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_alloc_token" "sanitize_hwaddress" "sanitize_memtag"
          "sanitize_thread" "sanitize_memory" "strictfp" "swifterror" "uwtable" "vscale_range" "willreturn" "writeonly" "zeroext") 'symbols) . font-lock-constant-face)
    ;; Variables
    '("%[-a-zA-Z$._][-a-zA-Z$._0-9]*" . font-lock-variable-name-face)
diff --git a/llvm/utils/gn/secondary/llvm/lib/Transforms/Instrumentation/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Transforms/Instrumentation/BUILD.gn
index a8eb834c1da23..2c6204e758559 100644
--- a/llvm/utils/gn/secondary/llvm/lib/Transforms/Instrumentation/BUILD.gn
+++ b/llvm/utils/gn/secondary/llvm/lib/Transforms/Instrumentation/BUILD.gn
@@ -11,6 +11,7 @@ static_library("Instrumentation") {
   ]
   sources = [
     "AddressSanitizer.cpp",
+    "AllocToken.cpp",
     "BlockCoverageInference.cpp",
     "BoundsChecking.cpp",
     "CGProfile.cpp",
diff --git a/llvm/utils/llvm.grm b/llvm/utils/llvm.grm
index 411323178bde1..dddfe3c301b65 100644
--- a/llvm/utils/llvm.grm
+++ b/llvm/utils/llvm.grm
@@ -173,6 +173,7 @@ FuncAttr      ::= noreturn
  | returns_twice
  | nonlazybind
  | sanitize_address
+ | sanitize_alloc_token
  | sanitize_thread
  | sanitize_memory
  | mustprogress
diff --git a/llvm/utils/vim/syntax/llvm.vim b/llvm/utils/vim/syntax/llvm.vim
index e3b8ff8629559..e048caa20406a 100644
--- a/llvm/utils/vim/syntax/llvm.vim
+++ b/llvm/utils/vim/syntax/llvm.vim
@@ -163,6 +163,7 @@ syn keyword llvmKeyword
       \ returns_twice
       \ safestack
       \ sanitize_address
+      \ sanitize_alloc_token
       \ sanitize_hwaddress
       \ sanitize_memory
       \ sanitize_memtag
diff --git a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml
index b64482336f404..1faaf6b26f301 100644
--- a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml
+++ b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml
@@ -258,6 +258,7 @@ patterns:
             \\breturns_twice\\b|\
             \\bsafestack\\b|\
             \\bsanitize_address\\b|\
+            \\bsanitize_alloc_token\\b|\
             \\bsanitize_hwaddress\\b|\
             \\bsanitize_memory\\b|\
             \\bsanitize_memtag\\b|\

>From f9a8b155e631a35fd42416a096e66f7538e97c15 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Thu, 4 Sep 2025 12:11:07 +0200
Subject: [PATCH 02/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/docs/index.rst | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/docs/index.rst b/clang/docs/index.rst
index be654af57f890..aa2b3a73dc11b 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -40,6 +40,7 @@ Using Clang as a Compiler
    SanitizerCoverage
    SanitizerStats
    SanitizerSpecialCaseList
+   AllocToken
    BoundsSafety
    BoundsSafetyAdoptionGuide
    BoundsSafetyImplPlans

>From 8da5f6353759bb89e306ad16e49baa1b39a8fa32 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Fri, 5 Sep 2025 13:19:27 +0200
Subject: [PATCH 03/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/lib/CodeGen/CGExpr.cpp                       | 2 +-
 llvm/include/llvm/IR/FixedMetadataKinds.def        | 1 +
 llvm/lib/Transforms/Instrumentation/AllocToken.cpp | 2 +-
 llvm/lib/Transforms/Utils/Local.cpp                | 4 ++++
 4 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 3edc1e898114f..288b41bc42203 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1339,7 +1339,7 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
   // Format: !{<type-name>, <contains-pointer>}
   auto *MDN =
       llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD});
-  CB->setMetadata("alloc_token_hint", MDN);
+  CB->setMetadata(llvm::LLVMContext::MD_alloc_token_hint, MDN);
 }
 
 CodeGenFunction::ComplexPairTy CodeGenFunction::
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index d09cc15d65ff6..a5a8a5663df06 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -55,3 +55,4 @@ LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40)
 LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41)
 LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
 LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
+LLVM_FIXED_MD_KIND(MD_alloc_token_hint, "alloc_token_hint", 44)
diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
index fc3beba4a86fd..74cda227d50a7 100644
--- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -149,7 +149,7 @@ STATISTIC(NumAllocations, "Allocations found");
 ///
 /// Expected format is: !{<type-name>, <contains-pointer>}
 MDNode *getAllocTokenHintMetadata(const CallBase &CB) {
-  MDNode *Ret = CB.getMetadata("alloc_token_hint");
+  MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token_hint);
   if (!Ret)
     return nullptr;
   assert(Ret->getNumOperands() == 2 && "bad !alloc_token_hint");
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index b94ed7db91580..108b3fdfa0252 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -3025,6 +3025,10 @@ static void combineMetadata(Instruction *K, const Instruction *J,
         // Preserve !nosanitize if both K and J have it.
         K->setMetadata(Kind, JMD);
         break;
+      case LLVMContext::MD_alloc_token_hint:
+        // Preserve !alloc_token_hint if both K and J have it.
+        K->setMetadata(Kind, JMD);
+        break;
       }
   }
   // Set !invariant.group from J if J has it. If both instructions have it

>From 85dc54d1b2b8ad936f02c94c2da2446639c416fa Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Mon, 8 Sep 2025 21:32:27 +0200
Subject: [PATCH 04/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/lib/CodeGen/CGExpr.cpp | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 288b41bc42203..455de644daf00 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1289,6 +1289,7 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
   // Check if QualType contains a pointer. Implements a simple DFS to
   // recursively check if a type contains a pointer type.
   llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD;
+  bool IncompleteType = false;
   auto TypeContainsPtr = [&](auto &&self, QualType T) -> bool {
     QualType CanonicalType = T.getCanonicalType();
     if (CanonicalType->isPointerType())
@@ -1312,6 +1313,10 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
       return self(self, AT->getElementType());
     // The type is a struct, class, or union.
     if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) {
+      if (!RD->isCompleteDefinition()) {
+        IncompleteType = true;
+        return false;
+      }
       if (!VisitedRD.insert(RD).second)
         return false; // already visited
       // Check all fields.
@@ -1333,6 +1338,8 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
     return false;
   };
   const bool ContainsPtr = TypeContainsPtr(TypeContainsPtr, AllocType);
+  if (!ContainsPtr && IncompleteType)
+    return nullptr;
   auto *ContainsPtrC = Builder.getInt1(ContainsPtr);
   auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC);
 

>From 6d9fc6a1f10de8c8757613820b2caf5bac5bb488 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Mon, 8 Sep 2025 21:58:07 +0200
Subject: [PATCH 05/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/lib/CodeGen/CGExpr.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 455de644daf00..e7a0e7696e204 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1339,7 +1339,7 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
   };
   const bool ContainsPtr = TypeContainsPtr(TypeContainsPtr, AllocType);
   if (!ContainsPtr && IncompleteType)
-    return nullptr;
+    return;
   auto *ContainsPtrC = Builder.getInt1(ContainsPtr);
   auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC);
 

>From 9ca8ddca339fdcd47d7a85f93e389358973691b7 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Thu, 18 Sep 2025 12:11:20 +0200
Subject: [PATCH 06/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/docs/AllocToken.rst                     |  38 +++---
 clang/include/clang/Driver/SanitizerArgs.h    |   1 +
 clang/lib/CodeGen/CGExpr.cpp                  | 106 +++++++++--------
 clang/lib/CodeGen/CGExprCXX.cpp               |   4 +-
 clang/lib/CodeGen/CodeGenFunction.h           |   2 +-
 clang/lib/Frontend/CompilerInvocation.cpp     |   5 +-
 clang/test/CodeGenCXX/alloc-token-pointer.cpp |  20 ++--
 clang/test/CodeGenCXX/alloc-token.cpp         |  14 +--
 llvm/docs/LangRef.rst                         |   9 +-
 llvm/docs/ReleaseNotes.md                     |   6 +-
 llvm/include/llvm/IR/FixedMetadataKinds.def   |   2 +-
 .../Transforms/Instrumentation/AllocToken.cpp | 110 +++++++++---------
 llvm/lib/Transforms/Utils/Local.cpp           |   4 +-
 llvm/test/Instrumentation/AllocToken/basic.ll |   2 +-
 .../Instrumentation/AllocToken/basic32.ll     |  25 ++++
 .../AllocToken/extralibfuncs.ll               |   6 +-
 llvm/test/Instrumentation/AllocToken/fast.ll  |   4 +-
 .../test/Instrumentation/AllocToken/invoke.ll |   2 +-
 .../Instrumentation/AllocToken/nonlibcalls.ll |  18 +--
 .../test/Instrumentation/AllocToken/remark.ll |   2 +-
 20 files changed, 207 insertions(+), 173 deletions(-)
 create mode 100644 llvm/test/Instrumentation/AllocToken/basic32.ll

diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst
index fb354d6738ea3..27c9dbd011231 100644
--- a/clang/docs/AllocToken.rst
+++ b/clang/docs/AllocToken.rst
@@ -31,23 +31,23 @@ Token Assignment Mode
 
 The default mode to calculate tokens is:
 
-* *TypeHashPointerSplit* (mode=3): This mode assigns a token ID based on
-  the hash of the allocated type's name, where the top half ID-space is
-  reserved for types that contain pointers and the bottom half for types that
-  do not contain pointers.
+* ``typehashpointersplit``: This mode assigns a token ID based on the hash of
+  the allocated type's name, where the top half ID-space is reserved for types
+  that contain pointers and the bottom half for types that do not contain
+  pointers.
 
 Other token ID assignment modes are supported, but they may be subject to
 change or removal. These may (experimentally) be selected with ``-mllvm
 -alloc-token-mode=<mode>``:
 
-* *TypeHash* (mode=2): This mode assigns a token ID based on the hash of
-  the allocated type's name.
+* ``typehash``: This mode assigns a token ID based on the hash of the allocated
+  type's name.
 
-* *Random* (mode=1): This mode assigns a statically-determined random token ID
-  to each allocation site.
+* ``random``: This mode assigns a statically-determined random token ID to each
+  allocation site.
 
-* *Increment* (mode=0): This mode assigns a simple, incrementally increasing
-  token ID to each allocation site.
+* ``increment``: This mode assigns a simple, incrementally increasing token ID
+  to each allocation site.
 
 Allocation Token Instrumentation
 ================================
@@ -74,7 +74,7 @@ In addition, it is typically recommended to configure the following:
 
 * ``-falloc-token-max=<N>``
     Configures the maximum number of tokens. No max by default (tokens bounded
-    by ``UINT64_MAX``).
+    by ``SIZE_MAX``).
 
     .. code-block:: console
 
@@ -85,21 +85,21 @@ Runtime Interface
 
 A compatible runtime must be provided that implements the token-enabled
 allocation functions. The instrumentation generates calls to functions that
-take a final ``uint64_t token_id`` argument.
+take a final ``size_t token_id`` argument.
 
 .. code-block:: c
 
     // C standard library functions
-    void *__alloc_token_malloc(size_t size, uint64_t token_id);
-    void *__alloc_token_calloc(size_t count, size_t size, uint64_t token_id);
-    void *__alloc_token_realloc(void *ptr, size_t size, uint64_t token_id);
+    void *__alloc_token_malloc(size_t size, size_t token_id);
+    void *__alloc_token_calloc(size_t count, size_t size, size_t token_id);
+    void *__alloc_token_realloc(void *ptr, size_t size, size_t token_id);
     // ...
 
     // C++ operators (mangled names)
-    // operator new(size_t, uint64_t)
-    void *__alloc_token_Znwm(size_t size, uint64_t token_id);
-    // operator new[](size_t, uint64_t)
-    void *__alloc_token_Znam(size_t size, uint64_t token_id);
+    // operator new(size_t, size_t)
+    void *__alloc_token_Znwm(size_t size, size_t token_id);
+    // operator new[](size_t, size_t)
+    void *__alloc_token_Znam(size_t size, size_t token_id);
     // ... other variants like nothrow, etc., are also instrumented.
 
 Fast ABI
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index ed51009654d4e..e0328bd6ed8c3 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -74,6 +74,7 @@ class SanitizerArgs {
   bool HwasanUseAliases = false;
   llvm::AsanDetectStackUseAfterReturnMode AsanUseAfterReturn =
       llvm::AsanDetectStackUseAfterReturnMode::Invalid;
+
   std::string MemtagMode;
   bool AllocTokenFastABI = false;
   bool AllocTokenExtended = false;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index e7a0e7696e204..4556a50c5a59c 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1272,8 +1272,58 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound,
   EmitCheck(std::make_pair(Check, CheckKind), CheckHandler, StaticData, Index);
 }
 
-void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
-                                         QualType AllocType) {
+static bool
+typeContainsPointer(QualType T,
+                    llvm::SmallPtrSet<const RecordDecl *, 4> &VisitedRD,
+                    bool &IncompleteType) {
+  QualType CanonicalType = T.getCanonicalType();
+  if (CanonicalType->isPointerType())
+    return true; // base case
+
+  // Look through typedef chain to check for special types.
+  for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>();
+       CurrentT = TT->getDecl()->getUnderlyingType()) {
+    const IdentifierInfo *II = TT->getDecl()->getIdentifier();
+    if (!II)
+      continue;
+    // Special Case: Syntactically uintptr_t is not a pointer; semantically,
+    // however, very likely used as such. Therefore, classify uintptr_t as a
+    // pointer, too.
+    if (II->isStr("uintptr_t"))
+      return true;
+  }
+
+  // The type is an array; check the element type.
+  if (const ArrayType *AT = CanonicalType->getAsArrayTypeUnsafe())
+    return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType);
+  // The type is a struct, class, or union.
+  if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) {
+    if (!RD->isCompleteDefinition()) {
+      IncompleteType = true;
+      return false;
+    }
+    if (!VisitedRD.insert(RD).second)
+      return false; // already visited
+    // Check all fields.
+    for (const FieldDecl *Field : RD->fields()) {
+      if (typeContainsPointer(Field->getType(), VisitedRD, IncompleteType))
+        return true;
+    }
+    // For C++ classes, also check base classes.
+    if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+      // Polymorphic types require a vptr.
+      if (CXXRD->isPolymorphic())
+        return true;
+      for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
+        if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType))
+          return true;
+      }
+    }
+  }
+  return false;
+}
+
+void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) {
   assert(SanOpts.has(SanitizerKind::AllocToken) &&
          "Only needed with -fsanitize=alloc-token");
 
@@ -1290,54 +1340,8 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
   // recursively check if a type contains a pointer type.
   llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD;
   bool IncompleteType = false;
-  auto TypeContainsPtr = [&](auto &&self, QualType T) -> bool {
-    QualType CanonicalType = T.getCanonicalType();
-    if (CanonicalType->isPointerType())
-      return true; // base case
-
-    // Look through typedef chain to check for special types.
-    for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>();
-         CurrentT = TT->getDecl()->getUnderlyingType()) {
-      const IdentifierInfo *II = TT->getDecl()->getIdentifier();
-      if (!II)
-        continue;
-      // Special Case: Syntactically uintptr_t is not a pointer; semantically,
-      // however, very likely used as such. Therefore, classify uintptr_t as a
-      // pointer, too.
-      if (II->isStr("uintptr_t"))
-        return true;
-    }
-
-    // The type is an array; check the element type.
-    if (const ArrayType *AT = CanonicalType->getAsArrayTypeUnsafe())
-      return self(self, AT->getElementType());
-    // The type is a struct, class, or union.
-    if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) {
-      if (!RD->isCompleteDefinition()) {
-        IncompleteType = true;
-        return false;
-      }
-      if (!VisitedRD.insert(RD).second)
-        return false; // already visited
-      // Check all fields.
-      for (const FieldDecl *Field : RD->fields()) {
-        if (self(self, Field->getType()))
-          return true;
-      }
-      // For C++ classes, also check base classes.
-      if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
-        // Polymorphic types require a vptr.
-        if (CXXRD->isPolymorphic())
-          return true;
-        for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
-          if (self(self, Base.getType()))
-            return true;
-        }
-      }
-    }
-    return false;
-  };
-  const bool ContainsPtr = TypeContainsPtr(TypeContainsPtr, AllocType);
+  const bool ContainsPtr =
+      typeContainsPointer(AllocType, VisitedRD, IncompleteType);
   if (!ContainsPtr && IncompleteType)
     return;
   auto *ContainsPtrC = Builder.getInt1(ContainsPtr);
@@ -1346,7 +1350,7 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB,
   // Format: !{<type-name>, <contains-pointer>}
   auto *MDN =
       llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD});
-  CB->setMetadata(llvm::LLVMContext::MD_alloc_token_hint, MDN);
+  CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN);
 }
 
 CodeGenFunction::ComplexPairTy CodeGenFunction::
diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp
index 6bf3332b425fa..9b3fe1612cbcc 100644
--- a/clang/lib/CodeGen/CGExprCXX.cpp
+++ b/clang/lib/CodeGen/CGExprCXX.cpp
@@ -1713,8 +1713,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) {
         CGDI->addHeapAllocSiteMetadata(newCall, allocType, E->getExprLoc());
       }
       if (SanOpts.has(SanitizerKind::AllocToken)) {
-        // Set !alloc_token_hint metadata.
-        EmitAllocTokenHint(newCall, allocType);
+        // Set !alloc_token metadata.
+        EmitAllocToken(newCall, allocType);
       }
     }
 
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index fd7ec36183c2d..e17e1e616c024 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3349,7 +3349,7 @@ class CodeGenFunction : public CodeGenTypeCache {
                              SanitizerHandler Handler);
 
   /// Emit additional metadata used by the AllocToken instrumentation.
-  void EmitAllocTokenHint(llvm::CallBase *CB, QualType AllocType);
+  void EmitAllocToken(llvm::CallBase *CB, QualType AllocType);
 
   llvm::Value *GetCountedByFieldExprGEP(const Expr *Base, const FieldDecl *FD,
                                         const FieldDecl *CountDecl);
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index eab2da4ddcc98..06280fd2fc381 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -2374,11 +2374,10 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
   if (const auto *Arg = Args.getLastArg(options::OPT_falloc_token_max_EQ)) {
     StringRef S = Arg->getValue();
     uint64_t Value = 0;
-    if (S.getAsInteger(0, Value)) {
+    if (S.getAsInteger(0, Value))
       Diags.Report(diag::err_drv_invalid_value) << Arg->getAsString(Args) << S;
-    } else {
+    else
       Opts.AllocTokenMax = Value;
-    }
   }
 
   Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
diff --git a/clang/test/CodeGenCXX/alloc-token-pointer.cpp b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
index 7adb75c7afebb..e8019a59c1bf2 100644
--- a/clang/test/CodeGenCXX/alloc-token-pointer.cpp
+++ b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
@@ -31,25 +31,25 @@ int **test_malloc_ptr() {
 
 // CHECK-LABEL: @_Z12test_new_intv(
 int *test_new_int() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0){{.*}} !alloc_token
   return new int;
 }
 
 // CHECK-LABEL: @_Z20test_new_ulong_arrayv(
 unsigned long *test_new_ulong_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 80, i64 0){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 80, i64 0){{.*}} !alloc_token
   return new unsigned long[10];
 }
 
 // CHECK-LABEL: @_Z12test_new_ptrv(
 int **test_new_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 8, i64 1){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 8, i64 1){{.*}} !alloc_token
   return new int*;
 }
 
 // CHECK-LABEL: @_Z18test_new_ptr_arrayv(
 int **test_new_ptr_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 80, i64 1){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 80, i64 1){{.*}} !alloc_token
   return new int*[10];
 }
 
@@ -108,13 +108,13 @@ ContainsPtr *test_operatornew_struct_array_with_ptr2() {
 
 // CHECK-LABEL: @_Z24test_new_struct_with_ptrv(
 ContainsPtr *test_new_struct_with_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 1){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 1){{.*}} !alloc_token
   return new ContainsPtr;
 }
 
 // CHECK-LABEL: @_Z30test_new_struct_array_with_ptrv(
 ContainsPtr *test_new_struct_array_with_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 160, i64 1){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 160, i64 1){{.*}} !alloc_token
   return new ContainsPtr[10];
 }
 
@@ -127,13 +127,13 @@ class TestClass {
 
 // CHECK-LABEL: @_Z14test_new_classv(
 TestClass *test_new_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 64, i64 0){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 64, i64 0){{.*}} !alloc_token
   return new TestClass();
 }
 
 // CHECK-LABEL: @_Z20test_new_class_arrayv(
 TestClass *test_new_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 648, i64 0){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 648, i64 0){{.*}} !alloc_token
   return new TestClass[10];
 }
 
@@ -147,13 +147,13 @@ class VirtualTestClass {
 
 // CHECK-LABEL: @_Z22test_new_virtual_classv(
 VirtualTestClass *test_new_virtual_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 1){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 1){{.*}} !alloc_token
   return new VirtualTestClass();
 }
 
 // CHECK-LABEL: @_Z28test_new_virtual_class_arrayv(
 VirtualTestClass *test_new_virtual_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 1){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 1){{.*}} !alloc_token
   return new VirtualTestClass[10];
 }
 
diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp
index 180c771e43ae9..b8f0ed660bc67 100644
--- a/clang/test/CodeGenCXX/alloc-token.cpp
+++ b/clang/test/CodeGenCXX/alloc-token.cpp
@@ -69,25 +69,25 @@ void test_operator_new_nothrow() {
 
 // CHECK-LABEL: @_Z8test_newv(
 int *test_new() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new int;
 }
 
 // CHECK-LABEL: @_Z14test_new_arrayv(
 int *test_new_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new int[10];
 }
 
 // CHECK-LABEL: @_Z16test_new_nothrowv(
 int *test_new_nothrow() {
-  // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new (std::nothrow) int;
 }
 
 // CHECK-LABEL: @_Z22test_new_array_nothrowv(
 int *test_new_array_nothrow() {
-  // CHECK: call {{.*}} ptr @__alloc_token_ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new (std::nothrow) int[10];
 }
 
@@ -123,7 +123,7 @@ void may_throw();
 TestClass *test_exception_handling_new() {
   try {
     // CHECK: invoke {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}})
-    // CHECK-NEXT: !alloc_token_hint
+    // CHECK-NEXT: !alloc_token
     TestClass *obj = new TestClass();
     may_throw();
     return obj;
@@ -134,7 +134,7 @@ TestClass *test_exception_handling_new() {
 
 // CHECK-LABEL: @_Z14test_new_classv(
 TestClass *test_new_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   TestClass *obj = new TestClass();
   obj->data[0] = 42;
   return obj;
@@ -142,7 +142,7 @@ TestClass *test_new_class() {
 
 // CHECK-LABEL: @_Z20test_new_class_arrayv(
 TestClass *test_new_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint
+  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   TestClass* arr = new TestClass[10];
   arr[0].data[0] = 123;
   return arr;
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 4791527a4b86b..1cf7ccc463b38 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2428,7 +2428,7 @@ For example:
     attributed with ``sanitize_realtime``.
     This attribute is incompatible with the ``sanitize_realtime`` attribute.
 ``sanitize_alloc_token``
-    This attributes indicates that implicit allocation token instrumentation
+    This attribute indicates that implicit allocation token instrumentation
     is enabled for this function.
 ``speculative_load_hardening``
     This attribute indicates that
@@ -8392,6 +8392,13 @@ Example:
 The ``nofree`` metadata indicates the memory pointed by the pointer will not be
 freed after the attached instruction.
 
+'``alloc_token``' Metadata
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``alloc_token`` metadata may be attached to calls to memory allocation
+functions, and contains richer semantic information about the type of the
+allocation. This information is consumed by the ``alloc-token`` pass to
+instrument such calls with allocation token IDs.
 
 Module Flags Metadata
 =====================
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index eae9a73bedc34..7e0bcbba666d4 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -166,9 +166,9 @@ Changes to Sanitizers
 Other Changes
 -------------
 
-* Introduces the `AllocToken` pass, an instrumentation pass designed to provide
-  tokens to memory allocators enabling various heap organization strategies,
-  such as heap partitioning.
+* Introduces the `AllocToken` pass, an instrumentation pass providing tokens to
+  memory allocators enabling various heap organization strategies, such as heap
+  partitioning.
 
 External Open Source Projects Using LLVM {{env.config.release}}
 ===============================================================
diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
index a5a8a5663df06..86a4d79afc2de 100644
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -55,4 +55,4 @@ LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40)
 LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41)
 LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
 LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
-LLVM_FIXED_MD_KIND(MD_alloc_token_hint, "alloc_token_hint", 44)
+LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 44)
diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
index 74cda227d50a7..95d533bddf5e5 100644
--- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -74,27 +74,25 @@ enum class TokenMode : unsigned {
   /// reserved for types that contain pointers and the bottom half for types
   /// that do not contain pointers.
   TypeHashPointerSplit = 3,
-
-  // Mode count - keep last
-  ModeCount
 };
 
 //===--- Command-line options ---------------------------------------------===//
 
-struct ModeParser : public cl::parser<unsigned> {
-  ModeParser(cl::Option &O) : cl::parser<unsigned>(O) {}
-  bool parse(cl::Option &O, StringRef ArgName, StringRef Arg, unsigned &Value) {
-    if (cl::parser<unsigned>::parse(O, ArgName, Arg, Value))
-      return true;
-    if (Value >= static_cast<unsigned>(TokenMode::ModeCount))
-      return O.error("'" + Arg + "' value invalid");
-    return false;
-  }
-};
-
-cl::opt<unsigned, false, ModeParser>
-    ClMode("alloc-token-mode", cl::desc("Token assignment mode"), cl::Hidden,
-           cl::init(static_cast<unsigned>(TokenMode::TypeHashPointerSplit)));
+cl::opt<TokenMode> ClMode(
+    "alloc-token-mode", cl::Hidden, cl::desc("Token assignment mode"),
+    cl::init(TokenMode::TypeHashPointerSplit),
+    cl::values(
+        clEnumValN(TokenMode::Increment, "increment",
+                   "Incrementally increasing token ID"),
+        clEnumValN(TokenMode::Random, "random",
+                   "Statically-assigned random token ID"),
+        clEnumValN(TokenMode::TypeHash, "typehash",
+                   "Token ID based on allocated type hash"),
+        clEnumValN(
+            TokenMode::TypeHashPointerSplit, "typehashpointersplit",
+            "Token ID based on allocated type hash, where the top half "
+            "ID-space is reserved for types that contain pointers and the "
+            "bottom half for types that do not contain pointers. ")));
 
 cl::opt<std::string> ClFuncPrefix("alloc-token-prefix",
                                   cl::desc("The allocation function prefix"),
@@ -111,7 +109,7 @@ cl::opt<bool>
 
 // Instrument libcalls only by default - compatible allocators only need to take
 // care of providing standard allocation functions. With extended coverage, also
-// instrument non-libcall allocation function calls with !alloc_token_hint
+// instrument non-libcall allocation function calls with !alloc_token
 // metadata.
 cl::opt<bool>
     ClExtended("alloc-token-extended",
@@ -145,14 +143,14 @@ STATISTIC(NumAllocations, "Allocations found");
 
 //===----------------------------------------------------------------------===//
 
-/// Returns the !alloc_token_hint metadata if available.
+/// Returns the !alloc_token metadata if available.
 ///
 /// Expected format is: !{<type-name>, <contains-pointer>}
-MDNode *getAllocTokenHintMetadata(const CallBase &CB) {
-  MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token_hint);
+MDNode *getAllocTokenMetadata(const CallBase &CB) {
+  MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token);
   if (!Ret)
     return nullptr;
-  assert(Ret->getNumOperands() == 2 && "bad !alloc_token_hint");
+  assert(Ret->getNumOperands() == 2 && "bad !alloc_token");
   assert(isa<MDString>(Ret->getOperand(0)));
   assert(isa<ConstantAsMetadata>(Ret->getOperand(1)));
   return Ret;
@@ -217,7 +215,7 @@ class TypeHashMode : public ModeBase {
 protected:
   std::pair<MDNode *, uint64_t> getHash(const CallBase &CB,
                                         OptimizationRemarkEmitter &ORE) {
-    if (MDNode *N = getAllocTokenHintMetadata(CB)) {
+    if (MDNode *N = getAllocTokenMetadata(CB)) {
       MDString *S = cast<MDString>(N->getOperand(0));
       return {N, xxHash64(S->getString())};
     }
@@ -232,7 +230,7 @@ class TypeHashMode : public ModeBase {
       ore::NV FuncNV("Function", CB.getParent()->getParent());
       const Function *Callee = CB.getCalledFunction();
       ore::NV CalleeNV("Callee", Callee ? Callee->getName() : "<unknown>");
-      return OptimizationRemark(DEBUG_TYPE, "NoAllocTokenHint", &CB)
+      return OptimizationRemark(DEBUG_TYPE, "NoAllocToken", &CB)
              << "Call to '" << CalleeNV << "' in '" << FuncNV
              << "' without source-level type token";
     });
@@ -275,7 +273,7 @@ class AllocToken {
       : Options(transformOptionsFromCl(std::move(Opts))), Mod(M),
         FAM(MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager()),
         Mode(IncrementMode(*Options.MaxTokens)) {
-    switch (static_cast<TokenMode>(ClMode.getValue())) {
+    switch (ClMode.getValue()) {
     case TokenMode::Increment:
       break;
     case TokenMode::Random:
@@ -287,9 +285,6 @@ class AllocToken {
     case TokenMode::TypeHashPointerSplit:
       Mode.emplace<TypeHashPointerSplitMode>(*Options.MaxTokens);
       break;
-    case TokenMode::ModeCount:
-      llvm_unreachable("");
-      break;
     }
   }
 
@@ -298,10 +293,11 @@ class AllocToken {
 private:
   /// Returns true for !isAllocationFn() functions that are also eligible for
   /// instrumentation.
-  bool isInstrumentableLibFunc(LibFunc Func) const;
+  static bool isInstrumentableLibFunc(LibFunc Func, const Value *V,
+                                      const TargetLibraryInfo *TLI);
 
   /// Returns true for isAllocationFn() functions that we should ignore.
-  bool ignoreInstrumentableLibFunc(LibFunc Func) const;
+  static bool ignoreInstrumentableLibFunc(LibFunc Func);
 
   /// Replace a call/invoke with a call/invoke to the allocation function
   /// with token ID.
@@ -320,6 +316,7 @@ class AllocToken {
 
   const AllocTokenOptions Options;
   Module &Mod;
+  IntegerType *IntPtrTy = Mod.getDataLayout().getIntPtrType(Mod.getContext());
   FunctionAnalysisManager &FAM;
   // Cache for replacement functions.
   DenseMap<std::pair<LibFunc, uint64_t>, FunctionCallee> TokenAllocFunctions;
@@ -361,28 +358,29 @@ bool AllocToken::instrumentFunction(Function &F) {
     if (TLI.getLibFunc(*Callee, Func)) {
       if (ignoreInstrumentableLibFunc(Func))
         continue;
-      if (isInstrumentableLibFunc(Func) || isAllocationFn(CB, &TLI))
+      if (isInstrumentableLibFunc(Func, CB, &TLI))
         AllocCalls.emplace_back(CB, Func);
-    } else if (Options.Extended && getAllocTokenHintMetadata(*CB)) {
+    } else if (Options.Extended && getAllocTokenMetadata(*CB)) {
       AllocCalls.emplace_back(CB, NotLibFunc);
     }
   }
 
-  bool Modified = false;
+  if (AllocCalls.empty())
+    return false;
 
-  if (!AllocCalls.empty()) {
-    for (auto &[CB, Func] : AllocCalls) {
-      replaceAllocationCall(CB, Func, ORE, TLI);
-    }
-    NumAllocations += AllocCalls.size();
-    NumFunctionsInstrumented++;
-    Modified = true;
-  }
+  for (auto &[CB, Func] : AllocCalls)
+    replaceAllocationCall(CB, Func, ORE, TLI);
+  NumAllocations += AllocCalls.size();
+  NumFunctionsInstrumented++;
 
-  return Modified;
+  return true;
 }
 
-bool AllocToken::isInstrumentableLibFunc(LibFunc Func) const {
+bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const Value *V,
+                                         const TargetLibraryInfo *TLI) {
+  if (isAllocationFn(V, TLI))
+    return true;
+
   switch (Func) {
   case LibFunc_posix_memalign:
   case LibFunc_size_returning_new:
@@ -420,7 +418,7 @@ bool AllocToken::isInstrumentableLibFunc(LibFunc Func) const {
   }
 }
 
-bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) const {
+bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) {
   switch (Func) {
   case LibFunc_strdup:
   case LibFunc_dunder_strdup:
@@ -440,16 +438,17 @@ void AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func,
   FunctionCallee TokenAlloc = getTokenAllocFunction(*CB, TokenID, Func);
   if (!TokenAlloc)
     return;
+  if (Options.FastABI) {
+    assert(TokenAlloc.getFunctionType()->getNumParams() == CB->arg_size());
+    CB->setCalledFunction(TokenAlloc);
+    return;
+  }
 
   IRBuilder<> IRB(CB);
-
   // Original args.
   SmallVector<Value *, 4> NewArgs{CB->args()};
-  if (!Options.FastABI) {
-    // Add token ID.
-    NewArgs.push_back(
-        ConstantInt::get(Type::getInt64Ty(Mod.getContext()), TokenID));
-  }
+  // Add token ID.
+  NewArgs.push_back(ConstantInt::get(IntPtrTy, TokenID));
   assert(TokenAlloc.getFunctionType()->getNumParams() == NewArgs.size());
 
   // Preserve invoke vs call semantics for exception handling.
@@ -488,15 +487,13 @@ FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB,
   if (OldFTy->isVarArg())
     return FunctionCallee();
   // Copy params, and append token ID type.
-  LLVMContext &C = Mod.getContext();
   Type *RetTy = OldFTy->getReturnType();
   SmallVector<Type *, 4> NewParams{OldFTy->params()};
   std::string TokenAllocName = ClFuncPrefix;
-  if (Options.FastABI) {
+  if (Options.FastABI)
     TokenAllocName += utostr(TokenID) + "_";
-  } else {
-    NewParams.push_back(Type::getInt64Ty(C)); // token ID
-  }
+  else
+    NewParams.push_back(IntPtrTy); // token ID
   FunctionType *NewFTy = FunctionType::get(RetTy, NewParams, false);
   // Remove leading '_' - we add our own.
   StringRef No_ = Callee->getName().drop_while([](char C) { return C == '_'; });
@@ -525,5 +522,6 @@ PreservedAnalyses AllocTokenPass::run(Module &M, ModuleAnalysisManager &MAM) {
     Modified |= Pass.instrumentFunction(F);
   }
 
-  return Modified ? PreservedAnalyses::none() : PreservedAnalyses::all();
+  return Modified ? PreservedAnalyses::none().preserveSet<CFGAnalyses>()
+                  : PreservedAnalyses::all();
 }
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index 108b3fdfa0252..0649e70f4f5c8 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -3025,8 +3025,8 @@ static void combineMetadata(Instruction *K, const Instruction *J,
         // Preserve !nosanitize if both K and J have it.
         K->setMetadata(Kind, JMD);
         break;
-      case LLVMContext::MD_alloc_token_hint:
-        // Preserve !alloc_token_hint if both K and J have it.
+      case LLVMContext::MD_alloc_token:
+        // Preserve !alloc_token if both K and J have it.
         K->setMetadata(Kind, JMD);
         break;
       }
diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll
index 94f5ef7ac5511..0fdd2ced7b130 100644
--- a/llvm/test/Instrumentation/AllocToken/basic.ll
+++ b/llvm/test/Instrumentation/AllocToken/basic.ll
@@ -1,4 +1,4 @@
-; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -S | FileCheck %s
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
diff --git a/llvm/test/Instrumentation/AllocToken/basic32.ll b/llvm/test/Instrumentation/AllocToken/basic32.ll
new file mode 100644
index 0000000000000..a28a11d1b9367
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/basic32.ll
@@ -0,0 +1,25 @@
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
+
+target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
+target triple = "i386-pc-linux-gnu"
+
+declare ptr @malloc(i32)
+declare ptr @_Znwm(i32)
+
+; CHECK-LABEL: @test_basic_rewriting
+define ptr @test_basic_rewriting() sanitize_alloc_token {
+entry:
+  ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_malloc(i32 64, i32 0)
+  ; CHECK-NOT: call ptr @malloc(
+  %ptr1 = call ptr @malloc(i32 64)
+  ret ptr %ptr1
+}
+
+; CHECK-LABEL: @test_cpp_operators
+define ptr @test_cpp_operators() sanitize_alloc_token {
+entry:
+  ; CHECK: call ptr @__alloc_token_Znwm(i32 32, i32 1)
+  ; CHECK-NOT: call ptr @_Znwm(
+  %ptr1 = call ptr @_Znwm(i32 32)
+  ret ptr %ptr1
+}
diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
index 94fc12b20caff..2953a1bd53c7d 100644
--- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
+++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
@@ -11,7 +11,7 @@ declare {ptr, i64} @__size_returning_new(i64)
 define ptr @test_extra_libfuncs() sanitize_alloc_token {
 entry:
   ; CHECK: call {{.*}} @__alloc_token_size_returning_new(
-  %srn = call {ptr, i64} @__size_returning_new(i64 10), !alloc_token_hint !0
+  %srn = call {ptr, i64} @__size_returning_new(i64 10), !alloc_token !0
   %ptr1  = extractvalue {ptr, i64} %srn, 0
   ret ptr %ptr1
 }
@@ -23,9 +23,9 @@ declare ptr @_Znam(i64) nobuiltin allocsize(0)
 define ptr @test_replaceable_new() sanitize_alloc_token {
 entry:
   ; CHECK: call ptr @__alloc_token_Znwm(
-  %ptr1 = call ptr @_Znwm(i64 32), !alloc_token_hint !0
+  %ptr1 = call ptr @_Znwm(i64 32), !alloc_token !0
   ; CHECK: call ptr @__alloc_token_Znam(
-  %ptr2 = call ptr @_Znam(i64 64), !alloc_token_hint !0
+  %ptr2 = call ptr @_Znam(i64 64), !alloc_token !0
   ret ptr %ptr1
 }
 
diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll
index c691cdcdc37c6..140035d93a851 100644
--- a/llvm/test/Instrumentation/AllocToken/fast.ll
+++ b/llvm/test/Instrumentation/AllocToken/fast.ll
@@ -1,4 +1,4 @@
-; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
@@ -13,7 +13,7 @@ declare ptr @_Znam(i64)
 ; CHECK-LABEL: @test_basic_rewriting
 define ptr @test_basic_rewriting() sanitize_alloc_token {
 entry:
-  ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_0_malloc(i64 64)
+  ; CHECK: [[PTR1:%ptr[0-9]]] = call ptr @__alloc_token_0_malloc(i64 64)
   ; CHECK: call ptr @__alloc_token_1_calloc(i64 8, i64 8)
   ; CHECK: call ptr @__alloc_token_2_realloc(ptr [[PTR1]], i64 128)
   ; CHECK-NOT: call ptr @malloc(
diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll
index 243462a54968f..b1564e99adfde 100644
--- a/llvm/test/Instrumentation/AllocToken/invoke.ll
+++ b/llvm/test/Instrumentation/AllocToken/invoke.ll
@@ -1,4 +1,4 @@
-; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -S | FileCheck %s
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
index b933c0bd352e9..eb5bc3c2200fa 100644
--- a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
+++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
@@ -1,4 +1,4 @@
-; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -alloc-token-extended -S | FileCheck %s
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-extended -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
@@ -19,7 +19,7 @@ entry:
 define ptr @test_libcall_hint() sanitize_alloc_token {
 entry:
   ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 1)
-  %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0
+  %ptr1 = call ptr @malloc(i64 64), !alloc_token !0
   ret ptr %ptr1
 }
 
@@ -40,10 +40,10 @@ entry:
   ; CHECK: call ptr @__alloc_token_kmalloc(i64 32, i64 0, i64 3)
   ; CHECK: call ptr @__alloc_token_custom_malloc(i64 64, i64 4)
   ; CHECK: call ptr @__alloc_token_kmalloc(i64 128, i64 2, i64 5)
-  %ptr1 = call ptr @custom_malloc(i64 8), !alloc_token_hint !0
-  %ptr2 = call ptr @kmalloc(i64 32, i64 0), !alloc_token_hint !0
-  %ptr3 = call ptr @custom_malloc(i64 64), !alloc_token_hint !0
-  %ptr4 = call ptr @kmalloc(i64 128, i64 2), !alloc_token_hint !0
+  %ptr1 = call ptr @custom_malloc(i64 8), !alloc_token !0
+  %ptr2 = call ptr @kmalloc(i64 32, i64 0), !alloc_token !0
+  %ptr3 = call ptr @custom_malloc(i64 64), !alloc_token !0
+  %ptr4 = call ptr @kmalloc(i64 128, i64 2), !alloc_token !0
   ret ptr %ptr1
 }
 
@@ -54,9 +54,9 @@ entry:
   ; CHECK: call ptr @malloc(i64 64)
   ; CHECK: call ptr @custom_malloc(i64 8)
   ; CHECK: call ptr @kmalloc(i64 32, i64 0)
-  %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0
-  %ptr2 = call ptr @custom_malloc(i64 8), !alloc_token_hint !0
-  %ptr3 = call ptr @kmalloc(i64 32, i64 0), !alloc_token_hint !0
+  %ptr1 = call ptr @malloc(i64 64), !alloc_token !0
+  %ptr2 = call ptr @custom_malloc(i64 8), !alloc_token !0
+  %ptr3 = call ptr @kmalloc(i64 32, i64 0), !alloc_token !0
   ret ptr %ptr1
 }
 
diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll
index d2bc273060403..f087cbcc4ff60 100644
--- a/llvm/test/Instrumentation/AllocToken/remark.ll
+++ b/llvm/test/Instrumentation/AllocToken/remark.ll
@@ -12,7 +12,7 @@ declare ptr @malloc(i64)
 define ptr @test_has_metadata() sanitize_alloc_token {
 entry:
   ; CHECK: call ptr @__alloc_token_malloc(
-  %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0
+  %ptr1 = call ptr @malloc(i64 64), !alloc_token !0
   ret ptr %ptr1
 }
 

>From 14c47f8dae8930217d2bc05e871ac05fe8156501 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Fri, 19 Sep 2025 14:31:27 +0200
Subject: [PATCH 07/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/docs/AllocToken.rst                     |  3 +-
 clang/lib/CodeGen/CGExpr.cpp                  |  2 +-
 .../Transforms/Instrumentation/AllocToken.cpp | 43 ++++++++-----------
 .../test/Instrumentation/AllocToken/ignore.ll | 15 +++----
 4 files changed, 27 insertions(+), 36 deletions(-)

diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst
index 27c9dbd011231..62c641f0cfa0d 100644
--- a/clang/docs/AllocToken.rst
+++ b/clang/docs/AllocToken.rst
@@ -68,7 +68,7 @@ example:
     ptr = malloc(size);
 
     // Instrumented:
-    ptr = __alloc_token_malloc(size, token_id);
+    ptr = __alloc_token_malloc(size, <token id>);
 
 In addition, it is typically recommended to configure the following:
 
@@ -153,6 +153,7 @@ files or functions from instrumentation.
 
 .. code-block:: bash
 
+    [alloc-token]
     # Exclude specific source files
     src:third_party/allocator.c
     # Exclude function name patterns
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 4556a50c5a59c..28ae2c120edc9 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1294,7 +1294,7 @@ typeContainsPointer(QualType T,
   }
 
   // The type is an array; check the element type.
-  if (const ArrayType *AT = CanonicalType->getAsArrayTypeUnsafe())
+  if (const ArrayType *AT = dyn_cast<ArrayType>(CanonicalType))
     return typeContainsPointer(AT->getElementType(), VisitedRD, IncompleteType);
   // The type is a struct, class, or union.
   if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) {
diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
index 95d533bddf5e5..36ca518903b2f 100644
--- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -124,13 +124,6 @@ cl::opt<bool> ClCoverReplaceableNew("alloc-token-cover-replaceable-new",
                                     cl::desc("Cover replaceable operator new"),
                                     cl::Hidden, cl::init(true));
 
-// strdup-family functions only operate on strings, covering them does not make
-// sense in most cases.
-cl::opt<bool>
-    ClCoverStrdup("alloc-token-cover-strdup",
-                  cl::desc("Cover strdup-family allocation functions"),
-                  cl::Hidden, cl::init(false));
-
 cl::opt<uint64_t> ClFallbackToken(
     "alloc-token-fallback",
     cl::desc("The default fallback token where none could be determined"),
@@ -139,7 +132,7 @@ cl::opt<uint64_t> ClFallbackToken(
 //===--- Statistics -------------------------------------------------------===//
 
 STATISTIC(NumFunctionsInstrumented, "Functions instrumented");
-STATISTIC(NumAllocations, "Allocations found");
+STATISTIC(NumAllocationsInstrumented, "Allocations instrumented");
 
 //===----------------------------------------------------------------------===//
 
@@ -220,12 +213,13 @@ class TypeHashMode : public ModeBase {
       return {N, xxHash64(S->getString())};
     }
     // Fallback.
-    remarkNoHint(CB, ORE);
+    remarkNoMetadata(CB, ORE);
     return {nullptr, ClFallbackToken};
   }
 
   /// Remark that there was no precise type information.
-  void remarkNoHint(const CallBase &CB, OptimizationRemarkEmitter &ORE) {
+  static void remarkNoMetadata(const CallBase &CB,
+                               OptimizationRemarkEmitter &ORE) {
     ORE.emit([&] {
       ore::NV FuncNV("Function", CB.getParent()->getParent());
       const Function *Callee = CB.getCalledFunction();
@@ -258,12 +252,12 @@ class TypeHashPointerSplitMode : public TypeHashMode {
 };
 
 // Apply opt overrides.
-AllocTokenOptions &&transformOptionsFromCl(AllocTokenOptions &&Opts) {
+AllocTokenOptions transformOptionsFromCl(AllocTokenOptions Opts) {
   if (!Opts.MaxTokens.has_value())
     Opts.MaxTokens = ClMaxTokens;
   Opts.FastABI |= ClFastABI;
   Opts.Extended |= ClExtended;
-  return std::move(Opts);
+  return Opts;
 }
 
 class AllocToken {
@@ -301,7 +295,7 @@ class AllocToken {
 
   /// Replace a call/invoke with a call/invoke to the allocation function
   /// with token ID.
-  void replaceAllocationCall(CallBase *CB, LibFunc Func,
+  bool replaceAllocationCall(CallBase *CB, LibFunc Func,
                              OptimizationRemarkEmitter &ORE,
                              const TargetLibraryInfo &TLI);
 
@@ -365,15 +359,13 @@ bool AllocToken::instrumentFunction(Function &F) {
     }
   }
 
-  if (AllocCalls.empty())
-    return false;
-
+  bool Modified = false;
   for (auto &[CB, Func] : AllocCalls)
-    replaceAllocationCall(CB, Func, ORE, TLI);
-  NumAllocations += AllocCalls.size();
-  NumFunctionsInstrumented++;
+    Modified |= replaceAllocationCall(CB, Func, ORE, TLI);
 
-  return true;
+  if (Modified)
+    NumFunctionsInstrumented++;
+  return Modified;
 }
 
 bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const Value *V,
@@ -424,24 +416,26 @@ bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) {
   case LibFunc_dunder_strdup:
   case LibFunc_strndup:
   case LibFunc_dunder_strndup:
-    return !ClCoverStrdup;
+    return true;
   default:
     return false;
   }
 }
 
-void AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func,
+bool AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func,
                                        OptimizationRemarkEmitter &ORE,
                                        const TargetLibraryInfo &TLI) {
   uint64_t TokenID = getToken(*CB, ORE);
 
   FunctionCallee TokenAlloc = getTokenAllocFunction(*CB, TokenID, Func);
   if (!TokenAlloc)
-    return;
+    return false;
+  NumAllocationsInstrumented++;
+
   if (Options.FastABI) {
     assert(TokenAlloc.getFunctionType()->getNumParams() == CB->arg_size());
     CB->setCalledFunction(TokenAlloc);
-    return;
+    return true;
   }
 
   IRBuilder<> IRB(CB);
@@ -467,6 +461,7 @@ void AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func,
   // Replace all uses and delete the old call.
   CB->replaceAllUsesWith(NewCall);
   CB->eraseFromParent();
+  return true;
 }
 
 FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB,
diff --git a/llvm/test/Instrumentation/AllocToken/ignore.ll b/llvm/test/Instrumentation/AllocToken/ignore.ll
index 65921685d70a0..cc8ec4934e222 100644
--- a/llvm/test/Instrumentation/AllocToken/ignore.ll
+++ b/llvm/test/Instrumentation/AllocToken/ignore.ll
@@ -1,7 +1,6 @@
 ; Test for all allocation functions that should be ignored by default.
 ;
-; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s --check-prefixes=CHECK,DEFAULT
-; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-cover-strdup -S | FileCheck %s --check-prefixes=CHECK,COVER
+; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
@@ -14,17 +13,13 @@ declare ptr @__strndup(ptr, i64)
 ; CHECK-LABEL: @test_ignorable_allocation_functions
 define ptr @test_ignorable_allocation_functions(ptr %ptr) sanitize_alloc_token {
 entry:
-  ; COVER:   call ptr @__alloc_token_strdup(
-  ; DEFAULT: call ptr @strdup(
+  ; CHECK: call ptr @strdup(
   %ptr1 = call ptr @strdup(ptr %ptr)
-  ; COVER:   call ptr @__alloc_token_strdup(
-  ; DEFAULT: call ptr @__strdup(
+  ; CHECK: call ptr @__strdup(
   %ptr2 = call ptr @__strdup(ptr %ptr)
-  ; COVER:   call ptr @__alloc_token_strndup(
-  ; DEFAULT: call ptr @strndup(
+  ; CHECK: call ptr @strndup(
   %ptr3 = call ptr @strndup(ptr %ptr, i64 42)
-  ; COVER:   call ptr @__alloc_token_strndup(
-  ; DEFAULT: call ptr @__strndup(
+  ; CHECK: call ptr @__strndup(
   %ptr4 = call ptr @__strndup(ptr %ptr, i64 42)
   ret ptr %ptr1
 }

>From 0e30e56237ead42aa4d07c04db31f5c34efa260a Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Mon, 22 Sep 2025 16:51:50 +0200
Subject: [PATCH 08/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 llvm/test/Instrumentation/AllocToken/basic.ll | 65 ++++++++++------
 .../Instrumentation/AllocToken/basic32.ll     | 19 +++--
 .../AllocToken/extralibfuncs.ll               | 23 ++++--
 llvm/test/Instrumentation/AllocToken/fast.ll  | 28 ++++---
 .../test/Instrumentation/AllocToken/ignore.ll | 15 ++--
 .../test/Instrumentation/AllocToken/invoke.ll | 76 ++++++++++++++-----
 .../Instrumentation/AllocToken/nonlibcalls.ll | 55 ++++++++++----
 .../test/Instrumentation/AllocToken/remark.ll | 20 ++++-
 8 files changed, 209 insertions(+), 92 deletions(-)

diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll
index 0fdd2ced7b130..fe673ca9fd524 100644
--- a/llvm/test/Instrumentation/AllocToken/basic.ll
+++ b/llvm/test/Instrumentation/AllocToken/basic.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
@@ -13,15 +14,16 @@ declare void @_ZdlPv(ptr)
 declare i32 @foobar(i64)
 
 ; Test basic allocation call rewriting
-; CHECK-LABEL: @test_basic_rewriting
 define ptr @test_basic_rewriting() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_basic_rewriting(
+; CHECK-SAME: ) #[[ATTR5:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 0)
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token_calloc(i64 8, i64 8, i64 1)
+; CHECK-NEXT:    [[TMP2:%.*]] = call ptr @__alloc_token_realloc(ptr [[TMP0]], i64 128, i64 2)
+; CHECK-NEXT:    ret ptr [[TMP2]]
+;
 entry:
-  ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_malloc(i64 64, i64 0)
-  ; CHECK: call ptr @__alloc_token_calloc(i64 8, i64 8, i64 1)
-  ; CHECK: call ptr @__alloc_token_realloc(ptr [[PTR1]], i64 128, i64 2)
-  ; CHECK-NOT: call ptr @malloc(
-  ; CHECK-NOT: call ptr @calloc(
-  ; CHECK-NOT: call ptr @realloc(
   %ptr1 = call ptr @malloc(i64 64)
   %ptr2 = call ptr @calloc(i64 8, i64 8)
   %ptr3 = call ptr @realloc(ptr %ptr1, i64 128)
@@ -29,56 +31,69 @@ entry:
 }
 
 ; Test C++ operator rewriting
-; CHECK-LABEL: @test_cpp_operators
 define ptr @test_cpp_operators() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_cpp_operators(
+; CHECK-SAME: ) #[[ATTR5]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i64 32, i64 3)
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token_Znam(i64 64, i64 4)
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_Znwm(i64 32, i64 3)
-  ; CHECK: call ptr @__alloc_token_Znam(i64 64, i64 4)
-  ; CHECK-NOT: call ptr @_Znwm(
-  ; CHECK-NOT: call ptr @_Znam(
   %ptr1 = call ptr @_Znwm(i64 32)
   %ptr2 = call ptr @_Znam(i64 64)
   ret ptr %ptr1
 }
 
 ; Functions without sanitize_alloc_token do not get instrumented
-; CHECK-LABEL: @without_attribute
 define ptr @without_attribute() {
+; CHECK-LABEL: define ptr @without_attribute() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[PTR:%.*]] = call ptr @malloc(i64 16)
+; CHECK-NEXT:    ret ptr [[PTR]]
+;
 entry:
-  ; CHECK: call ptr @malloc(i64 16)
-  ; CHECK-NOT: call ptr @__alloc_token_malloc
   %ptr = call ptr @malloc(i64 16)
   ret ptr %ptr
 }
 
 ; Test that free/delete are untouched
-; CHECK-LABEL: @test_free_untouched
 define void @test_free_untouched(ptr %ptr) sanitize_alloc_token {
+; CHECK-LABEL: define void @test_free_untouched(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    call void @free(ptr [[PTR]])
+; CHECK-NEXT:    call void @_ZdlPv(ptr [[PTR]])
+; CHECK-NEXT:    ret void
+;
 entry:
-  ; CHECK: call void @free(ptr %ptr)
-  ; CHECK: call void @_ZdlPv(ptr %ptr)
-  ; CHECK-NOT: call ptr @__alloc_token_
   call void @free(ptr %ptr)
   call void @_ZdlPv(ptr %ptr)
   ret void
 }
 
 ; Non-allocation functions are untouched
-; CHECK-LABEL: @no_allocations
 define i32 @no_allocations(i32 %x) sanitize_alloc_token {
+; CHECK-LABEL: define i32 @no_allocations(
+; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR5]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[RESULT:%.*]] = call i32 @foobar(i64 42)
+; CHECK-NEXT:    ret i32 [[RESULT]]
+;
 entry:
-  ; CHECK: call i32 @foobar
-  ; CHECK-NOT: call i32 @__alloc_token_
   %result = call i32 @foobar(i64 42)
   ret i32 %result
 }
 
 ; Test that tail calls are preserved
-; CHECK-LABEL: @test_tail_call_preserved
 define ptr @test_tail_call_preserved() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_tail_call_preserved(
+; CHECK-SAME: ) #[[ATTR5]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = tail call ptr @__alloc_token_malloc(i64 42, i64 5)
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: tail call ptr @__alloc_token_malloc(i64 42, i64 5)
-  ; CHECK-NOT: tail call ptr @malloc(
   %result = tail call ptr @malloc(i64 42)
   ret ptr %result
 }
diff --git a/llvm/test/Instrumentation/AllocToken/basic32.ll b/llvm/test/Instrumentation/AllocToken/basic32.ll
index a28a11d1b9367..ed09fdbaf3d56 100644
--- a/llvm/test/Instrumentation/AllocToken/basic32.ll
+++ b/llvm/test/Instrumentation/AllocToken/basic32.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
 
 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
@@ -6,20 +7,26 @@ target triple = "i386-pc-linux-gnu"
 declare ptr @malloc(i32)
 declare ptr @_Znwm(i32)
 
-; CHECK-LABEL: @test_basic_rewriting
 define ptr @test_basic_rewriting() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_basic_rewriting(
+; CHECK-SAME: ) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i32 64, i32 0)
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_malloc(i32 64, i32 0)
-  ; CHECK-NOT: call ptr @malloc(
   %ptr1 = call ptr @malloc(i32 64)
   ret ptr %ptr1
 }
 
-; CHECK-LABEL: @test_cpp_operators
 define ptr @test_cpp_operators() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_cpp_operators(
+; CHECK-SAME: ) #[[ATTR2]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i32 32, i32 1)
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_Znwm(i32 32, i32 1)
-  ; CHECK-NOT: call ptr @_Znwm(
   %ptr1 = call ptr @_Znwm(i32 32)
   ret ptr %ptr1
 }
diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
index 2953a1bd53c7d..e472995997711 100644
--- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
+++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; Test for special libfuncs not automatically considered allocation functions.
 ;
 ; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s
@@ -7,10 +8,15 @@ target triple = "x86_64-unknown-linux-gnu"
 
 declare {ptr, i64} @__size_returning_new(i64)
 
-; CHECK-LABEL: @test_extra_libfuncs
 define ptr @test_extra_libfuncs() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_extra_libfuncs(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token_size_returning_new(i64 10, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]]
+; CHECK-NEXT:    [[PTR1:%.*]] = extractvalue { ptr, i64 } [[TMP0]], 0
+; CHECK-NEXT:    ret ptr [[PTR1]]
+;
 entry:
-  ; CHECK: call {{.*}} @__alloc_token_size_returning_new(
   %srn = call {ptr, i64} @__size_returning_new(i64 10), !alloc_token !0
   %ptr1  = extractvalue {ptr, i64} %srn, 0
   ret ptr %ptr1
@@ -19,14 +25,21 @@ entry:
 declare ptr @_Znwm(i64) nobuiltin allocsize(0)
 declare ptr @_Znam(i64) nobuiltin allocsize(0)
 
-; CHECK-LABEL: @test_replaceable_new
 define ptr @test_replaceable_new() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_replaceable_new(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i64 32, i64 6985720287680550851), !alloc_token [[META0]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token_Znam(i64 64, i64 6985720287680550851), !alloc_token [[META0]]
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_Znwm(
   %ptr1 = call ptr @_Znwm(i64 32), !alloc_token !0
-  ; CHECK: call ptr @__alloc_token_Znam(
   %ptr2 = call ptr @_Znam(i64 64), !alloc_token !0
   ret ptr %ptr1
 }
 
 !0 = !{!"int", i1 0}
+;.
+; CHECK: [[META0]] = !{!"int", i1 false}
+;.
diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll
index 140035d93a851..60169f308244f 100644
--- a/llvm/test/Instrumentation/AllocToken/fast.ll
+++ b/llvm/test/Instrumentation/AllocToken/fast.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
@@ -10,15 +11,16 @@ declare ptr @_Znwm(i64)
 declare ptr @_Znam(i64)
 
 ; Test basic allocation call rewriting
-; CHECK-LABEL: @test_basic_rewriting
 define ptr @test_basic_rewriting() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_basic_rewriting(
+; CHECK-SAME: ) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[PTR1:%.*]] = call ptr @__alloc_token_0_malloc(i64 64)
+; CHECK-NEXT:    [[PTR2:%.*]] = call ptr @__alloc_token_1_calloc(i64 8, i64 8)
+; CHECK-NEXT:    [[PTR3:%.*]] = call ptr @__alloc_token_2_realloc(ptr [[PTR1]], i64 128)
+; CHECK-NEXT:    ret ptr [[PTR3]]
+;
 entry:
-  ; CHECK: [[PTR1:%ptr[0-9]]] = call ptr @__alloc_token_0_malloc(i64 64)
-  ; CHECK: call ptr @__alloc_token_1_calloc(i64 8, i64 8)
-  ; CHECK: call ptr @__alloc_token_2_realloc(ptr [[PTR1]], i64 128)
-  ; CHECK-NOT: call ptr @malloc(
-  ; CHECK-NOT: call ptr @calloc(
-  ; CHECK-NOT: call ptr @realloc(
   %ptr1 = call ptr @malloc(i64 64)
   %ptr2 = call ptr @calloc(i64 8, i64 8)
   %ptr3 = call ptr @realloc(ptr %ptr1, i64 128)
@@ -26,13 +28,15 @@ entry:
 }
 
 ; Test C++ operator rewriting
-; CHECK-LABEL: @test_cpp_operators
 define ptr @test_cpp_operators() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_cpp_operators(
+; CHECK-SAME: ) #[[ATTR4]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[PTR1:%.*]] = call ptr @__alloc_token_0_Znwm(i64 32)
+; CHECK-NEXT:    [[PTR2:%.*]] = call ptr @__alloc_token_1_Znam(i64 64)
+; CHECK-NEXT:    ret ptr [[PTR1]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_0_Znwm(i64 32)
-  ; CHECK: call ptr @__alloc_token_1_Znam(i64 64)
-  ; CHECK-NOT: call ptr @_Znwm(
-  ; CHECK-NOT: call ptr @_Znam(
   %ptr1 = call ptr @_Znwm(i64 32)
   %ptr2 = call ptr @_Znam(i64 64)
   ret ptr %ptr1
diff --git a/llvm/test/Instrumentation/AllocToken/ignore.ll b/llvm/test/Instrumentation/AllocToken/ignore.ll
index cc8ec4934e222..ff8e85f87f64e 100644
--- a/llvm/test/Instrumentation/AllocToken/ignore.ll
+++ b/llvm/test/Instrumentation/AllocToken/ignore.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; Test for all allocation functions that should be ignored by default.
 ;
 ; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s
@@ -10,16 +11,20 @@ declare ptr @__strdup(ptr)
 declare ptr @strndup(ptr, i64)
 declare ptr @__strndup(ptr, i64)
 
-; CHECK-LABEL: @test_ignorable_allocation_functions
 define ptr @test_ignorable_allocation_functions(ptr %ptr) sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_ignorable_allocation_functions(
+; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[PTR1:%.*]] = call ptr @strdup(ptr [[PTR]])
+; CHECK-NEXT:    [[PTR2:%.*]] = call ptr @__strdup(ptr [[PTR]])
+; CHECK-NEXT:    [[PTR3:%.*]] = call ptr @strndup(ptr [[PTR]], i64 42)
+; CHECK-NEXT:    [[PTR4:%.*]] = call ptr @__strndup(ptr [[PTR]], i64 42)
+; CHECK-NEXT:    ret ptr [[PTR1]]
+;
 entry:
-  ; CHECK: call ptr @strdup(
   %ptr1 = call ptr @strdup(ptr %ptr)
-  ; CHECK: call ptr @__strdup(
   %ptr2 = call ptr @__strdup(ptr %ptr)
-  ; CHECK: call ptr @strndup(
   %ptr3 = call ptr @strndup(ptr %ptr, i64 42)
-  ; CHECK: call ptr @__strndup(
   %ptr4 = call ptr @__strndup(ptr %ptr, i64 42)
   ret ptr %ptr1
 }
diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll
index b1564e99adfde..8977ed67563c8 100644
--- a/llvm/test/Instrumentation/AllocToken/invoke.ll
+++ b/llvm/test/Instrumentation/AllocToken/invoke.ll
@@ -1,15 +1,23 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
 target triple = "x86_64-unknown-linux-gnu"
 
-; CHECK-LABEL: @test_invoke_malloc
 define ptr @test_invoke_malloc() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: define ptr @test_invoke_malloc(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = invoke ptr @__alloc_token_malloc(i64 64, i64 0)
+; CHECK-NEXT:            to label %[[NORMAL:.*]] unwind label %[[CLEANUP:.*]]
+; CHECK:       [[NORMAL]]:
+; CHECK-NEXT:    ret ptr [[TMP0]]
+; CHECK:       [[CLEANUP]]:
+; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; CHECK-NEXT:            cleanup
+; CHECK-NEXT:    ret ptr null
+;
 entry:
-  ; CHECK: invoke ptr @__alloc_token_malloc(i64 64, i64 0)
-  ; CHECK-NEXT: to label %normal unwind label %cleanup
-  ; CHECK-NOT: call ptr @__alloc_token_malloc
-  ; CHECK-NOT: call ptr @malloc
   %ptr = invoke ptr @malloc(i64 64) to label %normal unwind label %cleanup
 
 normal:
@@ -20,13 +28,20 @@ cleanup:
   ret ptr null
 }
 
-; CHECK-LABEL: @test_invoke_operator_new
 define ptr @test_invoke_operator_new() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: define ptr @test_invoke_operator_new(
+; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = invoke ptr @__alloc_token_Znwm(i64 32, i64 1)
+; CHECK-NEXT:            to label %[[NORMAL:.*]] unwind label %[[CLEANUP:.*]]
+; CHECK:       [[NORMAL]]:
+; CHECK-NEXT:    ret ptr [[TMP0]]
+; CHECK:       [[CLEANUP]]:
+; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; CHECK-NEXT:            cleanup
+; CHECK-NEXT:    ret ptr null
+;
 entry:
-  ; CHECK: invoke ptr @__alloc_token_Znwm(i64 32, i64 1)
-  ; CHECK-NEXT: to label %normal unwind label %cleanup
-  ; CHECK-NOT: call ptr @__alloc_token_Znwm
-  ; CHECK-NOT: call ptr @_Znwm
   %ptr = invoke ptr @_Znwm(i64 32) to label %normal unwind label %cleanup
 
 normal:
@@ -38,16 +53,30 @@ cleanup:
 }
 
 ; Test complex exception flow with multiple invoke allocations
-; CHECK-LABEL: @test_complex_invoke_flow
 define ptr @test_complex_invoke_flow() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: define ptr @test_complex_invoke_flow(
+; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = invoke ptr @__alloc_token_malloc(i64 16, i64 2)
+; CHECK-NEXT:            to label %[[FIRST_OK:.*]] unwind label %[[CLEANUP1:.*]]
+; CHECK:       [[FIRST_OK]]:
+; CHECK-NEXT:    [[TMP1:%.*]] = invoke ptr @__alloc_token_Znwm(i64 32, i64 3)
+; CHECK-NEXT:            to label %[[SECOND_OK:.*]] unwind label %[[CLEANUP2:.*]]
+; CHECK:       [[SECOND_OK]]:
+; CHECK-NEXT:    ret ptr [[TMP0]]
+; CHECK:       [[CLEANUP1]]:
+; CHECK-NEXT:    [[LP1:%.*]] = landingpad { ptr, i32 }
+; CHECK-NEXT:            cleanup
+; CHECK-NEXT:    ret ptr null
+; CHECK:       [[CLEANUP2]]:
+; CHECK-NEXT:    [[LP2:%.*]] = landingpad { ptr, i32 }
+; CHECK-NEXT:            cleanup
+; CHECK-NEXT:    ret ptr null
+;
 entry:
-  ; CHECK: invoke ptr @__alloc_token_malloc(i64 16, i64 2)
-  ; CHECK-NEXT: to label %first_ok unwind label %cleanup1
   %ptr1 = invoke ptr @malloc(i64 16) to label %first_ok unwind label %cleanup1
 
 first_ok:
-  ; CHECK: invoke ptr @__alloc_token_Znwm(i64 32, i64 3)
-  ; CHECK-NEXT: to label %second_ok unwind label %cleanup2
   %ptr2 = invoke ptr @_Znwm(i64 32) to label %second_ok unwind label %cleanup2
 
 second_ok:
@@ -63,14 +92,23 @@ cleanup2:
 }
 
 ; Test mixed call/invoke
-; CHECK-LABEL: @test_mixed_call_invoke
 define ptr @test_mixed_call_invoke() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
+; CHECK-LABEL: define ptr @test_mixed_call_invoke(
+; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 8, i64 4)
+; CHECK-NEXT:    [[TMP1:%.*]] = invoke ptr @__alloc_token_malloc(i64 16, i64 5)
+; CHECK-NEXT:            to label %[[NORMAL:.*]] unwind label %[[CLEANUP:.*]]
+; CHECK:       [[NORMAL]]:
+; CHECK-NEXT:    ret ptr [[TMP0]]
+; CHECK:       [[CLEANUP]]:
+; CHECK-NEXT:    [[LP:%.*]] = landingpad { ptr, i32 }
+; CHECK-NEXT:            cleanup
+; CHECK-NEXT:    ret ptr null
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_malloc(i64 8, i64 4)
   %ptr1 = call ptr @malloc(i64 8)
 
-  ; CHECK: invoke ptr @__alloc_token_malloc(i64 16, i64 5)
-  ; CHECK-NEXT: to label %normal unwind label %cleanup
   %ptr2 = invoke ptr @malloc(i64 16) to label %normal unwind label %cleanup
 
 normal:
diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
index eb5bc3c2200fa..769f570ed35d2 100644
--- a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
+++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-extended -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
@@ -7,39 +8,55 @@ declare ptr @malloc(i64)
 declare ptr @custom_malloc(i64)
 declare ptr @kmalloc(i64, i64)
 
-; CHECK-LABEL: @test_libcall
 define ptr @test_libcall() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_libcall(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 0)
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 0)
   %ptr1 = call ptr @malloc(i64 64)
   ret ptr %ptr1
 }
 
-; CHECK-LABEL: @test_libcall_hint
 define ptr @test_libcall_hint() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_libcall_hint(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 1), !alloc_token [[META0:![0-9]+]]
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 1)
   %ptr1 = call ptr @malloc(i64 64), !alloc_token !0
   ret ptr %ptr1
 }
 
-; CHECK-LABEL: @test_nonlibcall_nohint
 define ptr @test_nonlibcall_nohint() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_nonlibcall_nohint(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[PTR1:%.*]] = call ptr @custom_malloc(i64 8)
+; CHECK-NEXT:    [[PTR2:%.*]] = call ptr @kmalloc(i64 32, i64 0)
+; CHECK-NEXT:    ret ptr [[PTR1]]
+;
 entry:
-  ; CHECK: call ptr @custom_malloc(i64 8)
-  ; CHECK: call ptr @kmalloc(i64 32, i64 0)
   %ptr1 = call ptr @custom_malloc(i64 8)
   %ptr2 = call ptr @kmalloc(i64 32, i64 0)
   ret ptr %ptr1
 }
 
-; CHECK-LABEL: @test_nonlibcall_hint
 define ptr @test_nonlibcall_hint() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_nonlibcall_hint(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_custom_malloc(i64 8, i64 2), !alloc_token [[META0]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token_kmalloc(i64 32, i64 0, i64 3), !alloc_token [[META0]]
+; CHECK-NEXT:    [[TMP2:%.*]] = call ptr @__alloc_token_custom_malloc(i64 64, i64 4), !alloc_token [[META0]]
+; CHECK-NEXT:    [[TMP3:%.*]] = call ptr @__alloc_token_kmalloc(i64 128, i64 2, i64 5), !alloc_token [[META0]]
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_custom_malloc(i64 8, i64 2)
-  ; CHECK: call ptr @__alloc_token_kmalloc(i64 32, i64 0, i64 3)
-  ; CHECK: call ptr @__alloc_token_custom_malloc(i64 64, i64 4)
-  ; CHECK: call ptr @__alloc_token_kmalloc(i64 128, i64 2, i64 5)
   %ptr1 = call ptr @custom_malloc(i64 8), !alloc_token !0
   %ptr2 = call ptr @kmalloc(i64 32, i64 0), !alloc_token !0
   %ptr3 = call ptr @custom_malloc(i64 64), !alloc_token !0
@@ -48,12 +65,15 @@ entry:
 }
 
 ; Functions without sanitize_alloc_token do not get instrumented
-; CHECK-LABEL: @without_attribute
 define ptr @without_attribute() {
+; CHECK-LABEL: define ptr @without_attribute() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[PTR1:%.*]] = call ptr @malloc(i64 64), !alloc_token [[META0]]
+; CHECK-NEXT:    [[PTR2:%.*]] = call ptr @custom_malloc(i64 8), !alloc_token [[META0]]
+; CHECK-NEXT:    [[PTR3:%.*]] = call ptr @kmalloc(i64 32, i64 0), !alloc_token [[META0]]
+; CHECK-NEXT:    ret ptr [[PTR1]]
+;
 entry:
-  ; CHECK: call ptr @malloc(i64 64)
-  ; CHECK: call ptr @custom_malloc(i64 8)
-  ; CHECK: call ptr @kmalloc(i64 32, i64 0)
   %ptr1 = call ptr @malloc(i64 64), !alloc_token !0
   %ptr2 = call ptr @custom_malloc(i64 8), !alloc_token !0
   %ptr3 = call ptr @kmalloc(i64 32, i64 0), !alloc_token !0
@@ -61,3 +81,6 @@ entry:
 }
 
 !0 = !{!"int", i1 0}
+;.
+; CHECK: [[META0]] = !{!"int", i1 false}
+;.
diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll
index f087cbcc4ff60..a4e142fe2e4cc 100644
--- a/llvm/test/Instrumentation/AllocToken/remark.ll
+++ b/llvm/test/Instrumentation/AllocToken/remark.ll
@@ -1,3 +1,4 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt < %s -passes=inferattrs,alloc-token -pass-remarks=alloc-token -S 2>&1 | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
@@ -8,20 +9,31 @@ declare ptr @malloc(i64)
 ; CHECK-NOT: remark: <unknown>:0:0: Call to 'malloc' in 'test_has_metadata' without source-level type token
 ; CHECK: remark: <unknown>:0:0: Call to 'malloc' in 'test_no_metadata' without source-level type token
 
-; CHECK-LABEL: @test_has_metadata
 define ptr @test_has_metadata() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_has_metadata(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]]
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_malloc(
   %ptr1 = call ptr @malloc(i64 64), !alloc_token !0
   ret ptr %ptr1
 }
 
-; CHECK-LABEL: @test_no_metadata
 define ptr @test_no_metadata() sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_no_metadata(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 32, i64 0)
+; CHECK-NEXT:    ret ptr [[TMP0]]
+;
 entry:
-  ; CHECK: call ptr @__alloc_token_malloc(
   %ptr1 = call ptr @malloc(i64 32)
   ret ptr %ptr1
 }
 
 !0 = !{!"int", i1 0}
+;.
+; CHECK: [[META0]] = !{!"int", i1 false}
+;.

>From 5bac7bb7fa3ccaec0cb7823497b0821425dcc18c Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Tue, 23 Sep 2025 16:43:44 +0200
Subject: [PATCH 09/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/docs/AllocToken.rst                     |  4 +--
 clang/test/CodeGenCXX/alloc-token-pointer.cpp | 32 +++++++++----------
 clang/test/CodeGenCXX/alloc-token.cpp         | 30 ++++++++---------
 .../Transforms/Instrumentation/AllocToken.cpp |  4 +--
 llvm/test/Instrumentation/AllocToken/basic.ll |  4 +--
 .../Instrumentation/AllocToken/basic32.ll     |  2 +-
 .../AllocToken/extralibfuncs.ll               |  6 ++--
 llvm/test/Instrumentation/AllocToken/fast.ll  |  4 +--
 .../test/Instrumentation/AllocToken/invoke.ll |  4 +--
 9 files changed, 44 insertions(+), 46 deletions(-)

diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst
index 62c641f0cfa0d..2a50e73e314c1 100644
--- a/clang/docs/AllocToken.rst
+++ b/clang/docs/AllocToken.rst
@@ -97,9 +97,9 @@ take a final ``size_t token_id`` argument.
 
     // C++ operators (mangled names)
     // operator new(size_t, size_t)
-    void *__alloc_token_Znwm(size_t size, size_t token_id);
+    void *__alloc_token__Znwm(size_t size, size_t token_id);
     // operator new[](size_t, size_t)
-    void *__alloc_token_Znam(size_t size, size_t token_id);
+    void *__alloc_token__Znam(size_t size, size_t token_id);
     // ... other variants like nothrow, etc., are also instrumented.
 
 Fast ABI
diff --git a/clang/test/CodeGenCXX/alloc-token-pointer.cpp b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
index e8019a59c1bf2..64a502b4b2465 100644
--- a/clang/test/CodeGenCXX/alloc-token-pointer.cpp
+++ b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
@@ -31,25 +31,25 @@ int **test_malloc_ptr() {
 
 // CHECK-LABEL: @_Z12test_new_intv(
 int *test_new_int() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0){{.*}} !alloc_token
   return new int;
 }
 
 // CHECK-LABEL: @_Z20test_new_ulong_arrayv(
 unsigned long *test_new_ulong_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 80, i64 0){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 80, i64 0){{.*}} !alloc_token
   return new unsigned long[10];
 }
 
 // CHECK-LABEL: @_Z12test_new_ptrv(
 int **test_new_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 8, i64 1){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 8, i64 1){{.*}} !alloc_token
   return new int*;
 }
 
 // CHECK-LABEL: @_Z18test_new_ptr_arrayv(
 int **test_new_ptr_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 80, i64 1){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 80, i64 1){{.*}} !alloc_token
   return new int*[10];
 }
 
@@ -77,7 +77,7 @@ ContainsPtr *test_malloc_struct_array_with_ptr() {
 // CHECK-LABEL: @_Z32test_operatornew_struct_with_ptrv(
 ContainsPtr *test_operatornew_struct_with_ptr() {
   // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 0)
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 16, i64 0)
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(ContainsPtr));
   return c;
 }
@@ -85,7 +85,7 @@ ContainsPtr *test_operatornew_struct_with_ptr() {
 // CHECK-LABEL: @_Z38test_operatornew_struct_array_with_ptrv(
 ContainsPtr *test_operatornew_struct_array_with_ptr() {
   // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 160, i64 0)
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 160, i64 0)
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(ContainsPtr));
   return c;
 }
@@ -93,7 +93,7 @@ ContainsPtr *test_operatornew_struct_array_with_ptr() {
 // CHECK-LABEL: @_Z33test_operatornew_struct_with_ptr2v(
 ContainsPtr *test_operatornew_struct_with_ptr2() {
   // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 0)
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 16, i64 0)
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(*c));
   return c;
 }
@@ -101,20 +101,20 @@ ContainsPtr *test_operatornew_struct_with_ptr2() {
 // CHECK-LABEL: @_Z39test_operatornew_struct_array_with_ptr2v(
 ContainsPtr *test_operatornew_struct_array_with_ptr2() {
   // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 160, i64 0)
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 160, i64 0)
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(*c));
   return c;
 }
 
 // CHECK-LABEL: @_Z24test_new_struct_with_ptrv(
 ContainsPtr *test_new_struct_with_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 1){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 16, i64 1){{.*}} !alloc_token
   return new ContainsPtr;
 }
 
 // CHECK-LABEL: @_Z30test_new_struct_array_with_ptrv(
 ContainsPtr *test_new_struct_array_with_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 160, i64 1){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 160, i64 1){{.*}} !alloc_token
   return new ContainsPtr[10];
 }
 
@@ -127,13 +127,13 @@ class TestClass {
 
 // CHECK-LABEL: @_Z14test_new_classv(
 TestClass *test_new_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 64, i64 0){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 64, i64 0){{.*}} !alloc_token
   return new TestClass();
 }
 
 // CHECK-LABEL: @_Z20test_new_class_arrayv(
 TestClass *test_new_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 648, i64 0){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 648, i64 0){{.*}} !alloc_token
   return new TestClass[10];
 }
 
@@ -147,13 +147,13 @@ class VirtualTestClass {
 
 // CHECK-LABEL: @_Z22test_new_virtual_classv(
 VirtualTestClass *test_new_virtual_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 1){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 1){{.*}} !alloc_token
   return new VirtualTestClass();
 }
 
 // CHECK-LABEL: @_Z28test_new_virtual_class_arrayv(
 VirtualTestClass *test_new_virtual_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 1){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 728, i64 1){{.*}} !alloc_token
   return new VirtualTestClass[10];
 }
 
@@ -165,13 +165,13 @@ struct MyStructUintptr {
 
 // CHECK-LABEL: @_Z18test_uintptr_isptrv(
 MyStructUintptr *test_uintptr_isptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 16, i64 1)
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 16, i64 1)
   return new MyStructUintptr;
 }
 
 using uptr = uintptr_t;
 // CHECK-LABEL: @_Z19test_uintptr_isptr2v(
 uptr *test_uintptr_isptr2() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 8, i64 1)
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 8, i64 1)
   return new uptr;
 }
diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp
index b8f0ed660bc67..d115720e6b209 100644
--- a/clang/test/CodeGenCXX/alloc-token.cpp
+++ b/clang/test/CodeGenCXX/alloc-token.cpp
@@ -52,42 +52,42 @@ void test_malloc_like() {
 // CHECK-LABEL: @_Z17test_operator_newv(
 void test_operator_new() {
   // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0)
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0)
   sink = __builtin_operator_new(sizeof(int));
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0)
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0)
   sink = ::operator new(sizeof(int));
 }
 
 // CHECK-LABEL: @_Z25test_operator_new_nothrowv(
 void test_operator_new_nothrow() {
   // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0)
+  // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0)
   sink = __builtin_operator_new(sizeof(int), std::nothrow);
-  // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0)
+  // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0)
   sink = ::operator new(sizeof(int), std::nothrow);
 }
 
 // CHECK-LABEL: @_Z8test_newv(
 int *test_new() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new int;
 }
 
 // CHECK-LABEL: @_Z14test_new_arrayv(
 int *test_new_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new int[10];
 }
 
 // CHECK-LABEL: @_Z16test_new_nothrowv(
 int *test_new_nothrow() {
-  // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new (std::nothrow) int;
 }
 
 // CHECK-LABEL: @_Z22test_new_array_nothrowv(
 int *test_new_array_nothrow() {
-  // CHECK: call {{.*}} ptr @__alloc_token_ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new (std::nothrow) int[10];
 }
 
@@ -100,10 +100,10 @@ __attribute__((no_sanitize("alloc-token"))) int *no_sanitize_new() {
 // CHECK-LABEL: @_Z23test_size_returning_newv(
 void test_size_returning_new() {
   // FIXME: This should not be token ID 0!
-  // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new(i64 noundef 8, i64 0)
-  // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1, i64 0)
-  // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_aligned(i64 noundef 8, i64 noundef 32, i64 0)
-  // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1, i64 0)
+  // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new(i64 noundef 8, i64 0)
+  // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1, i64 0)
+  // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_aligned(i64 noundef 8, i64 noundef 32, i64 0)
+  // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1, i64 0)
   sink = __size_returning_new(sizeof(long)).p;
   sink = __size_returning_new_hot_cold(sizeof(long), __hot_cold_t{1}).p;
   sink = __size_returning_new_aligned(sizeof(long), std::align_val_t{32}).p;
@@ -122,7 +122,7 @@ void may_throw();
 // CHECK-LABEL: @_Z27test_exception_handling_newv(
 TestClass *test_exception_handling_new() {
   try {
-    // CHECK: invoke {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}})
+    // CHECK: invoke {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}})
     // CHECK-NEXT: !alloc_token
     TestClass *obj = new TestClass();
     may_throw();
@@ -134,7 +134,7 @@ TestClass *test_exception_handling_new() {
 
 // CHECK-LABEL: @_Z14test_new_classv(
 TestClass *test_new_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   TestClass *obj = new TestClass();
   obj->data[0] = 42;
   return obj;
@@ -142,7 +142,7 @@ TestClass *test_new_class() {
 
 // CHECK-LABEL: @_Z20test_new_class_arrayv(
 TestClass *test_new_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
+  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   TestClass* arr = new TestClass[10];
   arr[0].data[0] = 123;
   return arr;
diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
index 36ca518903b2f..52f5b35eb1fa5 100644
--- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -489,10 +489,8 @@ FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB,
     TokenAllocName += utostr(TokenID) + "_";
   else
     NewParams.push_back(IntPtrTy); // token ID
+  TokenAllocName += Callee->getName();
   FunctionType *NewFTy = FunctionType::get(RetTy, NewParams, false);
-  // Remove leading '_' - we add our own.
-  StringRef No_ = Callee->getName().drop_while([](char C) { return C == '_'; });
-  TokenAllocName += No_;
   FunctionCallee TokenAlloc = Mod.getOrInsertFunction(TokenAllocName, NewFTy);
   if (Function *F = dyn_cast<Function>(TokenAlloc.getCallee()))
     F->copyAttributesFrom(Callee); // preserve attrs
diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll
index fe673ca9fd524..add258ba4476b 100644
--- a/llvm/test/Instrumentation/AllocToken/basic.ll
+++ b/llvm/test/Instrumentation/AllocToken/basic.ll
@@ -35,8 +35,8 @@ define ptr @test_cpp_operators() sanitize_alloc_token {
 ; CHECK-LABEL: define ptr @test_cpp_operators(
 ; CHECK-SAME: ) #[[ATTR5]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i64 32, i64 3)
-; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token_Znam(i64 64, i64 4)
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i64 32, i64 3)
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token__Znam(i64 64, i64 4)
 ; CHECK-NEXT:    ret ptr [[TMP0]]
 ;
 entry:
diff --git a/llvm/test/Instrumentation/AllocToken/basic32.ll b/llvm/test/Instrumentation/AllocToken/basic32.ll
index ed09fdbaf3d56..5f68898983cc1 100644
--- a/llvm/test/Instrumentation/AllocToken/basic32.ll
+++ b/llvm/test/Instrumentation/AllocToken/basic32.ll
@@ -23,7 +23,7 @@ define ptr @test_cpp_operators() sanitize_alloc_token {
 ; CHECK-LABEL: define ptr @test_cpp_operators(
 ; CHECK-SAME: ) #[[ATTR2]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i32 32, i32 1)
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i32 32, i32 1)
 ; CHECK-NEXT:    ret ptr [[TMP0]]
 ;
 entry:
diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
index e472995997711..141c2639f81c1 100644
--- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
+++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
@@ -12,7 +12,7 @@ define ptr @test_extra_libfuncs() sanitize_alloc_token {
 ; CHECK-LABEL: define ptr @test_extra_libfuncs(
 ; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token_size_returning_new(i64 10, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token___size_returning_new(i64 10, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]]
 ; CHECK-NEXT:    [[PTR1:%.*]] = extractvalue { ptr, i64 } [[TMP0]], 0
 ; CHECK-NEXT:    ret ptr [[PTR1]]
 ;
@@ -29,8 +29,8 @@ define ptr @test_replaceable_new() sanitize_alloc_token {
 ; CHECK-LABEL: define ptr @test_replaceable_new(
 ; CHECK-SAME: ) #[[ATTR1]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i64 32, i64 6985720287680550851), !alloc_token [[META0]]
-; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token_Znam(i64 64, i64 6985720287680550851), !alloc_token [[META0]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i64 32, i64 6985720287680550851), !alloc_token [[META0]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token__Znam(i64 64, i64 6985720287680550851), !alloc_token [[META0]]
 ; CHECK-NEXT:    ret ptr [[TMP0]]
 ;
 entry:
diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll
index 60169f308244f..9c79e94453466 100644
--- a/llvm/test/Instrumentation/AllocToken/fast.ll
+++ b/llvm/test/Instrumentation/AllocToken/fast.ll
@@ -32,8 +32,8 @@ define ptr @test_cpp_operators() sanitize_alloc_token {
 ; CHECK-LABEL: define ptr @test_cpp_operators(
 ; CHECK-SAME: ) #[[ATTR4]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[PTR1:%.*]] = call ptr @__alloc_token_0_Znwm(i64 32)
-; CHECK-NEXT:    [[PTR2:%.*]] = call ptr @__alloc_token_1_Znam(i64 64)
+; CHECK-NEXT:    [[PTR1:%.*]] = call ptr @__alloc_token_0__Znwm(i64 32)
+; CHECK-NEXT:    [[PTR2:%.*]] = call ptr @__alloc_token_1__Znam(i64 64)
 ; CHECK-NEXT:    ret ptr [[PTR1]]
 ;
 entry:
diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll
index 8977ed67563c8..68cca2a6a1a33 100644
--- a/llvm/test/Instrumentation/AllocToken/invoke.ll
+++ b/llvm/test/Instrumentation/AllocToken/invoke.ll
@@ -32,7 +32,7 @@ define ptr @test_invoke_operator_new() sanitize_alloc_token personality ptr @__g
 ; CHECK-LABEL: define ptr @test_invoke_operator_new(
 ; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = invoke ptr @__alloc_token_Znwm(i64 32, i64 1)
+; CHECK-NEXT:    [[TMP0:%.*]] = invoke ptr @__alloc_token__Znwm(i64 32, i64 1)
 ; CHECK-NEXT:            to label %[[NORMAL:.*]] unwind label %[[CLEANUP:.*]]
 ; CHECK:       [[NORMAL]]:
 ; CHECK-NEXT:    ret ptr [[TMP0]]
@@ -60,7 +60,7 @@ define ptr @test_complex_invoke_flow() sanitize_alloc_token personality ptr @__g
 ; CHECK-NEXT:    [[TMP0:%.*]] = invoke ptr @__alloc_token_malloc(i64 16, i64 2)
 ; CHECK-NEXT:            to label %[[FIRST_OK:.*]] unwind label %[[CLEANUP1:.*]]
 ; CHECK:       [[FIRST_OK]]:
-; CHECK-NEXT:    [[TMP1:%.*]] = invoke ptr @__alloc_token_Znwm(i64 32, i64 3)
+; CHECK-NEXT:    [[TMP1:%.*]] = invoke ptr @__alloc_token__Znwm(i64 32, i64 3)
 ; CHECK-NEXT:            to label %[[SECOND_OK:.*]] unwind label %[[CLEANUP2:.*]]
 ; CHECK:       [[SECOND_OK]]:
 ; CHECK-NEXT:    ret ptr [[TMP0]]

>From 63e68f71739611669f9b24cc47f5bc9be9ea3dff Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Fri, 26 Sep 2025 20:54:09 +0200
Subject: [PATCH 10/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/docs/AllocToken.rst                     |   2 +-
 clang/docs/ReleaseNotes.rst                   |   7 +-
 clang/docs/UsersManual.rst                    |  20 +-
 clang/include/clang/Basic/CodeGenOptions.h    |   3 +-
 clang/include/clang/Driver/Options.td         |   2 +-
 clang/include/clang/Driver/SanitizerArgs.h    |   1 -
 clang/lib/CodeGen/CGExpr.cpp                  |   6 +-
 clang/lib/Driver/ToolChain.cpp                |   3 +-
 clang/lib/Driver/ToolChains/BareMetal.cpp     |   1 -
 clang/lib/Driver/ToolChains/Linux.cpp         |   1 -
 clang/test/CodeGen/alloc-token-ignorelist.c   |   6 +-
 clang/test/CodeGen/alloc-token.c              | 119 +++++++-
 clang/test/CodeGenCXX/alloc-token-pointer.cpp | 228 +++++++++++----
 clang/test/CodeGenCXX/alloc-token.cpp         | 259 ++++++++++++++----
 llvm/docs/LangRef.rst                         |   9 +
 llvm/lib/IR/Verifier.cpp                      |  12 +
 .../Transforms/Instrumentation/AllocToken.cpp |  69 +++--
 llvm/lib/Transforms/Utils/Local.cpp           |   7 +-
 llvm/test/Instrumentation/AllocToken/basic.ll |   1 -
 .../Instrumentation/AllocToken/basic32.ll     |   1 -
 .../AllocToken/extralibfuncs.ll               |   1 -
 llvm/test/Instrumentation/AllocToken/fast.ll  |   1 -
 .../test/Instrumentation/AllocToken/ignore.ll |   5 +-
 .../test/Instrumentation/AllocToken/invoke.ll |   1 -
 .../Instrumentation/AllocToken/nonlibcalls.ll |   1 -
 .../test/Instrumentation/AllocToken/remark.ll |   1 -
 .../AllocToken/typehashpointersplit.ll        |  35 +++
 .../SimplifyCFG/merge-calls-alloc-token.ll    | 104 +++++++
 28 files changed, 723 insertions(+), 183 deletions(-)
 create mode 100644 llvm/test/Instrumentation/AllocToken/typehashpointersplit.ll
 create mode 100644 llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll

diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst
index 2a50e73e314c1..7f3a128cd1557 100644
--- a/clang/docs/AllocToken.rst
+++ b/clang/docs/AllocToken.rst
@@ -70,7 +70,7 @@ example:
     // Instrumented:
     ptr = __alloc_token_malloc(size, <token id>);
 
-In addition, it is typically recommended to configure the following:
+The following command-line options affect generated token IDs:
 
 * ``-falloc-token-max=<N>``
     Configures the maximum number of tokens. No max by default (tokens bounded
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 193b356631995..7e56f4225d984 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -203,9 +203,10 @@ Non-comprehensive list of changes in this release
   Currently, the use of ``__builtin_dedup_pack`` is limited to template arguments and base
   specifiers, it also must be used within a template context.
 
-- Introduce support for allocation tokens to enable allocator-level heap
-  organization strategies. A feature to instrument all allocation functions
-  with a token ID can be enabled via the ``-fsanitize=alloc-token`` flag.
+- Introduce support for :doc:`allocation tokens <AllocToken>` to enable
+  allocator-level heap organization strategies. A feature to instrument all
+  allocation functions with a token ID can be enabled via the
+  ``-fsanitize=alloc-token`` flag.
 
 New Compiler Flags
 ------------------
diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index f1bd348e4e22d..67e356a9ad685 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -2155,13 +2155,11 @@ are listed below.
 
 .. option:: -f[no-]sanitize=check1,check2,...
 
-   Turn on runtime checks for various forms of undefined or suspicious
-   behavior.
+   Turn on runtime checks or mitigations for various forms of undefined or
+   suspicious behavior. These are disabled by default.
 
-   This option controls whether Clang adds runtime checks for various
-   forms of undefined or suspicious behavior, and is disabled by
-   default. If a check fails, a diagnostic message is produced at
-   runtime explaining the problem. The main checks are:
+   The following options enable runtime checks for various forms of undefined
+   or suspicious behavior:
 
    -  .. _opt_fsanitize_address:
 
@@ -2194,8 +2192,14 @@ are listed below.
       protection against stack-based memory corruption errors.
    -  ``-fsanitize=realtime``: :doc:`RealtimeSanitizer`,
       a real-time safety checker.
-   -  ``-fsanitize=alloc-token``: :doc:`AllocToken`,
-      allocation token instrumentation (requires compatible allocator).
+
+   The following options enable runtime mitigations for various forms of
+   undefined or suspicious behavior:
+
+   -  ``-fsanitize=alloc-token``: Enables :doc:`allocation tokens <AllocToken>`
+      for allocator-level heap organization strategies, such as for security
+      hardening. It passes type-derived token IDs to a compatible memory
+      allocator. Requires linking against a token-aware allocator.
 
    There are more fine-grained checks available: see
    the :ref:`list <ubsan-checks>` of specific kinds of
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index eb283a27ed95a..cae06c3c9495a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -447,7 +447,8 @@ class CodeGenOptions : public CodeGenOptionsBase {
 
   std::optional<double> AllowRuntimeCheckSkipHotCutoff;
 
-  /// Maximum number of allocation tokens (0 = no max).
+  /// Maximum number of allocation tokens (0 = no max), nullopt if none set (use
+  /// pass default).
   std::optional<uint64_t> AllocTokenMax;
 
   /// List of backend command-line options for -fembed-bitcode.
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 064d55d14dcc0..6de5e84fd1168 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2745,7 +2745,7 @@ defm sanitize_alloc_token_extended : BoolOption<"f", "sanitize-alloc-token-exten
 } // end -f[no-]sanitize* flags
 
 def falloc_token_max_EQ : Joined<["-"], "falloc-token-max=">,
-  Group<f_Group>, Visibility<[ClangOption, CC1Option, CLOption]>,
+  Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
   MetaVarName<"<N>">,
   HelpText<"Limit to maximum N allocation tokens (0 = no max)">;
 
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index e0328bd6ed8c3..eea7897e96afd 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -13,7 +13,6 @@
 #include "llvm/Option/Arg.h"
 #include "llvm/Option/ArgList.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h"
-#include <optional>
 #include <string>
 #include <vector>
 
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 28ae2c120edc9..6222a868331b3 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1333,8 +1333,10 @@ void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) {
   PrintingPolicy Policy(CGM.getContext().getLangOpts());
   Policy.SuppressTagKeyword = true;
   Policy.FullyQualifiedName = true;
-  std::string TypeName = AllocType.getCanonicalType().getAsString(Policy);
-  auto *TypeNameMD = MDB.createString(TypeName);
+  SmallString<64> TypeName;
+  llvm::raw_svector_ostream TypeNameOS(TypeName);
+  AllocType.getCanonicalType().print(TypeNameOS, Policy);
+  auto *TypeNameMD = MDB.createString(TypeNameOS.str());
 
   // Check if QualType contains a pointer. Implements a simple DFS to
   // recursively check if a type contains a pointer type.
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 49c89ab0c037f..cce00f13ba6c8 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -1621,7 +1621,8 @@ SanitizerMask ToolChain::getSupportedSanitizers() const {
       SanitizerKind::CFICastStrict | SanitizerKind::FloatDivideByZero |
       SanitizerKind::KCFI | SanitizerKind::UnsignedIntegerOverflow |
       SanitizerKind::UnsignedShiftBase | SanitizerKind::ImplicitConversion |
-      SanitizerKind::Nullability | SanitizerKind::LocalBounds;
+      SanitizerKind::Nullability | SanitizerKind::LocalBounds |
+      SanitizerKind::AllocToken;
   if (getTriple().getArch() == llvm::Triple::x86 ||
       getTriple().getArch() == llvm::Triple::x86_64 ||
       getTriple().getArch() == llvm::Triple::arm ||
diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp
index 3319cdd93b5e2..9b7f58c392885 100644
--- a/clang/lib/Driver/ToolChains/BareMetal.cpp
+++ b/clang/lib/Driver/ToolChains/BareMetal.cpp
@@ -726,7 +726,6 @@ SanitizerMask BareMetal::getSupportedSanitizers() const {
   Res |= SanitizerKind::SafeStack;
   Res |= SanitizerKind::Thread;
   Res |= SanitizerKind::Scudo;
-  Res |= SanitizerKind::AllocToken;
   if (IsX86_64 || IsAArch64 || IsRISCV64) {
     Res |= SanitizerKind::HWAddress;
     Res |= SanitizerKind::KernelHWAddress;
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
index 76070b063678f..16e35b08cfbd6 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -819,7 +819,6 @@ SanitizerMask Linux::getSupportedSanitizers() const {
   Res |= SanitizerKind::KernelAddress;
   Res |= SanitizerKind::Vptr;
   Res |= SanitizerKind::SafeStack;
-  Res |= SanitizerKind::AllocToken;
   if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64)
     Res |= SanitizerKind::DataFlow;
   if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64 ||
diff --git a/clang/test/CodeGen/alloc-token-ignorelist.c b/clang/test/CodeGen/alloc-token-ignorelist.c
index f6f82fe3b83e6..954e6e5964773 100644
--- a/clang/test/CodeGen/alloc-token-ignorelist.c
+++ b/clang/test/CodeGen/alloc-token-ignorelist.c
@@ -1,12 +1,12 @@
 // Test AllocToken respects ignorelist for functions and files.
 //
-// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ALLOW
+// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ALLOW
 //
 // RUN: echo "fun:excluded_by_all" > %t.func.ignorelist
-// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.func.ignorelist -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-FUN
+// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.func.ignorelist -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-FUN
 //
 // RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t.file.ignorelist
-// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.file.ignorelist -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-SRC
+// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.file.ignorelist -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-SRC
 
 extern void* malloc(unsigned long size);
 
diff --git a/clang/test/CodeGen/alloc-token.c b/clang/test/CodeGen/alloc-token.c
index de9b3f48c995f..58fdad55f8d25 100644
--- a/clang/test/CodeGen/alloc-token.c
+++ b/clang/test/CodeGen/alloc-token.c
@@ -1,5 +1,7 @@
-// RUN: %clang_cc1    -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1    -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm -disable-llvm-passes %s -o - | FileCheck --check-prefix=CHECK-CODEGEN %s
+// RUN: %clang_cc1    -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm                      %s -o - | FileCheck --check-prefix=CHECK-O0 %s
+// RUN: %clang_cc1 -O -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm                      %s -o - | FileCheck --check-prefix=CHECK-O1 %s
 
 typedef __typeof(sizeof(int)) size_t;
 
@@ -16,30 +18,119 @@ int posix_memalign(void **memptr, size_t alignment, size_t size);
 void *sink;
 
 // CHECK-LABEL: @test_malloc_like(
+// CHECK-CODEGEN-LABEL: define dso_local void @test_malloc_like(
+// CHECK-CODEGEN-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-CODEGEN-NEXT:  [[ENTRY:.*:]]
+// CHECK-CODEGEN-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR6:[0-9]+]]
+// CHECK-CODEGEN-NEXT:    store ptr [[CALL]], ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[CALL1:%.*]] = call ptr @calloc(i64 noundef 3, i64 noundef 4) #[[ATTR7:[0-9]+]]
+// CHECK-CODEGEN-NEXT:    store ptr [[CALL1]], ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[TMP0:%.*]] = load ptr, ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[CALL2:%.*]] = call ptr @realloc(ptr noundef [[TMP0]], i64 noundef 8) #[[ATTR8:[0-9]+]]
+// CHECK-CODEGEN-NEXT:    store ptr [[CALL2]], ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[TMP1:%.*]] = load ptr, ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[CALL3:%.*]] = call ptr @reallocarray(ptr noundef [[TMP1]], i64 noundef 5, i64 noundef 8)
+// CHECK-CODEGEN-NEXT:    store ptr [[CALL3]], ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[CALL4:%.*]] = call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) #[[ATTR8]]
+// CHECK-CODEGEN-NEXT:    store ptr [[CALL4]], ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[CALL5:%.*]] = call align 16 ptr @memalign(i64 noundef 16, i64 noundef 256) #[[ATTR8]]
+// CHECK-CODEGEN-NEXT:    store ptr [[CALL5]], ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[CALL6:%.*]] = call ptr @valloc(i64 noundef 4096)
+// CHECK-CODEGEN-NEXT:    store ptr [[CALL6]], ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[CALL7:%.*]] = call ptr @pvalloc(i64 noundef 8192)
+// CHECK-CODEGEN-NEXT:    store ptr [[CALL7]], ptr @sink, align 8
+// CHECK-CODEGEN-NEXT:    [[CALL8:%.*]] = call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4)
+// CHECK-CODEGEN-NEXT:    ret void
+//
+// CHECK-O0-LABEL: define dso_local void @test_malloc_like(
+// CHECK-O0-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-O0-NEXT:  [[ENTRY:.*:]]
+// CHECK-O0-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR9:[0-9]+]]
+// CHECK-O0-NEXT:    store ptr [[TMP0]], ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0) #[[ATTR10:[0-9]+]]
+// CHECK-O0-NEXT:    store ptr [[TMP1]], ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP2:%.*]] = load ptr, ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP3:%.*]] = call ptr @__alloc_token_realloc(ptr noundef [[TMP2]], i64 noundef 8, i64 0) #[[ATTR11:[0-9]+]]
+// CHECK-O0-NEXT:    store ptr [[TMP3]], ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP4:%.*]] = load ptr, ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP5:%.*]] = call ptr @__alloc_token_reallocarray(ptr noundef [[TMP4]], i64 noundef 5, i64 noundef 8, i64 0)
+// CHECK-O0-NEXT:    store ptr [[TMP5]], ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP6:%.*]] = call align 128 ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0) #[[ATTR11]]
+// CHECK-O0-NEXT:    store ptr [[TMP6]], ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP7:%.*]] = call align 16 ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0) #[[ATTR11]]
+// CHECK-O0-NEXT:    store ptr [[TMP7]], ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP8:%.*]] = call ptr @__alloc_token_valloc(i64 noundef 4096, i64 0)
+// CHECK-O0-NEXT:    store ptr [[TMP8]], ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP9:%.*]] = call ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0)
+// CHECK-O0-NEXT:    store ptr [[TMP9]], ptr @sink, align 8
+// CHECK-O0-NEXT:    [[TMP10:%.*]] = call i32 @__alloc_token_posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4, i64 0)
+// CHECK-O0-NEXT:    ret void
+//
+// CHECK-O1-LABEL: define dso_local void @test_malloc_like(
+// CHECK-O1-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+// CHECK-O1-NEXT:  [[ENTRY:.*:]]
+// CHECK-O1-NEXT:    [[TMP0:%.*]] = tail call dereferenceable_or_null(4) ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR9:[0-9]+]]
+// CHECK-O1-NEXT:    store ptr [[TMP0]], ptr @sink, align 8, !tbaa [[TBAA2:![0-9]+]]
+// CHECK-O1-NEXT:    [[TMP1:%.*]] = tail call dereferenceable_or_null(12) ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0) #[[ATTR10:[0-9]+]]
+// CHECK-O1-NEXT:    store ptr [[TMP1]], ptr @sink, align 8, !tbaa [[TBAA2]]
+// CHECK-O1-NEXT:    [[TMP2:%.*]] = tail call dereferenceable_or_null(8) ptr @__alloc_token_realloc(ptr noundef [[TMP1]], i64 noundef 8, i64 0) #[[ATTR11:[0-9]+]]
+// CHECK-O1-NEXT:    store ptr [[TMP2]], ptr @sink, align 8, !tbaa [[TBAA2]]
+// CHECK-O1-NEXT:    [[TMP3:%.*]] = tail call dereferenceable_or_null(40) ptr @__alloc_token_reallocarray(ptr noundef [[TMP2]], i64 noundef 5, i64 noundef 8, i64 0)
+// CHECK-O1-NEXT:    store ptr [[TMP3]], ptr @sink, align 8, !tbaa [[TBAA2]]
+// CHECK-O1-NEXT:    [[TMP4:%.*]] = tail call align 128 dereferenceable_or_null(1024) ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0) #[[ATTR11]]
+// CHECK-O1-NEXT:    store ptr [[TMP4]], ptr @sink, align 8, !tbaa [[TBAA2]]
+// CHECK-O1-NEXT:    [[TMP5:%.*]] = tail call align 16 dereferenceable_or_null(256) ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0) #[[ATTR11]]
+// CHECK-O1-NEXT:    store ptr [[TMP5]], ptr @sink, align 8, !tbaa [[TBAA2]]
+// CHECK-O1-NEXT:    [[TMP6:%.*]] = tail call dereferenceable_or_null(4096) ptr @__alloc_token_valloc(i64 noundef 4096, i64 0)
+// CHECK-O1-NEXT:    store ptr [[TMP6]], ptr @sink, align 8, !tbaa [[TBAA2]]
+// CHECK-O1-NEXT:    [[TMP7:%.*]] = tail call ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0)
+// CHECK-O1-NEXT:    store ptr [[TMP7]], ptr @sink, align 8, !tbaa [[TBAA2]]
+// CHECK-O1-NEXT:    [[TMP8:%.*]] = tail call i32 @__alloc_token_posix_memalign(ptr noundef nonnull @sink, i64 noundef 64, i64 noundef 4, i64 0) #[[ATTR12:[0-9]+]]
+// CHECK-O1-NEXT:    ret void
+//
 void test_malloc_like() {
-  // FIXME: Should not be token ID 0! Currently fail to infer the type.
-  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0)
   sink = malloc(sizeof(int));
-  // CHECK: call{{.*}} ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0)
   sink = calloc(3, sizeof(int));
-  // CHECK: call{{.*}} ptr @__alloc_token_realloc(ptr noundef {{[^,]*}}, i64 noundef 8, i64 0)
   sink = realloc(sink, sizeof(long));
-  // CHECK: call{{.*}} ptr @__alloc_token_reallocarray(ptr noundef {{[^,]*}}, i64 noundef 5, i64 noundef 8, i64 0)
   sink = reallocarray(sink, 5, sizeof(long));
-  // CHECK: call{{.*}} i32 @__alloc_token_posix_memalign(ptr noundef {{[^,]*}}, i64 noundef 64, i64 noundef 4, i64 0)
-  posix_memalign(&sink, 64, sizeof(int));
-  // CHECK: call align 128{{.*}} ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0)
   sink = aligned_alloc(128, 1024);
-  // CHECK: call align 16{{.*}} ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0)
   sink = memalign(16, 256);
-  // CHECK: call{{.*}} ptr @__alloc_token_valloc(i64 noundef 4096, i64 0)
   sink = valloc(4096);
-  // CHECK: call{{.*}} ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0)
   sink = pvalloc(8192);
+  posix_memalign(&sink, 64, sizeof(int));
 }
 
 // CHECK-LABEL: @no_sanitize_malloc(
+// CHECK-CODEGEN-LABEL: define dso_local ptr @no_sanitize_malloc(
+// CHECK-CODEGEN-SAME: i64 noundef [[SIZE:%.*]]) #[[ATTR5:[0-9]+]] {
+// CHECK-CODEGEN-NEXT:  [[ENTRY:.*:]]
+// CHECK-CODEGEN-NEXT:    [[SIZE_ADDR:%.*]] = alloca i64, align 8
+// CHECK-CODEGEN-NEXT:    store i64 [[SIZE]], ptr [[SIZE_ADDR]], align 8
+// CHECK-CODEGEN-NEXT:    [[TMP0:%.*]] = load i64, ptr [[SIZE_ADDR]], align 8
+// CHECK-CODEGEN-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef [[TMP0]]) #[[ATTR6]]
+// CHECK-CODEGEN-NEXT:    ret ptr [[CALL]]
+//
+// CHECK-O0-LABEL: define dso_local ptr @no_sanitize_malloc(
+// CHECK-O0-SAME: i64 noundef [[SIZE:%.*]]) #[[ATTR8:[0-9]+]] {
+// CHECK-O0-NEXT:  [[ENTRY:.*:]]
+// CHECK-O0-NEXT:    [[SIZE_ADDR:%.*]] = alloca i64, align 8
+// CHECK-O0-NEXT:    store i64 [[SIZE]], ptr [[SIZE_ADDR]], align 8
+// CHECK-O0-NEXT:    [[TMP0:%.*]] = load i64, ptr [[SIZE_ADDR]], align 8
+// CHECK-O0-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef [[TMP0]]) #[[ATTR9]]
+// CHECK-O0-NEXT:    ret ptr [[CALL]]
+//
+// CHECK-O1-LABEL: define dso_local noalias noundef ptr @no_sanitize_malloc(
+// CHECK-O1-SAME: i64 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+// CHECK-O1-NEXT:  [[ENTRY:.*:]]
+// CHECK-O1-NEXT:    [[CALL:%.*]] = tail call ptr @malloc(i64 noundef [[SIZE]]) #[[ATTR9]]
+// CHECK-O1-NEXT:    ret ptr [[CALL]]
+//
 void *no_sanitize_malloc(size_t size) __attribute__((no_sanitize("alloc-token"))) {
-  // CHECK: call ptr @malloc(
   return malloc(size);
 }
+//.
+// CHECK-O1: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0}
+// CHECK-O1: [[META3]] = !{!"any pointer", [[META4:![0-9]+]], i64 0}
+// CHECK-O1: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0}
+// CHECK-O1: [[META5]] = !{!"Simple C/C++ TBAA"}
+//.
diff --git a/clang/test/CodeGenCXX/alloc-token-pointer.cpp b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
index 64a502b4b2465..74f1ae850d19d 100644
--- a/clang/test/CodeGenCXX/alloc-token-pointer.cpp
+++ b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
@@ -1,8 +1,5 @@
-// Check -fsanitize=alloc-token TypeHashPointerSplit mode with only 2
-// tokens so we effectively only test the contains-pointer logic.
-//
-// RUN: %clang_cc1    -fsanitize=alloc-token -falloc-token-max=2 -triple x86_64-linux-gnu -std=c++20 -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2 -triple x86_64-linux-gnu -std=c++20 -emit-llvm %s -o - | FileCheck %s
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -std=c++20 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
 
 #include "../Analysis/Inputs/system-header-simulator-cxx.h"
 
@@ -12,44 +9,77 @@ extern "C" {
 void *malloc(size_t size);
 }
 
-// CHECK-LABEL: @_Z15test_malloc_intv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_intv(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[A:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR4:[0-9]+]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[A]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A]], align 8
+// CHECK-NEXT:    store i32 42, ptr [[TMP0]], align 4
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[A]], align 8
+// CHECK-NEXT:    ret ptr [[TMP1]]
+//
 void *test_malloc_int() {
-  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0)
   int *a = (int *)malloc(sizeof(int));
   *a = 42;
   return a;
 }
 
-// CHECK-LABEL: @_Z15test_malloc_ptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_ptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[A:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 8) #[[ATTR4]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[A]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A]], align 8
+// CHECK-NEXT:    store ptr null, ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[A]], align 8
+// CHECK-NEXT:    ret ptr [[TMP1]]
+//
 int **test_malloc_ptr() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 8, i64 0)
   int **a = (int **)malloc(sizeof(int*));
   *a = nullptr;
   return a;
 }
 
-// CHECK-LABEL: @_Z12test_new_intv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_intv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR5:[0-9]+]], !alloc_token [[META2:![0-9]+]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 int *test_new_int() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0){{.*}} !alloc_token
   return new int;
 }
 
-// CHECK-LABEL: @_Z20test_new_ulong_arrayv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_ulong_arrayv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 80) #[[ATTR5]], !alloc_token [[META3:![0-9]+]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 unsigned long *test_new_ulong_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 80, i64 0){{.*}} !alloc_token
   return new unsigned long[10];
 }
 
-// CHECK-LABEL: @_Z12test_new_ptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_ptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #[[ATTR5]], !alloc_token [[META4:![0-9]+]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 int **test_new_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 8, i64 1){{.*}} !alloc_token
   return new int*;
 }
 
-// CHECK-LABEL: @_Z18test_new_ptr_arrayv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z18test_new_ptr_arrayv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 80) #[[ATTR5]], !alloc_token [[META4]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 int **test_new_ptr_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 80, i64 1){{.*}} !alloc_token
   return new int*[10];
 }
 
@@ -58,63 +88,107 @@ struct ContainsPtr {
   char *buf;
 };
 
-// CHECK-LABEL: @_Z27test_malloc_struct_with_ptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z27test_malloc_struct_with_ptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 16) #[[ATTR4]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
 ContainsPtr *test_malloc_struct_with_ptr() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 16, i64 0)
   ContainsPtr *c = (ContainsPtr *)malloc(sizeof(ContainsPtr));
   return c;
 }
 
-// CHECK-LABEL: @_Z33test_malloc_struct_array_with_ptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z33test_malloc_struct_array_with_ptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 160) #[[ATTR4]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
 ContainsPtr *test_malloc_struct_array_with_ptr() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 160, i64 0)
   ContainsPtr *c = (ContainsPtr *)malloc(10 * sizeof(ContainsPtr));
   return c;
 }
 
-// CHECK-LABEL: @_Z32test_operatornew_struct_with_ptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z32test_operatornew_struct_with_ptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) #[[ATTR5]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
 ContainsPtr *test_operatornew_struct_with_ptr() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 16, i64 0)
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(ContainsPtr));
   return c;
 }
 
-// CHECK-LABEL: @_Z38test_operatornew_struct_array_with_ptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z38test_operatornew_struct_array_with_ptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 160) #[[ATTR5]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
 ContainsPtr *test_operatornew_struct_array_with_ptr() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 160, i64 0)
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(ContainsPtr));
   return c;
 }
 
-// CHECK-LABEL: @_Z33test_operatornew_struct_with_ptr2v(
+// CHECK-LABEL: define dso_local noundef ptr @_Z33test_operatornew_struct_with_ptr2v(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) #[[ATTR5]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
 ContainsPtr *test_operatornew_struct_with_ptr2() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 16, i64 0)
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(*c));
   return c;
 }
 
-// CHECK-LABEL: @_Z39test_operatornew_struct_array_with_ptr2v(
+// CHECK-LABEL: define dso_local noundef ptr @_Z39test_operatornew_struct_array_with_ptr2v(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 160) #[[ATTR5]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
 ContainsPtr *test_operatornew_struct_array_with_ptr2() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 160, i64 0)
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(*c));
   return c;
 }
 
-// CHECK-LABEL: @_Z24test_new_struct_with_ptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z24test_new_struct_with_ptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) #[[ATTR5]], !alloc_token [[META5:![0-9]+]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 ContainsPtr *test_new_struct_with_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 16, i64 1){{.*}} !alloc_token
   return new ContainsPtr;
 }
 
-// CHECK-LABEL: @_Z30test_new_struct_array_with_ptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z30test_new_struct_array_with_ptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 160) #[[ATTR5]], !alloc_token [[META5]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 ContainsPtr *test_new_struct_array_with_ptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 160, i64 1){{.*}} !alloc_token
   return new ContainsPtr[10];
 }
 
@@ -125,15 +199,26 @@ class TestClass {
   int data[16];
 };
 
-// CHECK-LABEL: @_Z14test_new_classv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_classv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 64) #[[ATTR5]], !alloc_token [[META6:![0-9]+]]
+// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 64, i1 false)
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 TestClass *test_new_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 64, i64 0){{.*}} !alloc_token
   return new TestClass();
 }
 
-// CHECK-LABEL: @_Z20test_new_class_arrayv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_class_arrayv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 648) #[[ATTR5]], !alloc_token [[META6]]
+// CHECK-NEXT:    store i64 10, ptr [[CALL]], align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
 TestClass *test_new_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 648, i64 0){{.*}} !alloc_token
   return new TestClass[10];
 }
 
@@ -145,15 +230,36 @@ class VirtualTestClass {
   int data[16];
 };
 
-// CHECK-LABEL: @_Z22test_new_virtual_classv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z22test_new_virtual_classv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR5]], !alloc_token [[META7:![0-9]+]]
+// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false)
+// CHECK-NEXT:    call void @_ZN16VirtualTestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR6:[0-9]+]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 VirtualTestClass *test_new_virtual_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 1){{.*}} !alloc_token
   return new VirtualTestClass();
 }
 
-// CHECK-LABEL: @_Z28test_new_virtual_class_arrayv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z28test_new_virtual_class_arrayv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 728) #[[ATTR5]], !alloc_token [[META7]]
+// CHECK-NEXT:    store i64 10, ptr [[CALL]], align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
+// CHECK-NEXT:    [[ARRAYCTOR_END:%.*]] = getelementptr inbounds [[CLASS_VIRTUALTESTCLASS:%.*]], ptr [[TMP0]], i64 10
+// CHECK-NEXT:    br label %[[ARRAYCTOR_LOOP:.*]]
+// CHECK:       [[ARRAYCTOR_LOOP]]:
+// CHECK-NEXT:    [[ARRAYCTOR_CUR:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[ARRAYCTOR_NEXT:%.*]], %[[ARRAYCTOR_LOOP]] ]
+// CHECK-NEXT:    call void @_ZN16VirtualTestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[ARRAYCTOR_CUR]]) #[[ATTR6]]
+// CHECK-NEXT:    [[ARRAYCTOR_NEXT]] = getelementptr inbounds [[CLASS_VIRTUALTESTCLASS]], ptr [[ARRAYCTOR_CUR]], i64 1
+// CHECK-NEXT:    [[ARRAYCTOR_DONE:%.*]] = icmp eq ptr [[ARRAYCTOR_NEXT]], [[ARRAYCTOR_END]]
+// CHECK-NEXT:    br i1 [[ARRAYCTOR_DONE]], label %[[ARRAYCTOR_CONT:.*]], label %[[ARRAYCTOR_LOOP]]
+// CHECK:       [[ARRAYCTOR_CONT]]:
+// CHECK-NEXT:    ret ptr [[TMP0]]
+//
 VirtualTestClass *test_new_virtual_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 728, i64 1){{.*}} !alloc_token
   return new VirtualTestClass[10];
 }
 
@@ -163,15 +269,33 @@ struct MyStructUintptr {
   uintptr_t ptr;
 };
 
-// CHECK-LABEL: @_Z18test_uintptr_isptrv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z18test_uintptr_isptrv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) #[[ATTR5]], !alloc_token [[META8:![0-9]+]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 MyStructUintptr *test_uintptr_isptr() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 16, i64 1)
   return new MyStructUintptr;
 }
 
 using uptr = uintptr_t;
-// CHECK-LABEL: @_Z19test_uintptr_isptr2v(
+// CHECK-LABEL: define dso_local noundef ptr @_Z19test_uintptr_isptr2v(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #[[ATTR5]], !alloc_token [[META9:![0-9]+]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 uptr *test_uintptr_isptr2() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 8, i64 1)
   return new uptr;
 }
+//.
+// CHECK: [[META2]] = !{!"int", i1 false}
+// CHECK: [[META3]] = !{!"unsigned long", i1 false}
+// CHECK: [[META4]] = !{!"int *", i1 true}
+// CHECK: [[META5]] = !{!"ContainsPtr", i1 true}
+// CHECK: [[META6]] = !{!"TestClass", i1 false}
+// CHECK: [[META7]] = !{!"VirtualTestClass", i1 true}
+// CHECK: [[META8]] = !{!"MyStructUintptr", i1 true}
+// CHECK: [[META9]] = !{!"unsigned long", i1 true}
+//.
diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp
index d115720e6b209..ab6b4e4d8917c 100644
--- a/clang/test/CodeGenCXX/alloc-token.cpp
+++ b/clang/test/CodeGenCXX/alloc-token.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1    -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s
-// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
+// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
 
 #include "../Analysis/Inputs/system-header-simulator-cxx.h"
 extern "C" {
@@ -26,84 +26,160 @@ __sized_ptr_t __size_returning_new_aligned_hot_cold(size_t, std::align_val_t,  _
 
 void *sink; // prevent optimizations from removing the calls
 
-// CHECK-LABEL: @_Z16test_malloc_likev(
+// CHECK-LABEL: define dso_local void @_Z16test_malloc_likev(
+// CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR10:[0-9]+]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL1:%.*]] = call ptr @calloc(i64 noundef 3, i64 noundef 4) #[[ATTR11:[0-9]+]]
+// CHECK-NEXT:    store ptr [[CALL1]], ptr @sink, align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr @sink, align 8
+// CHECK-NEXT:    [[CALL2:%.*]] = call ptr @realloc(ptr noundef [[TMP0]], i64 noundef 8) #[[ATTR12:[0-9]+]]
+// CHECK-NEXT:    store ptr [[CALL2]], ptr @sink, align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr @sink, align 8
+// CHECK-NEXT:    [[CALL3:%.*]] = call ptr @reallocarray(ptr noundef [[TMP1]], i64 noundef 5, i64 noundef 8)
+// CHECK-NEXT:    store ptr [[CALL3]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL4:%.*]] = call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) #[[ATTR12]]
+// CHECK-NEXT:    store ptr [[CALL4]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL5:%.*]] = call ptr @memalign(i64 noundef 16, i64 noundef 256)
+// CHECK-NEXT:    store ptr [[CALL5]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL6:%.*]] = call ptr @valloc(i64 noundef 4096)
+// CHECK-NEXT:    store ptr [[CALL6]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL7:%.*]] = call ptr @pvalloc(i64 noundef 8192)
+// CHECK-NEXT:    store ptr [[CALL7]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL8:%.*]] = call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4)
+// CHECK-NEXT:    ret void
+//
 void test_malloc_like() {
-  // FIXME: Should not be token ID 0! Currently fail to infer the type.
-  // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0)
   sink = malloc(sizeof(int));
-  // CHECK: call{{.*}} ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0)
   sink = calloc(3, sizeof(int));
-  // CHECK: call{{.*}} ptr @__alloc_token_realloc(ptr noundef {{[^,]*}}, i64 noundef 8, i64 0)
   sink = realloc(sink, sizeof(long));
-  // CHECK: call{{.*}} ptr @__alloc_token_reallocarray(ptr noundef {{[^,]*}}, i64 noundef 5, i64 noundef 8, i64 0)
   sink = reallocarray(sink, 5, sizeof(long));
-  // CHECK: call{{.*}} i32 @__alloc_token_posix_memalign(ptr noundef {{[^,]*}}, i64 noundef 64, i64 noundef 4, i64 0)
-  posix_memalign(&sink, 64, sizeof(int));
-  // CHECK: call align 128{{.*}} ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0)
   sink = aligned_alloc(128, 1024);
-  // CHECK: call{{.*}} ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0)
   sink = memalign(16, 256);
-  // CHECK: call{{.*}} ptr @__alloc_token_valloc(i64 noundef 4096, i64 0)
   sink = valloc(4096);
-  // CHECK: call{{.*}} ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0)
   sink = pvalloc(8192);
+  posix_memalign(&sink, 64, sizeof(int));
 }
 
-// CHECK-LABEL: @_Z17test_operator_newv(
+// CHECK-LABEL: define dso_local void @_Z17test_operator_newv(
+// CHECK-SAME: ) #[[ATTR1]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR13:[0-9]+]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL1:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR10]]
+// CHECK-NEXT:    store ptr [[CALL1]], ptr @sink, align 8
+// CHECK-NEXT:    ret void
+//
 void test_operator_new() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0)
   sink = __builtin_operator_new(sizeof(int));
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0)
   sink = ::operator new(sizeof(int));
 }
 
-// CHECK-LABEL: @_Z25test_operator_new_nothrowv(
+// CHECK-LABEL: define dso_local void @_Z25test_operator_new_nothrowv(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14:[0-9]+]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL1:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR15:[0-9]+]]
+// CHECK-NEXT:    store ptr [[CALL1]], ptr @sink, align 8
+// CHECK-NEXT:    ret void
+//
 void test_operator_new_nothrow() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0)
   sink = __builtin_operator_new(sizeof(int), std::nothrow);
-  // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0)
   sink = ::operator new(sizeof(int), std::nothrow);
 }
 
-// CHECK-LABEL: @_Z8test_newv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z8test_newv(
+// CHECK-SAME: ) #[[ATTR1]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR13]], !alloc_token [[META2:![0-9]+]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 int *test_new() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new int;
 }
 
-// CHECK-LABEL: @_Z14test_new_arrayv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_arrayv(
+// CHECK-SAME: ) #[[ATTR1]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 40) #[[ATTR13]], !alloc_token [[META2]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 int *test_new_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new int[10];
 }
 
-// CHECK-LABEL: @_Z16test_new_nothrowv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z16test_new_nothrowv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14]], !alloc_token [[META2]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 int *test_new_nothrow() {
-  // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new (std::nothrow) int;
 }
 
-// CHECK-LABEL: @_Z22test_new_array_nothrowv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z22test_new_array_nothrowv(
+// CHECK-SAME: ) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef ptr @_ZnamRKSt9nothrow_t(i64 noundef 40, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14]], !alloc_token [[META2]]
+// CHECK-NEXT:    ret ptr [[CALL]]
+//
 int *test_new_array_nothrow() {
-  // CHECK: call {{.*}} ptr @__alloc_token__ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   return new (std::nothrow) int[10];
 }
 
-// CHECK-LABEL: @_Z15no_sanitize_newv(
-__attribute__((no_sanitize("alloc-token"))) int *no_sanitize_new() {
-  // CHECK: call {{.*}} ptr @_Znwm(i64 noundef 4)
-  return new int;
-}
-
-// CHECK-LABEL: @_Z23test_size_returning_newv(
+// CHECK-LABEL: define dso_local void @_Z23test_size_returning_newv(
+// CHECK-SAME: ) #[[ATTR1]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[REF_TMP:%.*]] = alloca [[STRUCT___SIZED_PTR_T:%.*]], align 8
+// CHECK-NEXT:    [[REF_TMP1:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8
+// CHECK-NEXT:    [[REF_TMP4:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8
+// CHECK-NEXT:    [[REF_TMP7:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call { ptr, i64 } @__size_returning_new(i64 noundef 8)
+// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { ptr, i64 } [[CALL]], 0
+// CHECK-NEXT:    store ptr [[TMP1]], ptr [[TMP0]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP3:%.*]] = extractvalue { ptr, i64 } [[CALL]], 1
+// CHECK-NEXT:    store i64 [[TMP3]], ptr [[TMP2]], align 8
+// CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[P]], align 8
+// CHECK-NEXT:    store ptr [[TMP4]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL2:%.*]] = call { ptr, i64 } @__size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1)
+// CHECK-NEXT:    [[TMP5:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { ptr, i64 } [[CALL2]], 0
+// CHECK-NEXT:    store ptr [[TMP6]], ptr [[TMP5]], align 8
+// CHECK-NEXT:    [[TMP7:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP1]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { ptr, i64 } [[CALL2]], 1
+// CHECK-NEXT:    store i64 [[TMP8]], ptr [[TMP7]], align 8
+// CHECK-NEXT:    [[P3:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP9:%.*]] = load ptr, ptr [[P3]], align 8
+// CHECK-NEXT:    store ptr [[TMP9]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL5:%.*]] = call { ptr, i64 } @__size_returning_new_aligned(i64 noundef 8, i64 noundef 32)
+// CHECK-NEXT:    [[TMP10:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP4]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP11:%.*]] = extractvalue { ptr, i64 } [[CALL5]], 0
+// CHECK-NEXT:    store ptr [[TMP11]], ptr [[TMP10]], align 8
+// CHECK-NEXT:    [[TMP12:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP4]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP13:%.*]] = extractvalue { ptr, i64 } [[CALL5]], 1
+// CHECK-NEXT:    store i64 [[TMP13]], ptr [[TMP12]], align 8
+// CHECK-NEXT:    [[P6:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP4]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP14:%.*]] = load ptr, ptr [[P6]], align 8
+// CHECK-NEXT:    store ptr [[TMP14]], ptr @sink, align 8
+// CHECK-NEXT:    [[CALL8:%.*]] = call { ptr, i64 } @__size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1)
+// CHECK-NEXT:    [[TMP15:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP7]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP16:%.*]] = extractvalue { ptr, i64 } [[CALL8]], 0
+// CHECK-NEXT:    store ptr [[TMP16]], ptr [[TMP15]], align 8
+// CHECK-NEXT:    [[TMP17:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP7]], i32 0, i32 1
+// CHECK-NEXT:    [[TMP18:%.*]] = extractvalue { ptr, i64 } [[CALL8]], 1
+// CHECK-NEXT:    store i64 [[TMP18]], ptr [[TMP17]], align 8
+// CHECK-NEXT:    [[P9:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP7]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP19:%.*]] = load ptr, ptr [[P9]], align 8
+// CHECK-NEXT:    store ptr [[TMP19]], ptr @sink, align 8
+// CHECK-NEXT:    ret void
+//
 void test_size_returning_new() {
-  // FIXME: This should not be token ID 0!
-  // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new(i64 noundef 8, i64 0)
-  // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1, i64 0)
-  // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_aligned(i64 noundef 8, i64 noundef 32, i64 0)
-  // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1, i64 0)
   sink = __size_returning_new(sizeof(long)).p;
   sink = __size_returning_new_hot_cold(sizeof(long), __hot_cold_t{1}).p;
   sink = __size_returning_new_aligned(sizeof(long), std::align_val_t{32}).p;
@@ -119,11 +195,48 @@ class TestClass {
 
 void may_throw();
 
-// CHECK-LABEL: @_Z27test_exception_handling_newv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z27test_exception_handling_newv(
+// CHECK-SAME: ) #[[ATTR1]] personality ptr @__gxx_personality_v0 {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[RETVAL:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[OBJ:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[EXN_SLOT:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[CALL:%.*]] = invoke noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR13]]
+// CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]], !alloc_token [[META3:![0-9]+]]
+// CHECK:       [[INVOKE_CONT]]:
+// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false)
+// CHECK-NEXT:    call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR16:[0-9]+]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[OBJ]], align 8
+// CHECK-NEXT:    invoke void @_Z9may_throwv()
+// CHECK-NEXT:            to label %[[INVOKE_CONT1:.*]] unwind label %[[LPAD]]
+// CHECK:       [[INVOKE_CONT1]]:
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[OBJ]], align 8
+// CHECK-NEXT:    store ptr [[TMP0]], ptr [[RETVAL]], align 8
+// CHECK-NEXT:    br label %[[RETURN:.*]]
+// CHECK:       [[LPAD]]:
+// CHECK-NEXT:    [[TMP1:%.*]] = landingpad { ptr, i32 }
+// CHECK-NEXT:            catch ptr null
+// CHECK-NEXT:    [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 0
+// CHECK-NEXT:    store ptr [[TMP2]], ptr [[EXN_SLOT]], align 8
+// CHECK-NEXT:    [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 1
+// CHECK-NEXT:    store i32 [[TMP3]], ptr [[EHSELECTOR_SLOT]], align 4
+// CHECK-NEXT:    br label %[[CATCH:.*]]
+// CHECK:       [[CATCH]]:
+// CHECK-NEXT:    [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
+// CHECK-NEXT:    [[TMP4:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR16]]
+// CHECK-NEXT:    store ptr null, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    call void @__cxa_end_catch()
+// CHECK-NEXT:    br label %[[RETURN]]
+// CHECK:       [[TRY_CONT:.*:]]
+// CHECK-NEXT:    call void @llvm.trap()
+// CHECK-NEXT:    unreachable
+// CHECK:       [[RETURN]]:
+// CHECK-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[RETVAL]], align 8
+// CHECK-NEXT:    ret ptr [[TMP5]]
+//
 TestClass *test_exception_handling_new() {
   try {
-    // CHECK: invoke {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}})
-    // CHECK-NEXT: !alloc_token
     TestClass *obj = new TestClass();
     may_throw();
     return obj;
@@ -132,26 +245,58 @@ TestClass *test_exception_handling_new() {
   }
 }
 
-// CHECK-LABEL: @_Z14test_new_classv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_classv(
+// CHECK-SAME: ) #[[ATTR1]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[OBJ:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR13]], !alloc_token [[META3]]
+// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false)
+// CHECK-NEXT:    call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR16]]
+// CHECK-NEXT:    store ptr [[CALL]], ptr [[OBJ]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[OBJ]], align 8
+// CHECK-NEXT:    [[DATA:%.*]] = getelementptr inbounds nuw [[CLASS_TESTCLASS:%.*]], ptr [[TMP0]], i32 0, i32 1
+// CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [16 x i32], ptr [[DATA]], i64 0, i64 0
+// CHECK-NEXT:    store i32 42, ptr [[ARRAYIDX]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[OBJ]], align 8
+// CHECK-NEXT:    ret ptr [[TMP1]]
+//
 TestClass *test_new_class() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   TestClass *obj = new TestClass();
   obj->data[0] = 42;
   return obj;
 }
 
-// CHECK-LABEL: @_Z20test_new_class_arrayv(
+// CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_class_arrayv(
+// CHECK-SAME: ) #[[ATTR1]] {
+// CHECK-NEXT:  [[ENTRY:.*]]:
+// CHECK-NEXT:    [[ARR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 728) #[[ATTR13]], !alloc_token [[META3]]
+// CHECK-NEXT:    store i64 10, ptr [[CALL]], align 16
+// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
+// CHECK-NEXT:    [[ARRAYCTOR_END:%.*]] = getelementptr inbounds [[CLASS_TESTCLASS:%.*]], ptr [[TMP0]], i64 10
+// CHECK-NEXT:    br label %[[ARRAYCTOR_LOOP:.*]]
+// CHECK:       [[ARRAYCTOR_LOOP]]:
+// CHECK-NEXT:    [[ARRAYCTOR_CUR:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[ARRAYCTOR_NEXT:%.*]], %[[ARRAYCTOR_LOOP]] ]
+// CHECK-NEXT:    call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[ARRAYCTOR_CUR]]) #[[ATTR16]]
+// CHECK-NEXT:    [[ARRAYCTOR_NEXT]] = getelementptr inbounds [[CLASS_TESTCLASS]], ptr [[ARRAYCTOR_CUR]], i64 1
+// CHECK-NEXT:    [[ARRAYCTOR_DONE:%.*]] = icmp eq ptr [[ARRAYCTOR_NEXT]], [[ARRAYCTOR_END]]
+// CHECK-NEXT:    br i1 [[ARRAYCTOR_DONE]], label %[[ARRAYCTOR_CONT:.*]], label %[[ARRAYCTOR_LOOP]]
+// CHECK:       [[ARRAYCTOR_CONT]]:
+// CHECK-NEXT:    store ptr [[TMP0]], ptr [[ARR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[ARR]], align 8
+// CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [[CLASS_TESTCLASS]], ptr [[TMP1]], i64 0
+// CHECK-NEXT:    [[DATA:%.*]] = getelementptr inbounds nuw [[CLASS_TESTCLASS]], ptr [[ARRAYIDX]], i32 0, i32 1
+// CHECK-NEXT:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [16 x i32], ptr [[DATA]], i64 0, i64 0
+// CHECK-NEXT:    store i32 123, ptr [[ARRAYIDX1]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[ARR]], align 8
+// CHECK-NEXT:    ret ptr [[TMP2]]
+//
 TestClass *test_new_class_array() {
-  // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token
   TestClass* arr = new TestClass[10];
   arr[0].data[0] = 123;
   return arr;
 }
-
-// CHECK-LABEL: @_Z21test_delete_unchangedPiS_(
-void test_delete_unchanged(int *x, int *y) {
-  // CHECK: call void @_ZdlPvm
-  // CHECK: call void @_ZdaPv
-  delete x;
-  delete [] y;
-}
+//.
+// CHECK: [[META2]] = !{!"int", i1 false}
+// CHECK: [[META3]] = !{!"TestClass", i1 true}
+//.
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 1cf7ccc463b38..bfecb32efd035 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8400,6 +8400,15 @@ functions, and contains richer semantic information about the type of the
 allocation. This information is consumed by the ``alloc-token`` pass to
 instrument such calls with allocation token IDs.
 
+The metadata contains: string with the type of an allocation, and a boolean
+denoting if the type contains a pointer.
+
+.. code-block:: none
+
+  call ptr @malloc(i64 64), !alloc_token !0
+
+  !0 = !{!"<type-name>", i1 <contains-pointer>}
+
 Module Flags Metadata
 =====================
 
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 48007be924bda..da7464985bda4 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -535,6 +535,7 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
   void visitDIAssignIDMetadata(Instruction &I, MDNode *MD);
   void visitMMRAMetadata(Instruction &I, MDNode *MD);
   void visitAnnotationMetadata(MDNode *Annotation);
+  void visitAllocTokenMetadata(Instruction &I, MDNode *MD);
   void visitAliasScopeMetadata(const MDNode *MD);
   void visitAliasScopeListMetadata(const MDNode *MD);
   void visitAccessGroupMetadata(const MDNode *MD);
@@ -5332,6 +5333,14 @@ void Verifier::visitAccessGroupMetadata(const MDNode *MD) {
   }
 }
 
+void Verifier::visitAllocTokenMetadata(Instruction &I, MDNode *MD) {
+  Check(isa<CallBase>(I), "!alloc_token should only exist on calls", &I);
+  Check(MD->getNumOperands() == 2, "!alloc_token must have 2 operands", MD);
+  Check(isa<MDString>(MD->getOperand(0)), "expected string", MD);
+  Check(mdconst::dyn_extract_or_null<ConstantInt>(MD->getOperand(1)),
+        "expected integer constant", MD);
+}
+
 /// verifyInstruction - Verify that an instruction is well formed.
 ///
 void Verifier::visitInstruction(Instruction &I) {
@@ -5559,6 +5568,9 @@ void Verifier::visitInstruction(Instruction &I) {
   if (MDNode *Annotation = I.getMetadata(LLVMContext::MD_annotation))
     visitAnnotationMetadata(Annotation);
 
+  if (MDNode *MD = I.getMetadata(LLVMContext::MD_alloc_token))
+    visitAllocTokenMetadata(I, MD);
+
   if (MDNode *N = I.getDebugLoc().getAsMDNode()) {
     CheckDI(isa<DILocation>(N), "invalid !dbg metadata attachment", &I, N);
     visitMDNode(*N, AreDebugLocsAllowed::Yes);
diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
index 52f5b35eb1fa5..81d106af47a20 100644
--- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -285,10 +285,13 @@ class AllocToken {
   bool instrumentFunction(Function &F);
 
 private:
-  /// Returns true for !isAllocationFn() functions that are also eligible for
-  /// instrumentation.
-  static bool isInstrumentableLibFunc(LibFunc Func, const Value *V,
-                                      const TargetLibraryInfo *TLI);
+  /// Returns the LibFunc (or NotLibFunc) if this call should be instrumented.
+  std::optional<LibFunc>
+  shouldInstrumentCall(const CallBase &CB, const TargetLibraryInfo &TLI) const;
+
+  /// Returns true for functions that are eligible for instrumentation.
+  static bool isInstrumentableLibFunc(LibFunc Func, const CallBase &CB,
+                                      const TargetLibraryInfo &TLI);
 
   /// Returns true for isAllocationFn() functions that we should ignore.
   static bool ignoreInstrumentableLibFunc(LibFunc Func);
@@ -342,21 +345,8 @@ bool AllocToken::instrumentFunction(Function &F) {
     auto *CB = dyn_cast<CallBase>(&I);
     if (!CB)
       continue;
-    const Function *Callee = CB->getCalledFunction();
-    if (!Callee)
-      continue;
-    // Ignore nobuiltin of the CallBase, so that we can cover nobuiltin libcalls
-    // if requested via isInstrumentableLibFunc(). Note that isAllocationFn() is
-    // returning false for nobuiltin calls.
-    LibFunc Func;
-    if (TLI.getLibFunc(*Callee, Func)) {
-      if (ignoreInstrumentableLibFunc(Func))
-        continue;
-      if (isInstrumentableLibFunc(Func, CB, &TLI))
-        AllocCalls.emplace_back(CB, Func);
-    } else if (Options.Extended && getAllocTokenMetadata(*CB)) {
-      AllocCalls.emplace_back(CB, NotLibFunc);
-    }
+    if (std::optional<LibFunc> Func = shouldInstrumentCall(*CB, TLI))
+      AllocCalls.emplace_back(CB, Func.value());
   }
 
   bool Modified = false;
@@ -368,18 +358,46 @@ bool AllocToken::instrumentFunction(Function &F) {
   return Modified;
 }
 
-bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const Value *V,
-                                         const TargetLibraryInfo *TLI) {
-  if (isAllocationFn(V, TLI))
+std::optional<LibFunc>
+AllocToken::shouldInstrumentCall(const CallBase &CB,
+                                 const TargetLibraryInfo &TLI) const {
+  const Function *Callee = CB.getCalledFunction();
+  if (!Callee)
+    return std::nullopt;
+
+  // Ignore nobuiltin of the CallBase, so that we can cover nobuiltin libcalls
+  // if requested via isInstrumentableLibFunc(). Note that isAllocationFn() is
+  // returning false for nobuiltin calls.
+  LibFunc Func;
+  if (TLI.getLibFunc(*Callee, Func)) {
+    if (isInstrumentableLibFunc(Func, CB, TLI))
+      return Func;
+  } else if (Options.Extended && getAllocTokenMetadata(CB)) {
+    return NotLibFunc;
+  }
+
+  return std::nullopt;
+}
+
+bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const CallBase &CB,
+                                         const TargetLibraryInfo &TLI) {
+  if (ignoreInstrumentableLibFunc(Func))
+    return false;
+
+  if (isAllocationFn(&CB, &TLI))
     return true;
 
   switch (Func) {
+  // These libfuncs don't return normal pointers, and are therefore not handled
+  // by isAllocationFn().
   case LibFunc_posix_memalign:
   case LibFunc_size_returning_new:
   case LibFunc_size_returning_new_hot_cold:
   case LibFunc_size_returning_new_aligned:
   case LibFunc_size_returning_new_aligned_hot_cold:
     return true;
+
+  // See comment above ClCoverReplaceableNew.
   case LibFunc_Znwj:
   case LibFunc_ZnwjRKSt9nothrow_t:
   case LibFunc_ZnwjSt11align_val_t:
@@ -405,6 +423,7 @@ bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const Value *V,
   case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t:
   case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t:
     return ClCoverReplaceableNew;
+
   default:
     return false;
   }
@@ -441,7 +460,7 @@ bool AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func,
   IRBuilder<> IRB(CB);
   // Original args.
   SmallVector<Value *, 4> NewArgs{CB->args()};
-  // Add token ID.
+  // Add token ID, truncated to IntPtrTy width.
   NewArgs.push_back(ConstantInt::get(IntPtrTy, TokenID));
   assert(TokenAlloc.getFunctionType()->getNumParams() == NewArgs.size());
 
@@ -471,7 +490,7 @@ FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB,
   if (OriginalFunc != NotLibFunc) {
     Key = std::make_pair(OriginalFunc, Options.FastABI ? TokenID : 0);
     auto It = TokenAllocFunctions.find(*Key);
-    if (LLVM_LIKELY(It != TokenAllocFunctions.end()))
+    if (It != TokenAllocFunctions.end())
       return It->second;
   }
 
@@ -510,7 +529,7 @@ PreservedAnalyses AllocTokenPass::run(Module &M, ModuleAnalysisManager &MAM) {
   bool Modified = false;
 
   for (Function &F : M) {
-    if (LLVM_LIKELY(F.empty()))
+    if (F.empty())
       continue; // declaration
     Modified |= Pass.instrumentFunction(F);
   }
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index 0649e70f4f5c8..1460c7e08fe0a 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -3026,8 +3026,11 @@ static void combineMetadata(Instruction *K, const Instruction *J,
         K->setMetadata(Kind, JMD);
         break;
       case LLVMContext::MD_alloc_token:
-        // Preserve !alloc_token if both K and J have it.
-        K->setMetadata(Kind, JMD);
+        // Preserve !alloc_token if both K and J have it, and they are equal.
+        if (KMD == JMD)
+          K->setMetadata(Kind, JMD);
+        else
+          K->setMetadata(Kind, nullptr);
         break;
       }
   }
diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll
index add258ba4476b..099d37df264d6 100644
--- a/llvm/test/Instrumentation/AllocToken/basic.ll
+++ b/llvm/test/Instrumentation/AllocToken/basic.ll
@@ -2,7 +2,6 @@
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 declare ptr @malloc(i64)
 declare ptr @calloc(i64, i64)
diff --git a/llvm/test/Instrumentation/AllocToken/basic32.ll b/llvm/test/Instrumentation/AllocToken/basic32.ll
index 5f68898983cc1..944a452f4b4d7 100644
--- a/llvm/test/Instrumentation/AllocToken/basic32.ll
+++ b/llvm/test/Instrumentation/AllocToken/basic32.ll
@@ -2,7 +2,6 @@
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
 
 target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128"
-target triple = "i386-pc-linux-gnu"
 
 declare ptr @malloc(i32)
 declare ptr @_Znwm(i32)
diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
index 141c2639f81c1..a7163b93053e8 100644
--- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
+++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
@@ -4,7 +4,6 @@
 ; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 declare {ptr, i64} @__size_returning_new(i64)
 
diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll
index 9c79e94453466..19a3ef6bb9ede 100644
--- a/llvm/test/Instrumentation/AllocToken/fast.ll
+++ b/llvm/test/Instrumentation/AllocToken/fast.ll
@@ -2,7 +2,6 @@
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 declare ptr @malloc(i64)
 declare ptr @calloc(i64, i64)
diff --git a/llvm/test/Instrumentation/AllocToken/ignore.ll b/llvm/test/Instrumentation/AllocToken/ignore.ll
index ff8e85f87f64e..b92a920ed11ee 100644
--- a/llvm/test/Instrumentation/AllocToken/ignore.ll
+++ b/llvm/test/Instrumentation/AllocToken/ignore.ll
@@ -4,15 +4,14 @@
 ; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 declare ptr @strdup(ptr)
 declare ptr @__strdup(ptr)
 declare ptr @strndup(ptr, i64)
 declare ptr @__strndup(ptr, i64)
 
-define ptr @test_ignorable_allocation_functions(ptr %ptr) sanitize_alloc_token {
-; CHECK-LABEL: define ptr @test_ignorable_allocation_functions(
+define ptr @test_ignored_allocation_functions(ptr %ptr) sanitize_alloc_token {
+; CHECK-LABEL: define ptr @test_ignored_allocation_functions(
 ; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR2:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[PTR1:%.*]] = call ptr @strdup(ptr [[PTR]])
diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll
index 68cca2a6a1a33..347c99a2e8f8d 100644
--- a/llvm/test/Instrumentation/AllocToken/invoke.ll
+++ b/llvm/test/Instrumentation/AllocToken/invoke.ll
@@ -2,7 +2,6 @@
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 define ptr @test_invoke_malloc() sanitize_alloc_token personality ptr @__gxx_personality_v0 {
 ; CHECK-LABEL: define ptr @test_invoke_malloc(
diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
index 769f570ed35d2..19673da1bcfb6 100644
--- a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
+++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
@@ -2,7 +2,6 @@
 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-extended -S | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 declare ptr @malloc(i64)
 declare ptr @custom_malloc(i64)
diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll
index a4e142fe2e4cc..165fc81c2b351 100644
--- a/llvm/test/Instrumentation/AllocToken/remark.ll
+++ b/llvm/test/Instrumentation/AllocToken/remark.ll
@@ -2,7 +2,6 @@
 ; RUN: opt < %s -passes=inferattrs,alloc-token -pass-remarks=alloc-token -S 2>&1 | FileCheck %s
 
 target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
-target triple = "x86_64-unknown-linux-gnu"
 
 declare ptr @malloc(i64)
 
diff --git a/llvm/test/Instrumentation/AllocToken/typehashpointersplit.ll b/llvm/test/Instrumentation/AllocToken/typehashpointersplit.ll
new file mode 100644
index 0000000000000..1f776480c5b3a
--- /dev/null
+++ b/llvm/test/Instrumentation/AllocToken/typehashpointersplit.ll
@@ -0,0 +1,35 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=typehashpointersplit -alloc-token-max=2 -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+declare ptr @malloc(i64)
+
+define void @test_typehashpointersplit() sanitize_alloc_token {
+; CHECK-LABEL: define void @test_typehashpointersplit(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 4, i64 0), !alloc_token [[META0:![0-9]+]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token_malloc(i64 128, i64 0), !alloc_token [[META1:![0-9]+]]
+; CHECK-NEXT:    [[TMP2:%.*]] = call ptr @__alloc_token_malloc(i64 8, i64 1), !alloc_token [[META2:![0-9]+]]
+; CHECK-NEXT:    [[TMP3:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 1), !alloc_token [[META3:![0-9]+]]
+; CHECK-NEXT:    ret void
+;
+entry:
+  call ptr @malloc(i64 4), !alloc_token !0
+  call ptr @malloc(i64 128), !alloc_token !1
+  call ptr @malloc(i64 8), !alloc_token !2
+  call ptr @malloc(i64 64), !alloc_token !3
+  ret void
+}
+
+!0 = !{!"int", i1 0}
+!1 = !{!"Foo", i1 0}
+!2 = !{!"int*", i1 1}
+!3 = !{!"Foo", i1 1}
+;.
+; CHECK: [[META0]] = !{!"int", i1 false}
+; CHECK: [[META1]] = !{!"Foo", i1 false}
+; CHECK: [[META2]] = !{!"int*", i1 true}
+; CHECK: [[META3]] = !{!"Foo", i1 true}
+;.
diff --git a/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll b/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll
new file mode 100644
index 0000000000000..42d3dcc92712d
--- /dev/null
+++ b/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll
@@ -0,0 +1,104 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=simplifycfg -S | FileCheck %s
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
+
+declare ptr @_Znwm(i64)
+
+define ptr @test_merge_alloc_token_same(i1 %b) {
+; CHECK-LABEL: define ptr @test_merge_alloc_token_same(
+; CHECK-SAME: i1 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CALL:%.*]] = call ptr @_Znwm(i64 4), !alloc_token [[META0:![0-9]+]]
+; CHECK-NEXT:    ret ptr [[CALL]]
+;
+entry:
+  br i1 %b, label %if.then, label %if.else
+
+if.then:
+  %call = call ptr @_Znwm(i64 4), !alloc_token !0
+  br label %if.end
+
+if.else:
+  %call1 = call ptr @_Znwm(i64 4), !alloc_token !0
+  br label %if.end
+
+if.end:
+  %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+  ret ptr %x.0
+}
+
+define ptr @test_merge_alloc_token_different(i1 %b) {
+; CHECK-LABEL: define ptr @test_merge_alloc_token_different(
+; CHECK-SAME: i1 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CALL:%.*]] = call ptr @_Znwm(i64 4)
+; CHECK-NEXT:    ret ptr [[CALL]]
+;
+entry:
+  br i1 %b, label %if.then, label %if.else
+
+if.then:
+  %call = call ptr @_Znwm(i64 4), !alloc_token !0
+  br label %if.end
+
+if.else:
+  %call1 = call ptr @_Znwm(i64 4), !alloc_token !1
+  br label %if.end
+
+if.end:
+  %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+  ret ptr %x.0
+}
+
+define ptr @test_merge_alloc_token_some1(i1 %b) {
+; CHECK-LABEL: define ptr @test_merge_alloc_token_some1(
+; CHECK-SAME: i1 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CALL:%.*]] = call ptr @_Znwm(i64 4)
+; CHECK-NEXT:    ret ptr [[CALL]]
+;
+entry:
+  br i1 %b, label %if.then, label %if.else
+
+if.then:
+  %call = call ptr @_Znwm(i64 4), !alloc_token !0
+  br label %if.end
+
+if.else:
+  %call1 = call ptr @_Znwm(i64 4)
+  br label %if.end
+
+if.end:
+  %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+  ret ptr %x.0
+}
+
+define ptr @test_merge_alloc_token_some2(i1 %b) {
+; CHECK-LABEL: define ptr @test_merge_alloc_token_some2(
+; CHECK-SAME: i1 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[CALL:%.*]] = call ptr @_Znwm(i64 4)
+; CHECK-NEXT:    ret ptr [[CALL]]
+;
+entry:
+  br i1 %b, label %if.then, label %if.else
+
+if.then:
+  %call = call ptr @_Znwm(i64 4)
+  br label %if.end
+
+if.else:
+  %call1 = call ptr @_Znwm(i64 4), !alloc_token !0
+  br label %if.end
+
+if.end:
+  %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ]
+  ret ptr %x.0
+}
+
+!0 = !{!"int", i1 0}
+!1 = !{!"char[4]", i1 0}
+;.
+; CHECK: [[META0]] = !{!"int", i1 false}
+;.

>From 1f4e3e24d3a27f707f33f0854cb95186a2f325a2 Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Fri, 26 Sep 2025 22:37:55 +0200
Subject: [PATCH 11/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/lib/CodeGen/CGExpr.cpp                          |  2 +-
 llvm/include/llvm/Support/SipHash.h                   |  7 +++++++
 llvm/lib/Support/SipHash.cpp                          | 11 ++++++++---
 llvm/lib/Transforms/Instrumentation/AllocToken.cpp    |  6 +++---
 llvm/test/Instrumentation/AllocToken/extralibfuncs.ll |  6 +++---
 llvm/test/Instrumentation/AllocToken/remark.ll        |  2 +-
 llvm/unittests/Support/SipHashTest.cpp                |  7 +++++++
 7 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 6222a868331b3..c29a56509c423 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1312,7 +1312,7 @@ typeContainsPointer(QualType T,
     // For C++ classes, also check base classes.
     if (const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
       // Polymorphic types require a vptr.
-      if (CXXRD->isPolymorphic())
+      if (CXXRD->isDynamicClass())
         return true;
       for (const CXXBaseSpecifier &Base : CXXRD->bases()) {
         if (typeContainsPointer(Base.getType(), VisitedRD, IncompleteType))
diff --git a/llvm/include/llvm/Support/SipHash.h b/llvm/include/llvm/Support/SipHash.h
index 910cf59432c69..b090565641526 100644
--- a/llvm/include/llvm/Support/SipHash.h
+++ b/llvm/include/llvm/Support/SipHash.h
@@ -33,6 +33,13 @@ LLVM_ABI void getSipHash_2_4_64(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
 LLVM_ABI void getSipHash_2_4_128(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
                                  uint8_t (&Out)[16]);
 
+/// Compute a stable 64-bit hash of the given string.
+///
+/// The exact algorithm is the little-endian interpretation of the
+/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using
+/// a specific seed value which can be found in the source.
+LLVM_ABI uint64_t getStableSipHash(StringRef Str);
+
 /// Compute a stable non-zero 16-bit hash of the given string.
 ///
 /// The exact algorithm is the little-endian interpretation of the
diff --git a/llvm/lib/Support/SipHash.cpp b/llvm/lib/Support/SipHash.cpp
index 86dad66420435..382d36f0a8da5 100644
--- a/llvm/lib/Support/SipHash.cpp
+++ b/llvm/lib/Support/SipHash.cpp
@@ -35,14 +35,19 @@ void llvm::getSipHash_2_4_128(ArrayRef<uint8_t> In, const uint8_t (&K)[16],
   siphash<2, 4>(In.data(), In.size(), K, Out);
 }
 
-/// Compute an ABI-stable 16-bit hash of the given string.
-uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) {
+/// Compute an ABI-stable 64-bit hash of the given string.
+uint64_t llvm::getStableSipHash(StringRef Str) {
   static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79,
                                 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4};
 
   uint8_t RawHashBytes[8];
   getSipHash_2_4_64(arrayRefFromStringRef(Str), K, RawHashBytes);
-  uint64_t RawHash = endian::read64le(RawHashBytes);
+  return endian::read64le(RawHashBytes);
+}
+
+/// Compute an ABI-stable 16-bit hash of the given string.
+uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) {
+  uint64_t RawHash = getStableSipHash(Str);
 
   // Produce a non-zero 16-bit discriminator.
   uint16_t Discriminator = (RawHash % 0xFFFF) + 1;
diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
index 81d106af47a20..b1c7cf15c10be 100644
--- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -40,8 +40,8 @@
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/RandomNumberGenerator.h"
+#include "llvm/Support/SipHash.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/xxhash.h"
 #include <cassert>
 #include <cstddef>
 #include <cstdint>
@@ -194,7 +194,7 @@ class RandomMode : public ModeBase {
 };
 
 /// Implementation for TokenMode::TypeHash. The implementation ensures
-/// hashes are stable across different compiler invocations. Uses xxHash as the
+/// hashes are stable across different compiler invocations. Uses SipHash as the
 /// hash function.
 class TypeHashMode : public ModeBase {
 public:
@@ -210,7 +210,7 @@ class TypeHashMode : public ModeBase {
                                         OptimizationRemarkEmitter &ORE) {
     if (MDNode *N = getAllocTokenMetadata(CB)) {
       MDString *S = cast<MDString>(N->getOperand(0));
-      return {N, xxHash64(S->getString())};
+      return {N, getStableSipHash(S->getString())};
     }
     // Fallback.
     remarkNoMetadata(CB, ORE);
diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
index a7163b93053e8..0e382b2cebed6 100644
--- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
+++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
@@ -11,7 +11,7 @@ define ptr @test_extra_libfuncs() sanitize_alloc_token {
 ; CHECK-LABEL: define ptr @test_extra_libfuncs(
 ; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token___size_returning_new(i64 10, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token___size_returning_new(i64 10, i64 2689373973731826898), !alloc_token [[META0:![0-9]+]]
 ; CHECK-NEXT:    [[PTR1:%.*]] = extractvalue { ptr, i64 } [[TMP0]], 0
 ; CHECK-NEXT:    ret ptr [[PTR1]]
 ;
@@ -28,8 +28,8 @@ define ptr @test_replaceable_new() sanitize_alloc_token {
 ; CHECK-LABEL: define ptr @test_replaceable_new(
 ; CHECK-SAME: ) #[[ATTR1]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i64 32, i64 6985720287680550851), !alloc_token [[META0]]
-; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token__Znam(i64 64, i64 6985720287680550851), !alloc_token [[META0]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i64 32, i64 2689373973731826898), !alloc_token [[META0]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @__alloc_token__Znam(i64 64, i64 2689373973731826898), !alloc_token [[META0]]
 ; CHECK-NEXT:    ret ptr [[TMP0]]
 ;
 entry:
diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll
index 165fc81c2b351..f2eaa6209d89e 100644
--- a/llvm/test/Instrumentation/AllocToken/remark.ll
+++ b/llvm/test/Instrumentation/AllocToken/remark.ll
@@ -12,7 +12,7 @@ define ptr @test_has_metadata() sanitize_alloc_token {
 ; CHECK-LABEL: define ptr @test_has_metadata(
 ; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
-; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]]
+; CHECK-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 2689373973731826898), !alloc_token [[META0:![0-9]+]]
 ; CHECK-NEXT:    ret ptr [[TMP0]]
 ;
 entry:
diff --git a/llvm/unittests/Support/SipHashTest.cpp b/llvm/unittests/Support/SipHashTest.cpp
index 7c557eb488acc..3037e6436e18d 100644
--- a/llvm/unittests/Support/SipHashTest.cpp
+++ b/llvm/unittests/Support/SipHashTest.cpp
@@ -50,6 +50,13 @@ TEST(SipHashTest, SipHash_2_4_128) {
   }
 }
 
+// Tests for the 64-bit stable SipHash wrapper.
+TEST(SipHashTest, StableSipHash) {
+  EXPECT_EQ(0xB2BB69BB0A2AC0F1UL, getStableSipHash(""));
+  EXPECT_EQ(0x9304ABFF427B72E8UL, getStableSipHash("strlen"));
+  EXPECT_EQ(0x55F45179A08AE51BUL, getStableSipHash("_ZN1 ind; f"));
+}
+
 // Tests for the ptrauth-specific SipHash wrapper.
 TEST(SipHashTest, PointerAuthSipHash) {
   // Test some basic cases.

>From 02b014d9c8cb8bccb822f2b1896443e50f228e2c Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Thu, 2 Oct 2025 16:45:16 +0200
Subject: [PATCH 12/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/lib/CodeGen/CGExpr.cpp                       | 4 +---
 llvm/lib/Transforms/Instrumentation/AllocToken.cpp | 4 +++-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 349e6fc16a815..005d32926d8a1 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1284,12 +1284,10 @@ typeContainsPointer(QualType T,
   for (QualType CurrentT = T; const auto *TT = CurrentT->getAs<TypedefType>();
        CurrentT = TT->getDecl()->getUnderlyingType()) {
     const IdentifierInfo *II = TT->getDecl()->getIdentifier();
-    if (!II)
-      continue;
     // Special Case: Syntactically uintptr_t is not a pointer; semantically,
     // however, very likely used as such. Therefore, classify uintptr_t as a
     // pointer, too.
-    if (II->isStr("uintptr_t"))
+    if (II && II->isStr("uintptr_t"))
       return true;
   }
 
diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
index a797c9a1fc678..40720ae4b39ae 100644
--- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -158,7 +158,9 @@ bool containsPointer(const MDNode *MD) {
 class ModeBase {
 public:
   explicit ModeBase(const IntegerType &TokenTy, uint64_t MaxTokens)
-      : MaxTokens(MaxTokens ? MaxTokens : TokenTy.getBitMask()) {}
+      : MaxTokens(MaxTokens ? MaxTokens : TokenTy.getBitMask()) {
+    assert(MaxTokens <= TokenTy.getBitMask());
+  }
 
 protected:
   uint64_t boundedToken(uint64_t Val) const {

>From e34c2d9396be5050efd1b024dc9f2ce21cb3863a Mon Sep 17 00:00:00 2001
From: Marco Elver <elver at google.com>
Date: Thu, 2 Oct 2025 19:05:21 +0200
Subject: [PATCH 13/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?=
 =?UTF-8?q?changes=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.8-beta.1

[skip ci]
---
 clang/test/CodeGen/alloc-token-lower.c        |  50 +---
 clang/test/CodeGen/alloc-token.c              |  33 +--
 clang/test/CodeGenCXX/alloc-token-pointer.cpp | 184 +++------------
 clang/test/CodeGenCXX/alloc-token.cpp         | 217 +++---------------
 4 files changed, 73 insertions(+), 411 deletions(-)

diff --git a/clang/test/CodeGen/alloc-token-lower.c b/clang/test/CodeGen/alloc-token-lower.c
index 722fd121440e4..75197bb3dbd44 100644
--- a/clang/test/CodeGen/alloc-token-lower.c
+++ b/clang/test/CodeGen/alloc-token-lower.c
@@ -1,58 +1,22 @@
-// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
-//
 // Test optimization pipelines do not interfere with AllocToken lowering, and we
 // pass on function attributes correctly.
 //
-// RUN: %clang_cc1     -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s
-// RUN: %clang_cc1 -O1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O1 %s
-// RUN: %clang_cc1 -O2 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s
+// RUN: %clang_cc1     -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -O1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
+// RUN: %clang_cc1 -O2 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s
 
 typedef __typeof(sizeof(int)) size_t;
 
 void *malloc(size_t size);
 
-// CHECK-O0-LABEL: define dso_local ptr @test_malloc(
-// CHECK-O0-SAME: ) #[[ATTR0:[0-9]+]] {
-// CHECK-O0-NEXT:  [[ENTRY:.*:]]
-// CHECK-O0-NEXT:    [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR3:[0-9]+]]
-// CHECK-O0-NEXT:    ret ptr [[TMP0]]
-//
-// CHECK-O1-LABEL: define dso_local noalias noundef ptr @test_malloc(
-// CHECK-O1-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
-// CHECK-O1-NEXT:  [[ENTRY:.*:]]
-// CHECK-O1-NEXT:    [[TMP0:%.*]] = tail call dereferenceable_or_null(4) ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR3:[0-9]+]]
-// CHECK-O1-NEXT:    ret ptr [[TMP0]]
-//
-// CHECK-O2-LABEL: define dso_local noalias noundef ptr @test_malloc(
-// CHECK-O2-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
-// CHECK-O2-NEXT:  [[ENTRY:.*:]]
-// CHECK-O2-NEXT:    [[TMP0:%.*]] = tail call dereferenceable_or_null(4) ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR3:[0-9]+]]
-// CHECK-O2-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK-LABEL: @test_malloc(
+// CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0)
 void *test_malloc() {
   return malloc(sizeof(int));
 }
 
-// CHECK-O0-LABEL: define dso_local ptr @no_sanitize_malloc(
-// CHECK-O0-SAME: i64 noundef [[SIZE:%.*]]) #[[ATTR2:[0-9]+]] {
-// CHECK-O0-NEXT:  [[ENTRY:.*:]]
-// CHECK-O0-NEXT:    [[SIZE_ADDR:%.*]] = alloca i64, align 8
-// CHECK-O0-NEXT:    store i64 [[SIZE]], ptr [[SIZE_ADDR]], align 8
-// CHECK-O0-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR3]]
-// CHECK-O0-NEXT:    ret ptr [[CALL]]
-//
-// CHECK-O1-LABEL: define dso_local noalias noundef ptr @no_sanitize_malloc(
-// CHECK-O1-SAME: i64 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
-// CHECK-O1-NEXT:  [[ENTRY:.*:]]
-// CHECK-O1-NEXT:    [[CALL:%.*]] = tail call dereferenceable_or_null(4) ptr @malloc(i64 noundef 4) #[[ATTR3]]
-// CHECK-O1-NEXT:    ret ptr [[CALL]]
-//
-// CHECK-O2-LABEL: define dso_local noalias noundef ptr @no_sanitize_malloc(
-// CHECK-O2-SAME: i64 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
-// CHECK-O2-NEXT:  [[ENTRY:.*:]]
-// CHECK-O2-NEXT:    [[CALL:%.*]] = tail call dereferenceable_or_null(4) ptr @malloc(i64 noundef 4) #[[ATTR3]]
-// CHECK-O2-NEXT:    ret ptr [[CALL]]
-//
+// CHECK-LABEL: @no_sanitize_malloc(
+// CHECK: call{{.*}} ptr @malloc(i64 noundef 4)
 void *no_sanitize_malloc(size_t size) __attribute__((no_sanitize("alloc-token"))) {
   return malloc(sizeof(int));
 }
diff --git a/clang/test/CodeGen/alloc-token.c b/clang/test/CodeGen/alloc-token.c
index ec789c069f985..d1160adc060ba 100644
--- a/clang/test/CodeGen/alloc-token.c
+++ b/clang/test/CodeGen/alloc-token.c
@@ -1,4 +1,3 @@
-// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
 // RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
 
 typedef __typeof(sizeof(int)) size_t;
@@ -16,29 +15,15 @@ int posix_memalign(void **memptr, size_t alignment, size_t size);
 void *sink;
 
 // CHECK-LABEL: define dso_local void @test_malloc_like(
-// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR5:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL1:%.*]] = call ptr @calloc(i64 noundef 3, i64 noundef 4) #[[ATTR6:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL1]], ptr @sink, align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr @sink, align 8
-// CHECK-NEXT:    [[CALL2:%.*]] = call ptr @realloc(ptr noundef [[TMP0]], i64 noundef 8) #[[ATTR7:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL2]], ptr @sink, align 8
-// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr @sink, align 8
-// CHECK-NEXT:    [[CALL3:%.*]] = call ptr @reallocarray(ptr noundef [[TMP1]], i64 noundef 5, i64 noundef 8)
-// CHECK-NEXT:    store ptr [[CALL3]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL4:%.*]] = call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) #[[ATTR7]]
-// CHECK-NEXT:    store ptr [[CALL4]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL5:%.*]] = call align 16 ptr @memalign(i64 noundef 16, i64 noundef 256) #[[ATTR7]]
-// CHECK-NEXT:    store ptr [[CALL5]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL6:%.*]] = call ptr @valloc(i64 noundef 4096)
-// CHECK-NEXT:    store ptr [[CALL6]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL7:%.*]] = call ptr @pvalloc(i64 noundef 8192)
-// CHECK-NEXT:    store ptr [[CALL7]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL8:%.*]] = call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4)
-// CHECK-NEXT:    ret void
-//
+// CHECK: call ptr @malloc(i64 noundef 4)
+// CHECK: call ptr @calloc(i64 noundef 3, i64 noundef 4)
+// CHECK: call ptr @realloc(ptr noundef {{.*}}, i64 noundef 8)
+// CHECK: call ptr @reallocarray(ptr noundef {{.*}}, i64 noundef 5, i64 noundef 8)
+// CHECK: call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024)
+// CHECK: call align 16 ptr @memalign(i64 noundef 16, i64 noundef 256)
+// CHECK: call ptr @valloc(i64 noundef 4096)
+// CHECK: call ptr @pvalloc(i64 noundef 8192)
+// CHECK: call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4)
 void test_malloc_like() {
   sink = malloc(sizeof(int));
   sink = calloc(3, sizeof(int));
diff --git a/clang/test/CodeGenCXX/alloc-token-pointer.cpp b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
index 74f1ae850d19d..4781d1b4c13f0 100644
--- a/clang/test/CodeGenCXX/alloc-token-pointer.cpp
+++ b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
@@ -1,4 +1,3 @@
-// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
 // RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -std=c++20 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
 
 #include "../Analysis/Inputs/system-header-simulator-cxx.h"
@@ -10,16 +9,7 @@ void *malloc(size_t size);
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_intv(
-// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[A:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR4:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[A]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A]], align 8
-// CHECK-NEXT:    store i32 42, ptr [[TMP0]], align 4
-// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[A]], align 8
-// CHECK-NEXT:    ret ptr [[TMP1]]
-//
+// CHECK: call ptr @malloc(i64 noundef 4)
 void *test_malloc_int() {
   int *a = (int *)malloc(sizeof(int));
   *a = 42;
@@ -27,16 +17,7 @@ void *test_malloc_int() {
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_ptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[A:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 8) #[[ATTR4]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[A]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[A]], align 8
-// CHECK-NEXT:    store ptr null, ptr [[TMP0]], align 8
-// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[A]], align 8
-// CHECK-NEXT:    ret ptr [[TMP1]]
-//
+// CHECK: call ptr @malloc(i64 noundef 8)
 int **test_malloc_ptr() {
   int **a = (int **)malloc(sizeof(int*));
   *a = nullptr;
@@ -44,41 +25,25 @@ int **test_malloc_ptr() {
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_intv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR5:[0-9]+]], !alloc_token [[META2:![0-9]+]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]]
 int *test_new_int() {
   return new int;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_ulong_arrayv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 80) #[[ATTR5]], !alloc_token [[META3:![0-9]+]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 80){{.*}} !alloc_token [[META_ULONG:![0-9]+]]
 unsigned long *test_new_ulong_array() {
   return new unsigned long[10];
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_ptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #[[ATTR5]], !alloc_token [[META4:![0-9]+]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 8){{.*}} !alloc_token [[META_INTPTR:![0-9]+]]
 int **test_new_ptr() {
   return new int*;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z18test_new_ptr_arrayv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 80) #[[ATTR5]], !alloc_token [[META4]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 80){{.*}} !alloc_token [[META_INTPTR]]
 int **test_new_ptr_array() {
   return new int*[10];
 }
@@ -89,105 +54,55 @@ struct ContainsPtr {
 };
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z27test_malloc_struct_with_ptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 16) #[[ATTR4]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
-// CHECK-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK: call ptr @malloc(i64 noundef 16)
 ContainsPtr *test_malloc_struct_with_ptr() {
   ContainsPtr *c = (ContainsPtr *)malloc(sizeof(ContainsPtr));
   return c;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z33test_malloc_struct_array_with_ptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 160) #[[ATTR4]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
-// CHECK-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK: call ptr @malloc(i64 noundef 160)
 ContainsPtr *test_malloc_struct_array_with_ptr() {
   ContainsPtr *c = (ContainsPtr *)malloc(10 * sizeof(ContainsPtr));
   return c;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z32test_operatornew_struct_with_ptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) #[[ATTR5]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
-// CHECK-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16)
 ContainsPtr *test_operatornew_struct_with_ptr() {
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(ContainsPtr));
   return c;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z38test_operatornew_struct_array_with_ptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 160) #[[ATTR5]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
-// CHECK-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160)
 ContainsPtr *test_operatornew_struct_array_with_ptr() {
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(ContainsPtr));
   return c;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z33test_operatornew_struct_with_ptr2v(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) #[[ATTR5]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
-// CHECK-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16)
 ContainsPtr *test_operatornew_struct_with_ptr2() {
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(*c));
   return c;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z39test_operatornew_struct_array_with_ptr2v(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[C:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 160) #[[ATTR5]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[C]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[C]], align 8
-// CHECK-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160)
 ContainsPtr *test_operatornew_struct_array_with_ptr2() {
   ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(*c));
   return c;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z24test_new_struct_with_ptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) #[[ATTR5]], !alloc_token [[META5:![0-9]+]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR:![0-9]+]]
 ContainsPtr *test_new_struct_with_ptr() {
   return new ContainsPtr;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z30test_new_struct_array_with_ptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 160) #[[ATTR5]], !alloc_token [[META5]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 160){{.*}} !alloc_token [[META_CONTAINSPTR]]
 ContainsPtr *test_new_struct_array_with_ptr() {
   return new ContainsPtr[10];
 }
@@ -200,24 +115,13 @@ class TestClass {
 };
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_classv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 64) #[[ATTR5]], !alloc_token [[META6:![0-9]+]]
-// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 64, i1 false)
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 64){{.*}} !alloc_token [[META_TESTCLASS:![0-9]+]]
 TestClass *test_new_class() {
   return new TestClass();
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_class_arrayv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 648) #[[ATTR5]], !alloc_token [[META6]]
-// CHECK-NEXT:    store i64 10, ptr [[CALL]], align 16
-// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
-// CHECK-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 648){{.*}} !alloc_token [[META_TESTCLASS]]
 TestClass *test_new_class_array() {
   return new TestClass[10];
 }
@@ -231,34 +135,13 @@ class VirtualTestClass {
 };
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z22test_new_virtual_classv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR5]], !alloc_token [[META7:![0-9]+]]
-// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false)
-// CHECK-NEXT:    call void @_ZN16VirtualTestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR6:[0-9]+]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 72){{.*}} !alloc_token [[META_VIRTUALTESTCLASS:![0-9]+]]
 VirtualTestClass *test_new_virtual_class() {
   return new VirtualTestClass();
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z28test_new_virtual_class_arrayv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*]]:
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 728) #[[ATTR5]], !alloc_token [[META7]]
-// CHECK-NEXT:    store i64 10, ptr [[CALL]], align 16
-// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
-// CHECK-NEXT:    [[ARRAYCTOR_END:%.*]] = getelementptr inbounds [[CLASS_VIRTUALTESTCLASS:%.*]], ptr [[TMP0]], i64 10
-// CHECK-NEXT:    br label %[[ARRAYCTOR_LOOP:.*]]
-// CHECK:       [[ARRAYCTOR_LOOP]]:
-// CHECK-NEXT:    [[ARRAYCTOR_CUR:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[ARRAYCTOR_NEXT:%.*]], %[[ARRAYCTOR_LOOP]] ]
-// CHECK-NEXT:    call void @_ZN16VirtualTestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[ARRAYCTOR_CUR]]) #[[ATTR6]]
-// CHECK-NEXT:    [[ARRAYCTOR_NEXT]] = getelementptr inbounds [[CLASS_VIRTUALTESTCLASS]], ptr [[ARRAYCTOR_CUR]], i64 1
-// CHECK-NEXT:    [[ARRAYCTOR_DONE:%.*]] = icmp eq ptr [[ARRAYCTOR_NEXT]], [[ARRAYCTOR_END]]
-// CHECK-NEXT:    br i1 [[ARRAYCTOR_DONE]], label %[[ARRAYCTOR_CONT:.*]], label %[[ARRAYCTOR_LOOP]]
-// CHECK:       [[ARRAYCTOR_CONT]]:
-// CHECK-NEXT:    ret ptr [[TMP0]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 728){{.*}} !alloc_token [[META_VIRTUALTESTCLASS]]
 VirtualTestClass *test_new_virtual_class_array() {
   return new VirtualTestClass[10];
 }
@@ -270,32 +153,23 @@ struct MyStructUintptr {
 };
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z18test_uintptr_isptrv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) #[[ATTR5]], !alloc_token [[META8:![0-9]+]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_MYSTRUCTUINTPTR:![0-9]+]]
 MyStructUintptr *test_uintptr_isptr() {
   return new MyStructUintptr;
 }
 
 using uptr = uintptr_t;
 // CHECK-LABEL: define dso_local noundef ptr @_Z19test_uintptr_isptr2v(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 8) #[[ATTR5]], !alloc_token [[META9:![0-9]+]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 8){{.*}} !alloc_token [[META_UINTPTR:![0-9]+]]
 uptr *test_uintptr_isptr2() {
   return new uptr;
 }
-//.
-// CHECK: [[META2]] = !{!"int", i1 false}
-// CHECK: [[META3]] = !{!"unsigned long", i1 false}
-// CHECK: [[META4]] = !{!"int *", i1 true}
-// CHECK: [[META5]] = !{!"ContainsPtr", i1 true}
-// CHECK: [[META6]] = !{!"TestClass", i1 false}
-// CHECK: [[META7]] = !{!"VirtualTestClass", i1 true}
-// CHECK: [[META8]] = !{!"MyStructUintptr", i1 true}
-// CHECK: [[META9]] = !{!"unsigned long", i1 true}
-//.
+
+// CHECK: [[META_INT]] = !{!"int", i1 false}
+// CHECK: [[META_ULONG]] = !{!"unsigned long", i1 false}
+// CHECK: [[META_INTPTR]] = !{!"int *", i1 true}
+// CHECK: [[META_CONTAINSPTR]] = !{!"ContainsPtr", i1 true}
+// CHECK: [[META_TESTCLASS]] = !{!"TestClass", i1 false}
+// CHECK: [[META_VIRTUALTESTCLASS]] = !{!"VirtualTestClass", i1 true}
+// CHECK: [[META_MYSTRUCTUINTPTR]] = !{!"MyStructUintptr", i1 true}
+// CHECK: [[META_UINTPTR]] = !{!"unsigned long", i1 true}
diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp
index ab6b4e4d8917c..5914b4ca5ef23 100644
--- a/clang/test/CodeGenCXX/alloc-token.cpp
+++ b/clang/test/CodeGenCXX/alloc-token.cpp
@@ -1,4 +1,3 @@
-// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
 // RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s
 
 #include "../Analysis/Inputs/system-header-simulator-cxx.h"
@@ -27,29 +26,15 @@ __sized_ptr_t __size_returning_new_aligned_hot_cold(size_t, std::align_val_t,  _
 void *sink; // prevent optimizations from removing the calls
 
 // CHECK-LABEL: define dso_local void @_Z16test_malloc_likev(
-// CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR10:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL1:%.*]] = call ptr @calloc(i64 noundef 3, i64 noundef 4) #[[ATTR11:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL1]], ptr @sink, align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr @sink, align 8
-// CHECK-NEXT:    [[CALL2:%.*]] = call ptr @realloc(ptr noundef [[TMP0]], i64 noundef 8) #[[ATTR12:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL2]], ptr @sink, align 8
-// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr @sink, align 8
-// CHECK-NEXT:    [[CALL3:%.*]] = call ptr @reallocarray(ptr noundef [[TMP1]], i64 noundef 5, i64 noundef 8)
-// CHECK-NEXT:    store ptr [[CALL3]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL4:%.*]] = call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) #[[ATTR12]]
-// CHECK-NEXT:    store ptr [[CALL4]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL5:%.*]] = call ptr @memalign(i64 noundef 16, i64 noundef 256)
-// CHECK-NEXT:    store ptr [[CALL5]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL6:%.*]] = call ptr @valloc(i64 noundef 4096)
-// CHECK-NEXT:    store ptr [[CALL6]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL7:%.*]] = call ptr @pvalloc(i64 noundef 8192)
-// CHECK-NEXT:    store ptr [[CALL7]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL8:%.*]] = call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4)
-// CHECK-NEXT:    ret void
-//
+// CHECK: call ptr @malloc(i64 noundef 4)
+// CHECK: call ptr @calloc(i64 noundef 3, i64 noundef 4)
+// CHECK: call ptr @realloc(ptr noundef {{.*}}, i64 noundef 8)
+// CHECK: call ptr @reallocarray(ptr noundef {{.*}}, i64 noundef 5, i64 noundef 8)
+// CHECK: call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024)
+// CHECK: call ptr @memalign(i64 noundef 16, i64 noundef 256)
+// CHECK: call ptr @valloc(i64 noundef 4096)
+// CHECK: call ptr @pvalloc(i64 noundef 8192)
+// CHECK: call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4)
 void test_malloc_like() {
   sink = malloc(sizeof(int));
   sink = calloc(3, sizeof(int));
@@ -63,122 +48,50 @@ void test_malloc_like() {
 }
 
 // CHECK-LABEL: define dso_local void @_Z17test_operator_newv(
-// CHECK-SAME: ) #[[ATTR1]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR13:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL1:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR10]]
-// CHECK-NEXT:    store ptr [[CALL1]], ptr @sink, align 8
-// CHECK-NEXT:    ret void
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4)
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4)
 void test_operator_new() {
   sink = __builtin_operator_new(sizeof(int));
   sink = ::operator new(sizeof(int));
 }
 
 // CHECK-LABEL: define dso_local void @_Z25test_operator_new_nothrowv(
-// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL1:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR15:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL1]], ptr @sink, align 8
-// CHECK-NEXT:    ret void
-//
+// CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow)
+// CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow)
 void test_operator_new_nothrow() {
   sink = __builtin_operator_new(sizeof(int), std::nothrow);
   sink = ::operator new(sizeof(int), std::nothrow);
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z8test_newv(
-// CHECK-SAME: ) #[[ATTR1]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR13]], !alloc_token [[META2:![0-9]+]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]]
 int *test_new() {
   return new int;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_arrayv(
-// CHECK-SAME: ) #[[ATTR1]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 40) #[[ATTR13]], !alloc_token [[META2]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 40){{.*}} !alloc_token [[META_INT]]
 int *test_new_array() {
   return new int[10];
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z16test_new_nothrowv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14]], !alloc_token [[META2]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow){{.*}} !alloc_token [[META_INT]]
 int *test_new_nothrow() {
   return new (std::nothrow) int;
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z22test_new_array_nothrowv(
-// CHECK-SAME: ) #[[ATTR0]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef ptr @_ZnamRKSt9nothrow_t(i64 noundef 40, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14]], !alloc_token [[META2]]
-// CHECK-NEXT:    ret ptr [[CALL]]
-//
+// CHECK: call noalias noundef ptr @_ZnamRKSt9nothrow_t(i64 noundef 40, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow){{.*}} !alloc_token [[META_INT]]
 int *test_new_array_nothrow() {
   return new (std::nothrow) int[10];
 }
 
 // CHECK-LABEL: define dso_local void @_Z23test_size_returning_newv(
-// CHECK-SAME: ) #[[ATTR1]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[REF_TMP:%.*]] = alloca [[STRUCT___SIZED_PTR_T:%.*]], align 8
-// CHECK-NEXT:    [[REF_TMP1:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8
-// CHECK-NEXT:    [[REF_TMP4:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8
-// CHECK-NEXT:    [[REF_TMP7:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call { ptr, i64 } @__size_returning_new(i64 noundef 8)
-// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP]], i32 0, i32 0
-// CHECK-NEXT:    [[TMP1:%.*]] = extractvalue { ptr, i64 } [[CALL]], 0
-// CHECK-NEXT:    store ptr [[TMP1]], ptr [[TMP0]], align 8
-// CHECK-NEXT:    [[TMP2:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP]], i32 0, i32 1
-// CHECK-NEXT:    [[TMP3:%.*]] = extractvalue { ptr, i64 } [[CALL]], 1
-// CHECK-NEXT:    store i64 [[TMP3]], ptr [[TMP2]], align 8
-// CHECK-NEXT:    [[P:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP]], i32 0, i32 0
-// CHECK-NEXT:    [[TMP4:%.*]] = load ptr, ptr [[P]], align 8
-// CHECK-NEXT:    store ptr [[TMP4]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL2:%.*]] = call { ptr, i64 } @__size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1)
-// CHECK-NEXT:    [[TMP5:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP1]], i32 0, i32 0
-// CHECK-NEXT:    [[TMP6:%.*]] = extractvalue { ptr, i64 } [[CALL2]], 0
-// CHECK-NEXT:    store ptr [[TMP6]], ptr [[TMP5]], align 8
-// CHECK-NEXT:    [[TMP7:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP1]], i32 0, i32 1
-// CHECK-NEXT:    [[TMP8:%.*]] = extractvalue { ptr, i64 } [[CALL2]], 1
-// CHECK-NEXT:    store i64 [[TMP8]], ptr [[TMP7]], align 8
-// CHECK-NEXT:    [[P3:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP1]], i32 0, i32 0
-// CHECK-NEXT:    [[TMP9:%.*]] = load ptr, ptr [[P3]], align 8
-// CHECK-NEXT:    store ptr [[TMP9]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL5:%.*]] = call { ptr, i64 } @__size_returning_new_aligned(i64 noundef 8, i64 noundef 32)
-// CHECK-NEXT:    [[TMP10:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP4]], i32 0, i32 0
-// CHECK-NEXT:    [[TMP11:%.*]] = extractvalue { ptr, i64 } [[CALL5]], 0
-// CHECK-NEXT:    store ptr [[TMP11]], ptr [[TMP10]], align 8
-// CHECK-NEXT:    [[TMP12:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP4]], i32 0, i32 1
-// CHECK-NEXT:    [[TMP13:%.*]] = extractvalue { ptr, i64 } [[CALL5]], 1
-// CHECK-NEXT:    store i64 [[TMP13]], ptr [[TMP12]], align 8
-// CHECK-NEXT:    [[P6:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP4]], i32 0, i32 0
-// CHECK-NEXT:    [[TMP14:%.*]] = load ptr, ptr [[P6]], align 8
-// CHECK-NEXT:    store ptr [[TMP14]], ptr @sink, align 8
-// CHECK-NEXT:    [[CALL8:%.*]] = call { ptr, i64 } @__size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1)
-// CHECK-NEXT:    [[TMP15:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP7]], i32 0, i32 0
-// CHECK-NEXT:    [[TMP16:%.*]] = extractvalue { ptr, i64 } [[CALL8]], 0
-// CHECK-NEXT:    store ptr [[TMP16]], ptr [[TMP15]], align 8
-// CHECK-NEXT:    [[TMP17:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP7]], i32 0, i32 1
-// CHECK-NEXT:    [[TMP18:%.*]] = extractvalue { ptr, i64 } [[CALL8]], 1
-// CHECK-NEXT:    store i64 [[TMP18]], ptr [[TMP17]], align 8
-// CHECK-NEXT:    [[P9:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP7]], i32 0, i32 0
-// CHECK-NEXT:    [[TMP19:%.*]] = load ptr, ptr [[P9]], align 8
-// CHECK-NEXT:    store ptr [[TMP19]], ptr @sink, align 8
-// CHECK-NEXT:    ret void
-//
+// CHECK: call { ptr, i64 } @__size_returning_new(i64 noundef 8)
+// CHECK: call { ptr, i64 } @__size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1)
+// CHECK: call { ptr, i64 } @__size_returning_new_aligned(i64 noundef 8, i64 noundef 32)
+// CHECK: call { ptr, i64 } @__size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1)
 void test_size_returning_new() {
   sink = __size_returning_new(sizeof(long)).p;
   sink = __size_returning_new_hot_cold(sizeof(long), __hot_cold_t{1}).p;
@@ -196,45 +109,8 @@ class TestClass {
 void may_throw();
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z27test_exception_handling_newv(
-// CHECK-SAME: ) #[[ATTR1]] personality ptr @__gxx_personality_v0 {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[RETVAL:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[OBJ:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[EXN_SLOT:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4
-// CHECK-NEXT:    [[CALL:%.*]] = invoke noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR13]]
-// CHECK-NEXT:            to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]], !alloc_token [[META3:![0-9]+]]
-// CHECK:       [[INVOKE_CONT]]:
-// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false)
-// CHECK-NEXT:    call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR16:[0-9]+]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[OBJ]], align 8
-// CHECK-NEXT:    invoke void @_Z9may_throwv()
-// CHECK-NEXT:            to label %[[INVOKE_CONT1:.*]] unwind label %[[LPAD]]
-// CHECK:       [[INVOKE_CONT1]]:
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[OBJ]], align 8
-// CHECK-NEXT:    store ptr [[TMP0]], ptr [[RETVAL]], align 8
-// CHECK-NEXT:    br label %[[RETURN:.*]]
-// CHECK:       [[LPAD]]:
-// CHECK-NEXT:    [[TMP1:%.*]] = landingpad { ptr, i32 }
-// CHECK-NEXT:            catch ptr null
-// CHECK-NEXT:    [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 0
-// CHECK-NEXT:    store ptr [[TMP2]], ptr [[EXN_SLOT]], align 8
-// CHECK-NEXT:    [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 1
-// CHECK-NEXT:    store i32 [[TMP3]], ptr [[EHSELECTOR_SLOT]], align 4
-// CHECK-NEXT:    br label %[[CATCH:.*]]
-// CHECK:       [[CATCH]]:
-// CHECK-NEXT:    [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8
-// CHECK-NEXT:    [[TMP4:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR16]]
-// CHECK-NEXT:    store ptr null, ptr [[RETVAL]], align 8
-// CHECK-NEXT:    call void @__cxa_end_catch()
-// CHECK-NEXT:    br label %[[RETURN]]
-// CHECK:       [[TRY_CONT:.*:]]
-// CHECK-NEXT:    call void @llvm.trap()
-// CHECK-NEXT:    unreachable
-// CHECK:       [[RETURN]]:
-// CHECK-NEXT:    [[TMP5:%.*]] = load ptr, ptr [[RETVAL]], align 8
-// CHECK-NEXT:    ret ptr [[TMP5]]
-//
+// CHECK: invoke noalias noundef nonnull ptr @_Znwm(i64 noundef 72)
+// CHECK-NEXT: !alloc_token [[META_TESTCLASS:![0-9]+]]
 TestClass *test_exception_handling_new() {
   try {
     TestClass *obj = new TestClass();
@@ -246,20 +122,7 @@ TestClass *test_exception_handling_new() {
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_classv(
-// CHECK-SAME: ) #[[ATTR1]] {
-// CHECK-NEXT:  [[ENTRY:.*:]]
-// CHECK-NEXT:    [[OBJ:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR13]], !alloc_token [[META3]]
-// CHECK-NEXT:    call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false)
-// CHECK-NEXT:    call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR16]]
-// CHECK-NEXT:    store ptr [[CALL]], ptr [[OBJ]], align 8
-// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[OBJ]], align 8
-// CHECK-NEXT:    [[DATA:%.*]] = getelementptr inbounds nuw [[CLASS_TESTCLASS:%.*]], ptr [[TMP0]], i32 0, i32 1
-// CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [16 x i32], ptr [[DATA]], i64 0, i64 0
-// CHECK-NEXT:    store i32 42, ptr [[ARRAYIDX]], align 8
-// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[OBJ]], align 8
-// CHECK-NEXT:    ret ptr [[TMP1]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 72){{.*}} !alloc_token [[META_TESTCLASS]]
 TestClass *test_new_class() {
   TestClass *obj = new TestClass();
   obj->data[0] = 42;
@@ -267,36 +130,12 @@ TestClass *test_new_class() {
 }
 
 // CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_class_arrayv(
-// CHECK-SAME: ) #[[ATTR1]] {
-// CHECK-NEXT:  [[ENTRY:.*]]:
-// CHECK-NEXT:    [[ARR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 728) #[[ATTR13]], !alloc_token [[META3]]
-// CHECK-NEXT:    store i64 10, ptr [[CALL]], align 16
-// CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8
-// CHECK-NEXT:    [[ARRAYCTOR_END:%.*]] = getelementptr inbounds [[CLASS_TESTCLASS:%.*]], ptr [[TMP0]], i64 10
-// CHECK-NEXT:    br label %[[ARRAYCTOR_LOOP:.*]]
-// CHECK:       [[ARRAYCTOR_LOOP]]:
-// CHECK-NEXT:    [[ARRAYCTOR_CUR:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[ARRAYCTOR_NEXT:%.*]], %[[ARRAYCTOR_LOOP]] ]
-// CHECK-NEXT:    call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[ARRAYCTOR_CUR]]) #[[ATTR16]]
-// CHECK-NEXT:    [[ARRAYCTOR_NEXT]] = getelementptr inbounds [[CLASS_TESTCLASS]], ptr [[ARRAYCTOR_CUR]], i64 1
-// CHECK-NEXT:    [[ARRAYCTOR_DONE:%.*]] = icmp eq ptr [[ARRAYCTOR_NEXT]], [[ARRAYCTOR_END]]
-// CHECK-NEXT:    br i1 [[ARRAYCTOR_DONE]], label %[[ARRAYCTOR_CONT:.*]], label %[[ARRAYCTOR_LOOP]]
-// CHECK:       [[ARRAYCTOR_CONT]]:
-// CHECK-NEXT:    store ptr [[TMP0]], ptr [[ARR]], align 8
-// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[ARR]], align 8
-// CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [[CLASS_TESTCLASS]], ptr [[TMP1]], i64 0
-// CHECK-NEXT:    [[DATA:%.*]] = getelementptr inbounds nuw [[CLASS_TESTCLASS]], ptr [[ARRAYIDX]], i32 0, i32 1
-// CHECK-NEXT:    [[ARRAYIDX1:%.*]] = getelementptr inbounds [16 x i32], ptr [[DATA]], i64 0, i64 0
-// CHECK-NEXT:    store i32 123, ptr [[ARRAYIDX1]], align 8
-// CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[ARR]], align 8
-// CHECK-NEXT:    ret ptr [[TMP2]]
-//
+// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 728){{.*}} !alloc_token [[META_TESTCLASS]]
 TestClass *test_new_class_array() {
   TestClass* arr = new TestClass[10];
   arr[0].data[0] = 123;
   return arr;
 }
-//.
-// CHECK: [[META2]] = !{!"int", i1 false}
-// CHECK: [[META3]] = !{!"TestClass", i1 true}
-//.
+
+// CHECK: [[META_INT]] = !{!"int", i1 false}
+// CHECK: [[META_TESTCLASS]] = !{!"TestClass", i1 true}



More information about the llvm-commits mailing list