https://github.com/ainozaki updated https://github.com/llvm/llvm-project/pull/228492
>From 2d4e3ffbb562fa3639e7998bd3d7b0afa3bef5df Mon Sep 17 00:00:00 2001 From: ainozaki <[email protected]> Date: Tue, 29 Sep 2026 02:05:48 +0000 Subject: [PATCH 1/7] [Clang][AllocToken] Emit function names in TypeFuncHash modes In the TypeFuncHash and TypeFuncHashPointerSplit modes, always emit !alloc_token metadata, with the qualified name of the function containing the allocation as the third operand. If the allocated type cannot be inferred, emit an empty type name so that the token is still derived from the function name. __builtin_infer_alloc_token() is not supported in constant expressions in these modes. --- clang/docs/AllocToken.md | 13 +++- .../include/clang/Basic/DiagnosticASTKinds.td | 2 +- clang/lib/CodeGen/CGExpr.cpp | 35 +++++++-- clang/test/CodeGen/alloc-token-inline.c | 5 ++ .../CodeGenCXX/alloc-token-typefunchash.cpp | 72 +++++++++++++++++++ .../test/SemaCXX/alloc-token-typefunchash.cpp | 14 ++++ 6 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 clang/test/CodeGenCXX/alloc-token-typefunchash.cpp create mode 100644 clang/test/SemaCXX/alloc-token-typefunchash.cpp diff --git a/clang/docs/AllocToken.md b/clang/docs/AllocToken.md index 136f76bfb3111b..2d1496ba656865 100644 --- a/clang/docs/AllocToken.md +++ b/clang/docs/AllocToken.md @@ -34,6 +34,11 @@ change or removal. These may (experimentally) be selected with `-Xclang - `typehash`: This mode assigns a token ID based on the hash of the allocated type's name. +- `typefunchash`: This mode assigns a token ID based on the hash of the + allocated type's name (upper half of the token ID bits) and the hash of the + name of the function containing the allocation (lower half). +- `typefunchashpointersplit`: Like `typefunchash`, but the most significant bit + of the token ID is set for types that contain pointers. - `random`: This mode assigns a statically-determined random token ID to each allocation site. - `increment`: This mode assigns a simple, incrementally increasing token ID @@ -43,7 +48,9 @@ The following command-line options affect generated token IDs: - `-falloc-token-max=<N>` : Configures the maximum number of token IDs. By default the number of tokens - is bounded by `SIZE_MAX`. + is bounded by `SIZE_MAX`. In the `typefunchash` and + `typefunchashpointersplit` modes, `N` must be at least 8, and is rounded + down to a power of two. ## Querying Token IDs with `__builtin_infer_alloc_token` @@ -57,7 +64,9 @@ size_t __builtin_infer_alloc_token(<args>, ...); This builtin returns the token ID inferred from its argument expressions, which mirror arguments normally passed to any allocation function. The argument expressions are **unevaluated**, so it can be used with expressions that would -have side effects without any runtime impact. +have side effects without any runtime impact. The builtin cannot be used in +constant expressions in the `increment`, `random`, `typefunchash`, and +`typefunchashpointersplit` modes. For example, it can be used as follows: diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 0aca1f75428f8a..6e770812a9298e 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -416,7 +416,7 @@ def note_constexpr_infer_alloc_token_type_inference_failed : Note< def note_constexpr_infer_alloc_token_no_metadata : Note< "could not get token metadata for inferred type">; def note_constexpr_infer_alloc_token_stateful_mode - : Note<"stateful alloc token mode not supported in constexpr">; + : Note<"alloc token mode not supported in constexpr">; def warn_attribute_needs_aggregate : Warning< "%0 attribute is ignored in non-aggregate type %1">, diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 8323a170a1dbbc..dafe901397a86d 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1348,7 +1348,27 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *ArrayExpr, } llvm::MDNode *CodeGenFunction::buildAllocToken(QualType AllocType) { - auto ATMD = infer_alloc::getAllocTokenMetadata(AllocType, getContext()); + std::optional<llvm::AllocTokenMetadata> ATMD; + if (!AllocType.isNull()) + ATMD = infer_alloc::getAllocTokenMetadata(AllocType, getContext()); + + const llvm::AllocTokenMode Mode = + getLangOpts().AllocTokenMode.value_or(llvm::DefaultAllocTokenMode); + if (Mode == llvm::AllocTokenMode::TypeFuncHash || + Mode == llvm::AllocTokenMode::TypeFuncHashPointerSplit) { + // An empty type name denotes an unknown type. + if (!ATMD) + ATMD = llvm::AllocTokenMetadata{{}, false}; + // Use the function containing the allocation (for lambdas, the call + // operator; for blocks and captured statements, the enclosing function). + // Allocations outside of any function (e.g. global initializers) use "". + const Decl *D = + isa_and_nonnull<FunctionDecl>(CurCodeDecl) ? CurCodeDecl : CurFuncDecl; + std::string FuncName; + if (const auto *ND = dyn_cast_or_null<NamedDecl>(D)) + FuncName = ND->getQualifiedNameAsString(); + ATMD->FunctionName = FuncName; + } if (!ATMD) return nullptr; @@ -1357,8 +1377,11 @@ llvm::MDNode *CodeGenFunction::buildAllocToken(QualType AllocType) { auto *ContainsPtrC = Builder.getInt1(ATMD->ContainsPointer); auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC); - // Format: !{<type-name>, <contains-pointer>} - return llvm::MDNode::get(CGM.getLLVMContext(), {TypeNameMD, ContainsPtrMD}); + // Format: !{<type-name>, <contains-pointer>[, <function-name>]} + SmallVector<llvm::Metadata *, 3> Ops = {TypeNameMD, ContainsPtrMD}; + if (ATMD->FunctionName) + Ops.push_back(MDB.createString(*ATMD->FunctionName)); + return llvm::MDNode::get(CGM.getLLVMContext(), Ops); } void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { @@ -1369,10 +1392,8 @@ void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { } llvm::MDNode *CodeGenFunction::buildAllocToken(const CallExpr *E) { - QualType AllocType = infer_alloc::inferPossibleType(E, getContext(), CurCast); - if (!AllocType.isNull()) - return buildAllocToken(AllocType); - return nullptr; + return buildAllocToken( + infer_alloc::inferPossibleType(E, getContext(), CurCast)); } void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, const CallExpr *E) { diff --git a/clang/test/CodeGen/alloc-token-inline.c b/clang/test/CodeGen/alloc-token-inline.c index 04fb62b019c5d8..0b4bd0031f0fdf 100644 --- a/clang/test/CodeGen/alloc-token-inline.c +++ b/clang/test/CodeGen/alloc-token-inline.c @@ -1,5 +1,6 @@ // RUN: %clang_cc1 -O -fsanitize=alloc-token -fsanitize-alloc-token-extended -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --implicit-check-not=__alloc_token // RUN: %clang_cc1 -O -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --implicit-check-not=__alloc_token +// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-mode=typefunchash -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=TYPEFUNC --implicit-check-not=__alloc_token typedef __typeof(sizeof(int)) size_t; @@ -19,9 +20,13 @@ __attribute__((malloc, always_inline)) inline void *wrapper(size_t size) { // CHECK: call ptr @external_ptr_helper() // CHECK: call void @external_void_helper() // CHECK: call{{.*}} @__alloc_token_malloc(i64 noundef 4, i64 2689373973731826898){{.*}} !alloc_token [[META_INT:![0-9]+]] +// TYPEFUNC-LABEL: @test_inlined_wrapper( +// TYPEFUNC: call{{.*}} @__alloc_token_malloc(i64 noundef 4, i64 6086435482222324463){{.*}} !alloc_token [[TYPEFUNC_META_INT:![0-9]+]] void test_inlined_wrapper(void) { sink = wrapper(sizeof(int)); } // CHECK: declare{{.*}} @__alloc_token_malloc( // CHECK: [[META_INT]] = !{!"int", i1 false} +// TYPEFUNC: declare{{.*}} @__alloc_token_malloc( +// TYPEFUNC: [[TYPEFUNC_META_INT]] = !{!"int", i1 false, !"test_inlined_wrapper"} diff --git a/clang/test/CodeGenCXX/alloc-token-typefunchash.cpp b/clang/test/CodeGenCXX/alloc-token-typefunchash.cpp new file mode 100644 index 00000000000000..657eb880179089 --- /dev/null +++ b/clang/test/CodeGenCXX/alloc-token-typefunchash.cpp @@ -0,0 +1,72 @@ +// Test that the typefunchash modes add the name of the function containing the +// allocation to the !alloc_token metadata, and always emit metadata. +// +// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-mode=typefunchash -triple x86_64-linux-gnu -std=c++20 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s +// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-mode=typefunchashpointersplit -triple x86_64-linux-gnu -std=c++20 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s + +typedef __typeof(sizeof(int)) size_t; +extern "C" void *malloc(size_t size) __attribute__((malloc)); + +struct WithPtr { + int a; + char *buf; +}; + +struct Incomplete; + +void *sink; + +// Unknown or incomplete types use an empty type name. +// CHECK-LABEL: define {{.*}} @_Z9test_funcm( +// CHECK: call {{.*}} @malloc(i64 noundef 4){{.*}}, !alloc_token [[META_FUNC:![0-9]+]] +// CHECK: call {{.*}} @malloc(i64 noundef %{{.*}}){{.*}}, !alloc_token [[META_UNKNOWN:![0-9]+]] +// CHECK: call {{.*}} @malloc(i64 noundef 4){{.*}}, !alloc_token [[META_UNKNOWN]] +void test_func(size_t n) { + sink = (int *)malloc(sizeof(int)); + sink = malloc(n); + sink = (Incomplete *)malloc(4); +} + +namespace ns { +struct S { + void *method(); +}; +// CHECK-LABEL: define {{.*}} @_ZN2ns1S6methodEv( +// CHECK: call {{.*}} @_Znwm(i64 noundef 16){{.*}}, !alloc_token [[META_METHOD:![0-9]+]] +void *S::method() { return new WithPtr; } +} // namespace ns + +template <typename T> +struct Tmpl { + static void *alloc() { return new T; } +}; + +// CHECK-LABEL: define {{.*}} @_ZN4TmplIlE5allocEv( +// CHECK: call {{.*}} @_Znwm(i64 noundef 8){{.*}}, !alloc_token [[META_TMPL:![0-9]+]] +void test_template() { sink = Tmpl<long>::alloc(); } + +// Lambdas use the name of their call operator. +// CHECK-LABEL: define {{.*}} @"_ZZ11test_lambdavENK{{.*}}clEv"( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_LAMBDA:![0-9]+]] +void test_lambda() { + auto L = [] { return new int; }; + sink = L(); +} + +// Runtime use of the builtin, which is not a constant expression in this mode. +// CHECK-LABEL: define {{.*}} @_Z12test_builtinv( +// CHECK: call i64 @llvm.alloc.token.id.i64(metadata [[META_BUILTIN:![0-9]+]]) +unsigned long test_builtin() { return __builtin_infer_alloc_token(sizeof(int)); } + +// Allocations outside of any function use an empty function name. +// CHECK-LABEL: define internal void @__cxx_global_var_init( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_GLOBAL:![0-9]+]] +void *global = new int; + +// CHECK-DAG: [[META_FUNC]] = !{!"int", i1 false, !"test_func"} +// CHECK-DAG: [[META_UNKNOWN]] = !{!"", i1 false, !"test_func"} +// CHECK-DAG: [[META_METHOD]] = !{!"WithPtr", i1 true, !"ns::S::method"} +// CHECK-DAG: [[META_TMPL]] = !{!"long", i1 false, !"Tmpl<long>::alloc"} +// CHECK-DAG: [[META_LAMBDA]] = !{!"int", i1 false, !"test_lambda()::(lambda)::operator()"} +// CHECK-DAG: [[META_BUILTIN]] = !{!"int", i1 false, !"test_builtin"} +// CHECK-DAG: [[META_GLOBAL]] = !{!"int", i1 false, !""} diff --git a/clang/test/SemaCXX/alloc-token-typefunchash.cpp b/clang/test/SemaCXX/alloc-token-typefunchash.cpp new file mode 100644 index 00000000000000..50f1e73a130fc4 --- /dev/null +++ b/clang/test/SemaCXX/alloc-token-typefunchash.cpp @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -verify %s -falloc-token-mode=typefunchash +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -verify %s -falloc-token-mode=typefunchashpointersplit +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -verify %s -falloc-token-mode=typefunchash -fexperimental-new-constant-interpreter +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -verify %s -falloc-token-mode=typefunchashpointersplit -fexperimental-new-constant-interpreter + +// The token ID depends on the function containing the allocation, which is only +// known to the AllocToken pass: the builtin is not a constant expression. +static_assert(!__builtin_constant_p(__builtin_infer_alloc_token(sizeof(int)))); + +void test() { + constexpr auto token = __builtin_infer_alloc_token(sizeof(int)); // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{alloc token mode not supported in constexpr}} + auto runtime_token = __builtin_infer_alloc_token(sizeof(int)); +} >From 78a85edfe371ccfdd753396385b4f11f2d546c8d Mon Sep 17 00:00:00 2001 From: ainozaki <[email protected]> Date: Wed, 30 Sep 2026 11:47:06 +0000 Subject: [PATCH 2/7] update doc and test --- clang/docs/AllocToken.md | 5 ++--- clang/test/CodeGen/alloc-token-inline.c | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/clang/docs/AllocToken.md b/clang/docs/AllocToken.md index 2d1496ba656865..bef49e93544394 100644 --- a/clang/docs/AllocToken.md +++ b/clang/docs/AllocToken.md @@ -38,7 +38,7 @@ change or removal. These may (experimentally) be selected with `-Xclang allocated type's name (upper half of the token ID bits) and the hash of the name of the function containing the allocation (lower half). - `typefunchashpointersplit`: Like `typefunchash`, but the most significant bit - of the token ID is set for types that contain pointers. + of the `log2(N)`-bit token ID is set for types that contain pointers. - `random`: This mode assigns a statically-determined random token ID to each allocation site. - `increment`: This mode assigns a simple, incrementally increasing token ID @@ -49,8 +49,7 @@ The following command-line options affect generated token IDs: - `-falloc-token-max=<N>` : Configures the maximum number of token IDs. By default the number of tokens is bounded by `SIZE_MAX`. In the `typefunchash` and - `typefunchashpointersplit` modes, `N` must be at least 8, and is rounded - down to a power of two. + `typefunchashpointersplit` modes, `N` is rounded down to a power of two. ## Querying Token IDs with `__builtin_infer_alloc_token` diff --git a/clang/test/CodeGen/alloc-token-inline.c b/clang/test/CodeGen/alloc-token-inline.c index 0b4bd0031f0fdf..40cbf2db8abc7b 100644 --- a/clang/test/CodeGen/alloc-token-inline.c +++ b/clang/test/CodeGen/alloc-token-inline.c @@ -21,7 +21,7 @@ __attribute__((malloc, always_inline)) inline void *wrapper(size_t size) { // CHECK: call void @external_void_helper() // CHECK: call{{.*}} @__alloc_token_malloc(i64 noundef 4, i64 2689373973731826898){{.*}} !alloc_token [[META_INT:![0-9]+]] // TYPEFUNC-LABEL: @test_inlined_wrapper( -// TYPEFUNC: call{{.*}} @__alloc_token_malloc(i64 noundef 4, i64 6086435482222324463){{.*}} !alloc_token [[TYPEFUNC_META_INT:![0-9]+]] +// TYPEFUNC: call{{.*}} @__alloc_token_malloc(i64 noundef 4, i64 3043217740861139695){{.*}} !alloc_token [[TYPEFUNC_META_INT:![0-9]+]] void test_inlined_wrapper(void) { sink = wrapper(sizeof(int)); } >From 43ace18abdbad7cf8e6704c4b7bef143d52f7e64 Mon Sep 17 00:00:00 2001 From: ainozaki <[email protected]> Date: Fri, 2 Oct 2026 15:35:06 +0000 Subject: [PATCH 3/7] update doc and test --- clang/docs/AllocToken.md | 6 ++++-- clang/test/CodeGen/alloc-token-inline.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/clang/docs/AllocToken.md b/clang/docs/AllocToken.md index bef49e93544394..3c77f5bcc3be9b 100644 --- a/clang/docs/AllocToken.md +++ b/clang/docs/AllocToken.md @@ -38,7 +38,7 @@ change or removal. These may (experimentally) be selected with `-Xclang allocated type's name (upper half of the token ID bits) and the hash of the name of the function containing the allocation (lower half). - `typefunchashpointersplit`: Like `typefunchash`, but the most significant bit - of the `log2(N)`-bit token ID is set for types that contain pointers. + of the token ID is set for types that contain pointers. - `random`: This mode assigns a statically-determined random token ID to each allocation site. - `increment`: This mode assigns a simple, incrementally increasing token ID @@ -49,7 +49,9 @@ The following command-line options affect generated token IDs: - `-falloc-token-max=<N>` : Configures the maximum number of token IDs. By default the number of tokens is bounded by `SIZE_MAX`. In the `typefunchash` and - `typefunchashpointersplit` modes, `N` is rounded down to a power of two. + `typefunchashpointersplit` modes, `N` is rounded down to a power of two, + unless `N` is of the form `2^k-1` (e.g. the default `SIZE_MAX`), in which + case all `k` bits are used and token IDs are in `[0, N]`. ## Querying Token IDs with `__builtin_infer_alloc_token` diff --git a/clang/test/CodeGen/alloc-token-inline.c b/clang/test/CodeGen/alloc-token-inline.c index 40cbf2db8abc7b..0b4bd0031f0fdf 100644 --- a/clang/test/CodeGen/alloc-token-inline.c +++ b/clang/test/CodeGen/alloc-token-inline.c @@ -21,7 +21,7 @@ __attribute__((malloc, always_inline)) inline void *wrapper(size_t size) { // CHECK: call void @external_void_helper() // CHECK: call{{.*}} @__alloc_token_malloc(i64 noundef 4, i64 2689373973731826898){{.*}} !alloc_token [[META_INT:![0-9]+]] // TYPEFUNC-LABEL: @test_inlined_wrapper( -// TYPEFUNC: call{{.*}} @__alloc_token_malloc(i64 noundef 4, i64 3043217740861139695){{.*}} !alloc_token [[TYPEFUNC_META_INT:![0-9]+]] +// TYPEFUNC: call{{.*}} @__alloc_token_malloc(i64 noundef 4, i64 6086435482222324463){{.*}} !alloc_token [[TYPEFUNC_META_INT:![0-9]+]] void test_inlined_wrapper(void) { sink = wrapper(sizeof(int)); } >From 0d6f070e97b6394e8a177ca0bd828fd2a7aff853 Mon Sep 17 00:00:00 2001 From: ainozaki <[email protected]> Date: Sat, 3 Oct 2026 13:23:31 +0000 Subject: [PATCH 4/7] update comment --- clang/lib/CodeGen/CGExpr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index dafe901397a86d..889df27dae281f 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1359,8 +1359,8 @@ llvm::MDNode *CodeGenFunction::buildAllocToken(QualType AllocType) { // An empty type name denotes an unknown type. if (!ATMD) ATMD = llvm::AllocTokenMetadata{{}, false}; - // Use the function containing the allocation (for lambdas, the call - // operator; for blocks and captured statements, the enclosing function). + // Use the function containing the allocation. For lambdas, use the call + // operator. For blocks and captured statements, use the enclosing function. // Allocations outside of any function (e.g. global initializers) use "". const Decl *D = isa_and_nonnull<FunctionDecl>(CurCodeDecl) ? CurCodeDecl : CurFuncDecl; >From 6b546137a32c349f2549fe38476b12c0db7686d9 Mon Sep 17 00:00:00 2001 From: ainozaki <[email protected]> Date: Wed, 7 Oct 2026 02:20:29 +0000 Subject: [PATCH 5/7] update function name, diag and test --- .../include/clang/Basic/DiagnosticASTKinds.td | 2 +- clang/lib/AST/ByteCode/InterpBuiltin.cpp | 2 +- clang/lib/AST/ExprConstant.cpp | 2 +- clang/lib/CodeGen/CGExpr.cpp | 35 +++++++--- .../CodeGenCXX/alloc-token-typefunchash.cpp | 68 +++++++++++++++++-- 5 files changed, 92 insertions(+), 17 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 6e770812a9298e..e54c4d76590957 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -415,7 +415,7 @@ def note_constexpr_infer_alloc_token_type_inference_failed : Note< "could not infer allocation type for __builtin_infer_alloc_token">; def note_constexpr_infer_alloc_token_no_metadata : Note< "could not get token metadata for inferred type">; -def note_constexpr_infer_alloc_token_stateful_mode +def note_constexpr_infer_alloc_token_unsupported_mode : Note<"alloc token mode not supported in constexpr">; def warn_attribute_needs_aggregate : Warning< diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index 5ea3cc6bd29209..1410555b1f4816 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -1588,7 +1588,7 @@ static bool interp__builtin_infer_alloc_token(InterpState &S, CodePtr OpPC, auto MaybeToken = llvm::getAllocToken(Mode, *ATMD, MaxTokens); if (!MaybeToken) { - S.CCEDiag(Call, diag::note_constexpr_infer_alloc_token_stateful_mode); + S.CCEDiag(Call, diag::note_constexpr_infer_alloc_token_unsupported_mode); return false; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 34c3e39807c7f1..923987b51204f1 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -17407,7 +17407,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, MaxTokensOpt.value_or(0) ? *MaxTokensOpt : (~0ULL >> (64 - BitWidth)); auto MaybeToken = llvm::getAllocToken(Mode, *ATMD, MaxTokens); if (!MaybeToken) - return Error(E, diag::note_constexpr_infer_alloc_token_stateful_mode); + return Error(E, diag::note_constexpr_infer_alloc_token_unsupported_mode); return Success(llvm::APInt(BitWidth, *MaybeToken), E); } diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 889df27dae281f..80489c04b57019 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1347,6 +1347,28 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *ArrayExpr, IndexInst); } +/// Returns the qualified name of the function \p D for the TypeFuncHash modes, +/// or "" if there is none. +static SmallString<64> getAllocTokenFunctionName(const Decl *D, + const ASTContext &Ctx) { + SmallString<64> Name; + const auto *ND = dyn_cast_if_present<NamedDecl>(D); + if (!ND) + return Name; + // Use a fixed policy instead of the ASTContext's, so that the same function + // gets the same name in every TU. + PrintingPolicy Policy(Ctx.getLangOpts()); + // Do not use name lookup to decide whether to print inline namespaces. + Policy.SuppressInlineNamespace = + llvm::to_underlying(PrintingPolicy::SuppressInlineNamespaceMode::All); + // Do not print file paths for unnamed types, e.g. in template arguments. + Policy.AnonymousTagNameStyle = + llvm::to_underlying(PrintingPolicy::AnonymousTagMode::Plain); + llvm::raw_svector_ostream OS(Name); + ND->printQualifiedName(OS, Policy); + return Name; +} + llvm::MDNode *CodeGenFunction::buildAllocToken(QualType AllocType) { std::optional<llvm::AllocTokenMetadata> ATMD; if (!AllocType.isNull()) @@ -1359,15 +1381,10 @@ llvm::MDNode *CodeGenFunction::buildAllocToken(QualType AllocType) { // An empty type name denotes an unknown type. if (!ATMD) ATMD = llvm::AllocTokenMetadata{{}, false}; - // Use the function containing the allocation. For lambdas, use the call - // operator. For blocks and captured statements, use the enclosing function. - // Allocations outside of any function (e.g. global initializers) use "". - const Decl *D = - isa_and_nonnull<FunctionDecl>(CurCodeDecl) ? CurCodeDecl : CurFuncDecl; - std::string FuncName; - if (const auto *ND = dyn_cast_or_null<NamedDecl>(D)) - FuncName = ND->getQualifiedNameAsString(); - ATMD->FunctionName = FuncName; + // Use the outermost non-closure function, i.e. allocations in lambdas, + // blocks, and captured statements use the enclosing function. Allocations + // outside of any function (e.g. global initializers) use "". + ATMD->FunctionName = getAllocTokenFunctionName(CurFuncDecl, getContext()); } if (!ATMD) return nullptr; diff --git a/clang/test/CodeGenCXX/alloc-token-typefunchash.cpp b/clang/test/CodeGenCXX/alloc-token-typefunchash.cpp index 657eb880179089..6a44ddd8cba27f 100644 --- a/clang/test/CodeGenCXX/alloc-token-typefunchash.cpp +++ b/clang/test/CodeGenCXX/alloc-token-typefunchash.cpp @@ -1,8 +1,8 @@ // Test that the typefunchash modes add the name of the function containing the // allocation to the !alloc_token metadata, and always emit metadata. // -// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-mode=typefunchash -triple x86_64-linux-gnu -std=c++20 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s -// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-mode=typefunchashpointersplit -triple x86_64-linux-gnu -std=c++20 -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s +// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-mode=typefunchash -triple x86_64-linux-gnu -std=c++20 -fblocks -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s +// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-mode=typefunchashpointersplit -triple x86_64-linux-gnu -std=c++20 -fblocks -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s typedef __typeof(sizeof(int)) size_t; extern "C" void *malloc(size_t size) __attribute__((malloc)); @@ -45,7 +45,31 @@ struct Tmpl { // CHECK: call {{.*}} @_Znwm(i64 noundef 8){{.*}}, !alloc_token [[META_TMPL:![0-9]+]] void test_template() { sink = Tmpl<long>::alloc(); } -// Lambdas use the name of their call operator. +template <typename T> +struct Holder { + static void *alloc() { return new int; } +}; + +// Unnamed types are printed without their source location. +// CHECK-LABEL: define {{.*}} @"_ZN6HolderIZ17test_unnamed_typevE{{.*}}E5allocEv"( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_UNNAMED:![0-9]+]] +void test_unnamed_type() { + auto L = [] {}; + sink = Holder<decltype(L)>::alloc(); +} + +// Inline namespaces are always omitted, regardless of other declarations such +// as ns2::inl(int). +namespace ns2 { +void *inl(int); +inline namespace v1 { +// CHECK-LABEL: define {{.*}} @_ZN3ns22v13inlEv( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_INLINE_NS:![0-9]+]] +void *inl() { return new int; } +} // namespace v1 +} // namespace ns2 + +// Lambdas, blocks, and captured statements use the enclosing function. // CHECK-LABEL: define {{.*}} @"_ZZ11test_lambdavENK{{.*}}clEv"( // CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_LAMBDA:![0-9]+]] void test_lambda() { @@ -53,20 +77,54 @@ void test_lambda() { sink = L(); } +// CHECK-LABEL: define {{.*}} @___Z13test_closuresv_block_invoke( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_CLOSURES:![0-9]+]] +// CHECK-LABEL: define {{.*}} @__captured_stmt( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_CLOSURES]] +void test_closures() { + void (^B)(void) = ^{ sink = new int; }; + B(); +#pragma clang __debug captured + { sink = new int; } +} + // Runtime use of the builtin, which is not a constant expression in this mode. // CHECK-LABEL: define {{.*}} @_Z12test_builtinv( // CHECK: call i64 @llvm.alloc.token.id.i64(metadata [[META_BUILTIN:![0-9]+]]) unsigned long test_builtin() { return __builtin_infer_alloc_token(sizeof(int)); } -// Allocations outside of any function use an empty function name. +// Allocations outside of any function use an empty function name, also in +// lambdas. // CHECK-LABEL: define internal void @__cxx_global_var_init( // CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_GLOBAL:![0-9]+]] void *global = new int; +// CHECK-LABEL: define {{.*}} @"_ZNK{{.*}}clEv"( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_GLOBAL]] +void *global_lambda = [] { return new int; }(); + +// Closures nested in lambdas also use the enclosing function. +// CHECK-LABEL: define {{.*}} @"___ZZ11test_nestedvENK{{.*}}clEv_block_invoke"( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_NESTED:![0-9]+]] +// CHECK-LABEL: define {{.*}} @__captured_stmt{{.*}}( +// CHECK: call {{.*}} @_Znwm(i64 noundef 4){{.*}}, !alloc_token [[META_NESTED]] +void test_nested() { + auto L = [] { + void (^B)(void) = ^{ sink = new int; }; + B(); +#pragma clang __debug captured + { sink = new int; } + }; + L(); +} // CHECK-DAG: [[META_FUNC]] = !{!"int", i1 false, !"test_func"} // CHECK-DAG: [[META_UNKNOWN]] = !{!"", i1 false, !"test_func"} // CHECK-DAG: [[META_METHOD]] = !{!"WithPtr", i1 true, !"ns::S::method"} // CHECK-DAG: [[META_TMPL]] = !{!"long", i1 false, !"Tmpl<long>::alloc"} -// CHECK-DAG: [[META_LAMBDA]] = !{!"int", i1 false, !"test_lambda()::(lambda)::operator()"} +// CHECK-DAG: [[META_UNNAMED]] = !{!"int", i1 false, !"Holder<(lambda)>::alloc"} +// CHECK-DAG: [[META_INLINE_NS]] = !{!"int", i1 false, !"ns2::inl"} +// CHECK-DAG: [[META_LAMBDA]] = !{!"int", i1 false, !"test_lambda"} +// CHECK-DAG: [[META_CLOSURES]] = !{!"int", i1 false, !"test_closures"} // CHECK-DAG: [[META_BUILTIN]] = !{!"int", i1 false, !"test_builtin"} // CHECK-DAG: [[META_GLOBAL]] = !{!"int", i1 false, !""} +// CHECK-DAG: [[META_NESTED]] = !{!"int", i1 false, !"test_nested"} >From b8d5aa52d3714830af56dc3681c6c474f54081ba Mon Sep 17 00:00:00 2001 From: ainozaki <[email protected]> Date: Wed, 7 Oct 2026 07:45:38 +0000 Subject: [PATCH 6/7] update comments --- clang/docs/AllocToken.md | 26 ++++++++++++++------------ clang/lib/CodeGen/CGExpr.cpp | 5 ----- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/clang/docs/AllocToken.md b/clang/docs/AllocToken.md index 3c77f5bcc3be9b..f87870614bf26d 100644 --- a/clang/docs/AllocToken.md +++ b/clang/docs/AllocToken.md @@ -34,11 +34,12 @@ change or removal. These may (experimentally) be selected with `-Xclang - `typehash`: This mode assigns a token ID based on the hash of the allocated type's name. -- `typefunchash`: This mode assigns a token ID based on the hash of the - allocated type's name (upper half of the token ID bits) and the hash of the - name of the function containing the allocation (lower half). -- `typefunchashpointersplit`: Like `typefunchash`, but the most significant bit - of the token ID is set for types that contain pointers. +- `typefunchashpointersplit`: This mode assigns a token ID based on the hash of + the allocated type's name and the hash of the name of the function containing + the allocation. The upper half of the token ID bits is the type name hash, + the lower half of the token ID bits is the function name hash, and the most + significant bit is a flag indicating whether the type contains pointers. +- `typefunchash`: Like `typefunchashpointersplit`, but without the pointer flag. - `random`: This mode assigns a statically-determined random token ID to each allocation site. - `increment`: This mode assigns a simple, incrementally increasing token ID @@ -48,10 +49,11 @@ The following command-line options affect generated token IDs: - `-falloc-token-max=<N>` : Configures the maximum number of token IDs. By default the number of tokens - is bounded by `SIZE_MAX`. In the `typefunchash` and - `typefunchashpointersplit` modes, `N` is rounded down to a power of two, - unless `N` is of the form `2^k-1` (e.g. the default `SIZE_MAX`), in which - case all `k` bits are used and token IDs are in `[0, N]`. + is bounded by `SIZE_MAX`. In the `typefunchashpointersplit` and + `typefunchash` modes, the number of token IDs is `N` rounded down to a + power of two. As an exception, if `N` is of the form `2^k-1` + (e.g. the default `SIZE_MAX`), these modes use all `k` bits and token IDs + are in `[0, N]`, i.e. a token ID may be equal to `N`. ## Querying Token IDs with `__builtin_infer_alloc_token` @@ -65,9 +67,9 @@ size_t __builtin_infer_alloc_token(<args>, ...); This builtin returns the token ID inferred from its argument expressions, which mirror arguments normally passed to any allocation function. The argument expressions are **unevaluated**, so it can be used with expressions that would -have side effects without any runtime impact. The builtin cannot be used in -constant expressions in the `increment`, `random`, `typefunchash`, and -`typefunchashpointersplit` modes. +have side effects without any runtime impact. In the `typefunchashpointersplit`, +`typefunchash`, `random`, and `increment` modes, the builtin cannot be used in +constant expressions. For example, it can be used as follows: diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 80489c04b57019..3f17e245d6fd0f 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1347,16 +1347,12 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *ArrayExpr, IndexInst); } -/// Returns the qualified name of the function \p D for the TypeFuncHash modes, -/// or "" if there is none. static SmallString<64> getAllocTokenFunctionName(const Decl *D, const ASTContext &Ctx) { SmallString<64> Name; const auto *ND = dyn_cast_if_present<NamedDecl>(D); if (!ND) return Name; - // Use a fixed policy instead of the ASTContext's, so that the same function - // gets the same name in every TU. PrintingPolicy Policy(Ctx.getLangOpts()); // Do not use name lookup to decide whether to print inline namespaces. Policy.SuppressInlineNamespace = @@ -1378,7 +1374,6 @@ llvm::MDNode *CodeGenFunction::buildAllocToken(QualType AllocType) { getLangOpts().AllocTokenMode.value_or(llvm::DefaultAllocTokenMode); if (Mode == llvm::AllocTokenMode::TypeFuncHash || Mode == llvm::AllocTokenMode::TypeFuncHashPointerSplit) { - // An empty type name denotes an unknown type. if (!ATMD) ATMD = llvm::AllocTokenMetadata{{}, false}; // Use the outermost non-closure function, i.e. allocations in lambdas, >From dd9517a28a25a7bf95a51a36fb0fc7899e7ba1c7 Mon Sep 17 00:00:00 2001 From: ainozaki <[email protected]> Date: Wed, 7 Oct 2026 08:50:03 +0000 Subject: [PATCH 7/7] trigger CI _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
