[llvm] 6359980 - [AllocToken, Clang] Implement TypeHashPointerSplit mode (#156840)

via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 8 12:58:42 PDT 2025


Author: Marco Elver
Date: 2025-10-08T21:58:37+02:00
New Revision: 6359980f5b555f95429139c2e2e9530d75d4e78f

URL: https://github.com/llvm/llvm-project/commit/6359980f5b555f95429139c2e2e9530d75d4e78f
DIFF: https://github.com/llvm/llvm-project/commit/6359980f5b555f95429139c2e2e9530d75d4e78f.diff

LOG: [AllocToken, Clang] Implement TypeHashPointerSplit mode (#156840)

Implement the TypeHashPointerSplit mode: 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.

This mode with max tokens of 2 (`-falloc-token-max=2`) may also
be valuable for heap hardening strategies that simply separate pointer
types from non-pointer types.

Make it the new default mode.

Link: https://discourse.llvm.org/t/rfc-a-framework-for-allocator-partitioning-hints/87434

---

This change is part of the following series:
  1. https://github.com/llvm/llvm-project/pull/160131
  2. https://github.com/llvm/llvm-project/pull/156838
  3. https://github.com/llvm/llvm-project/pull/162098
  4. https://github.com/llvm/llvm-project/pull/162099
  5. https://github.com/llvm/llvm-project/pull/156839
  6. https://github.com/llvm/llvm-project/pull/156840
  7. https://github.com/llvm/llvm-project/pull/156841
  8. https://github.com/llvm/llvm-project/pull/156842

Added: 
    clang/test/CodeGenCXX/alloc-token-pointer.cpp
    llvm/test/Instrumentation/AllocToken/typehashpointersplit.ll

Modified: 
    clang/docs/AllocToken.rst
    clang/lib/CodeGen/CGExpr.cpp
    clang/test/CodeGenCXX/alloc-token.cpp
    llvm/docs/LangRef.rst
    llvm/lib/IR/Verifier.cpp
    llvm/lib/Transforms/Instrumentation/AllocToken.cpp
    llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
    llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
    llvm/test/Instrumentation/AllocToken/remark.ll
    llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll

Removed: 
    


################################################################################
diff  --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst
index fb5c060bed939..7f3a128cd1557 100644
--- a/clang/docs/AllocToken.rst
+++ b/clang/docs/AllocToken.rst
@@ -31,13 +31,18 @@ Token Assignment Mode
 
 The default mode to calculate tokens is:
 
-* ``typehash``: This mode assigns a token ID based on the hash of the allocated
-  type's name.
+* ``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``: This mode assigns a token ID based on the hash of the allocated
+  type's name.
+
 * ``random``: This mode assigns a statically-determined random token ID to each
   allocation site.
 

diff  --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index a071e801b91ec..7dd6a830502bd 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1272,20 +1272,84 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound,
   EmitCheck(std::make_pair(Check, CheckKind), CheckHandler, StaticData, Index);
 }
 
+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();
+    // 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 && II->isStr("uintptr_t"))
+      return true;
+  }
+
+  // The type is an array; check the element type.
+  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()) {
+    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->isDynamicClass())
+        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");
 
+  llvm::MDBuilder MDB(getLLVMContext());
+
+  // Get unique type name.
   PrintingPolicy Policy(CGM.getContext().getLangOpts());
   Policy.SuppressTagKeyword = true;
   Policy.FullyQualifiedName = true;
   SmallString<64> TypeName;
   llvm::raw_svector_ostream TypeNameOS(TypeName);
   AllocType.getCanonicalType().print(TypeNameOS, Policy);
-  auto *TypeMDS = llvm::MDString::get(CGM.getLLVMContext(), TypeNameOS.str());
+  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.
+  llvm::SmallPtrSet<const RecordDecl *, 4> VisitedRD;
+  bool IncompleteType = false;
+  const bool ContainsPtr =
+      typeContainsPointer(AllocType, VisitedRD, IncompleteType);
+  if (!ContainsPtr && IncompleteType)
+    return;
+  auto *ContainsPtrC = Builder.getInt1(ContainsPtr);
+  auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC);
 
-  // Format: !{<type-name>}
-  auto *MDN = llvm::MDNode::get(CGM.getLLVMContext(), {TypeMDS});
+  // Format: !{<type-name>, <contains-pointer>}
+  auto *MDN =
+      llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD});
   CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN);
 }
 

diff  --git a/clang/test/CodeGenCXX/alloc-token-pointer.cpp b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
new file mode 100644
index 0000000000000..4781d1b4c13f0
--- /dev/null
+++ b/clang/test/CodeGenCXX/alloc-token-pointer.cpp
@@ -0,0 +1,175 @@
+// 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"
+
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+extern "C" {
+void *malloc(size_t size);
+}
+
+// CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_intv(
+// CHECK: call ptr @malloc(i64 noundef 4)
+void *test_malloc_int() {
+  int *a = (int *)malloc(sizeof(int));
+  *a = 42;
+  return a;
+}
+
+// CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_ptrv(
+// CHECK: call ptr @malloc(i64 noundef 8)
+int **test_malloc_ptr() {
+  int **a = (int **)malloc(sizeof(int*));
+  *a = nullptr;
+  return a;
+}
+
+// CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_intv(
+// 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: 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: 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: call noalias noundef nonnull ptr @_Znam(i64 noundef 80){{.*}} !alloc_token [[META_INTPTR]]
+int **test_new_ptr_array() {
+  return new int*[10];
+}
+
+struct ContainsPtr {
+  int a;
+  char *buf;
+};
+
+// CHECK-LABEL: define dso_local noundef ptr @_Z27test_malloc_struct_with_ptrv(
+// 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: 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: 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: 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: 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: 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: 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: call noalias noundef nonnull ptr @_Znam(i64 noundef 160){{.*}} !alloc_token [[META_CONTAINSPTR]]
+ContainsPtr *test_new_struct_array_with_ptr() {
+  return new ContainsPtr[10];
+}
+
+class TestClass {
+public:
+  void Foo();
+  ~TestClass();
+  int data[16];
+};
+
+// CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_classv(
+// 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: call noalias noundef nonnull ptr @_Znam(i64 noundef 648){{.*}} !alloc_token [[META_TESTCLASS]]
+TestClass *test_new_class_array() {
+  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: define dso_local noundef ptr @_Z22test_new_virtual_classv(
+// 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: call noalias noundef nonnull ptr @_Znam(i64 noundef 728){{.*}} !alloc_token [[META_VIRTUALTESTCLASS]]
+VirtualTestClass *test_new_virtual_class_array() {
+  return new VirtualTestClass[10];
+}
+
+// uintptr_t is treated as a pointer.
+struct MyStructUintptr {
+  int a;
+  uintptr_t ptr;
+};
+
+// CHECK-LABEL: define dso_local noundef ptr @_Z18test_uintptr_isptrv(
+// 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: call noalias noundef nonnull ptr @_Znwm(i64 noundef 8){{.*}} !alloc_token [[META_UINTPTR:![0-9]+]]
+uptr *test_uintptr_isptr2() {
+  return new uptr;
+}
+
+// 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 52bad9c54fb3b..5914b4ca5ef23 100644
--- a/clang/test/CodeGenCXX/alloc-token.cpp
+++ b/clang/test/CodeGenCXX/alloc-token.cpp
@@ -137,5 +137,5 @@ TestClass *test_new_class_array() {
   return arr;
 }
 
-// CHECK: [[META_INT]] = !{!"int"}
-// CHECK: [[META_TESTCLASS]] = !{!"TestClass"}
+// CHECK: [[META_INT]] = !{!"int", i1 false}
+// CHECK: [[META_TESTCLASS]] = !{!"TestClass", i1 true}

diff  --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 6d0e82896fcea..8b6c25c58d61e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -8588,13 +8588,14 @@ 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 a string with the type of an allocation.
+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>"}
+  !0 = !{!"<type-name>", i1 <contains-pointer>}
 
 Module Flags Metadata
 =====================

diff  --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 71a8a3876eb5d..c9ff86b7df16b 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -5398,8 +5398,10 @@ void Verifier::visitCapturesMetadata(Instruction &I, const MDNode *Captures) {
 
 void Verifier::visitAllocTokenMetadata(Instruction &I, MDNode *MD) {
   Check(isa<CallBase>(I), "!alloc_token should only exist on calls", &I);
-  Check(MD->getNumOperands() == 1, "!alloc_token must have 1 operand", MD);
+  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.

diff  --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
index 782d5a162a314..40720ae4b39ae 100644
--- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
+++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp
@@ -69,19 +69,30 @@ enum class TokenMode : unsigned {
 
   /// 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,
 };
 
 //===--- Command-line options ---------------------------------------------===//
 
-cl::opt<TokenMode>
-    ClMode("alloc-token-mode", cl::Hidden, cl::desc("Token assignment mode"),
-           cl::init(TokenMode::TypeHash),
-           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")));
+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"),
@@ -127,16 +138,23 @@ STATISTIC(NumAllocationsInstrumented, "Allocations instrumented");
 
 /// Returns the !alloc_token metadata if available.
 ///
-/// Expected format is: !{<type-name>}
+/// Expected format is: !{<type-name>, <contains-pointer>}
 MDNode *getAllocTokenMetadata(const CallBase &CB) {
   MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token);
   if (!Ret)
     return nullptr;
-  assert(Ret->getNumOperands() == 1 && "bad !alloc_token");
+  assert(Ret->getNumOperands() == 2 && "bad !alloc_token");
   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(const IntegerType &TokenTy, uint64_t MaxTokens)
@@ -188,12 +206,20 @@ class TypeHashMode : public ModeBase {
   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 = getAllocTokenMetadata(CB)) {
       MDString *S = cast<MDString>(N->getOperand(0));
-      return boundedToken(getStableSipHash(S->getString()));
+      return {N, getStableSipHash(S->getString())};
     }
+    // Fallback.
     remarkNoMetadata(CB, ORE);
-    return ClFallbackToken;
+    return {nullptr, ClFallbackToken};
   }
 
   /// Remark that there was no precise type information.
@@ -210,6 +236,29 @@ class TypeHashMode : public ModeBase {
   }
 };
 
+/// 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 / 2;
+    const auto [N, H] = getHash(CB, ORE);
+    if (!N) {
+      // Pick the fallback token (ClFallbackToken), which by default is 0,
+      // meaning it'll fall into the pointer-less bucket. Override by setting
+      // -alloc-token-fallback if that is the wrong choice.
+      return H;
+    }
+    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())
@@ -236,6 +285,9 @@ class AllocToken {
     case TokenMode::TypeHash:
       Mode.emplace<TypeHashMode>(*IntPtrTy, *Options.MaxTokens);
       break;
+    case TokenMode::TypeHashPointerSplit:
+      Mode.emplace<TypeHashPointerSplitMode>(*IntPtrTy, *Options.MaxTokens);
+      break;
     }
   }
 
@@ -275,7 +327,9 @@ class AllocToken {
   // Cache for replacement functions.
   DenseMap<std::pair<LibFunc, uint64_t>, FunctionCallee> TokenAllocFunctions;
   // Selected mode.
-  std::variant<IncrementMode, RandomMode, TypeHashMode> Mode;
+  std::variant<IncrementMode, RandomMode, TypeHashMode,
+               TypeHashPointerSplitMode>
+      Mode;
 };
 
 bool AllocToken::instrumentFunction(Function &F) {

diff  --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
index 5f08552c789f3..0e382b2cebed6 100644
--- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
+++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll
@@ -38,7 +38,7 @@ entry:
   ret ptr %ptr1
 }
 
-!0 = !{!"int"}
+!0 = !{!"int", i1 0}
 ;.
-; CHECK: [[META0]] = !{!"int"}
+; CHECK: [[META0]] = !{!"int", i1 false}
 ;.

diff  --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
index e023ab6b11b1e..19673da1bcfb6 100644
--- a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
+++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll
@@ -79,7 +79,7 @@ entry:
   ret ptr %ptr1
 }
 
-!0 = !{!"int"}
+!0 = !{!"int", i1 0}
 ;.
-; CHECK: [[META0]] = !{!"int"}
+; CHECK: [[META0]] = !{!"int", i1 false}
 ;.

diff  --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll
index a2404526ea53f..f2eaa6209d89e 100644
--- a/llvm/test/Instrumentation/AllocToken/remark.ll
+++ b/llvm/test/Instrumentation/AllocToken/remark.ll
@@ -32,7 +32,7 @@ entry:
   ret ptr %ptr1
 }
 
-!0 = !{!"int"}
+!0 = !{!"int", i1 0}
 ;.
-; CHECK: [[META0]] = !{!"int"}
+; CHECK: [[META0]] = !{!"int", i1 false}
 ;.

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
index 9bbe3eb371673..42d3dcc92712d 100644
--- a/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll
+++ b/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll
@@ -97,8 +97,8 @@ if.end:
   ret ptr %x.0
 }
 
-!0 = !{!"int"}
-!1 = !{!"char[4]"}
+!0 = !{!"int", i1 0}
+!1 = !{!"char[4]", i1 0}
 ;.
-; CHECK: [[META0]] = !{!"int"}
+; CHECK: [[META0]] = !{!"int", i1 false}
 ;.


        


More information about the llvm-commits mailing list