Author: Marco Elver Date: 2025-10-23T18:14:33+02:00 New Revision: 7ca1472df011a3a7c341f7a866e68b2308d12442
URL: https://github.com/llvm/llvm-project/commit/7ca1472df011a3a7c341f7a866e68b2308d12442 DIFF: https://github.com/llvm/llvm-project/commit/7ca1472df011a3a7c341f7a866e68b2308d12442.diff LOG: [Clang] Implement constexpr evaluation for __builtin_infer_alloc_token() (#163639) Implement the constexpr evaluation for `__builtin_infer_alloc_token()` in Clang's constant expression evaluators (both in ExprConstant and the new bytecode interpreter). The constant evaluation is only supported for stateless (hash-based) token modes. If a stateful mode like `increment` is used, the evaluation fails, as the token value is not deterministic at compile time. Added: Modified: clang/include/clang/Basic/DiagnosticASTKinds.td clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ExprConstant.cpp clang/test/SemaCXX/alloc-token.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 0be9146f70364..5c462f9646b3b 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -403,6 +403,12 @@ def note_constexpr_assumption_failed : Note< def note_constexpr_countzeroes_zero : Note< "evaluation of %select{__builtin_elementwise_clzg|__builtin_elementwise_ctzg}0 " "with a zero value is undefined">; +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 : Note< + "stateful alloc token mode not supported in constexpr">; def err_experimental_clang_interp_failed : Error< "the experimental clang interpreter failed to evaluate an expression">; diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index d932a49dd2935..ff50e6dbfb44e 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -12,12 +12,14 @@ #include "InterpHelpers.h" #include "PrimType.h" #include "Program.h" +#include "clang/AST/InferAlloc.h" #include "clang/AST/OSLog.h" #include "clang/AST/RecordLayout.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/TargetBuiltins.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/AllocToken.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/SipHash.h" @@ -1307,6 +1309,45 @@ interp__builtin_ptrauth_string_discriminator(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_infer_alloc_token(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call) { + const ASTContext &ASTCtx = S.getASTContext(); + uint64_t BitWidth = ASTCtx.getTypeSize(ASTCtx.getSizeType()); + auto Mode = + ASTCtx.getLangOpts().AllocTokenMode.value_or(llvm::DefaultAllocTokenMode); + uint64_t MaxTokens = + ASTCtx.getLangOpts().AllocTokenMax.value_or(~0ULL >> (64 - BitWidth)); + + // We do not read any of the arguments; discard them. + for (int I = Call->getNumArgs() - 1; I >= 0; --I) + discard(S.Stk, *S.getContext().classify(Call->getArg(I))); + + // Note: Type inference from a surrounding cast is not supported in + // constexpr evaluation. + QualType AllocType = infer_alloc::inferPossibleType(Call, ASTCtx, nullptr); + if (AllocType.isNull()) { + S.CCEDiag(Call, + diag::note_constexpr_infer_alloc_token_type_inference_failed); + return false; + } + + auto ATMD = infer_alloc::getAllocTokenMetadata(AllocType, ASTCtx); + if (!ATMD) { + S.CCEDiag(Call, diag::note_constexpr_infer_alloc_token_no_metadata); + return false; + } + + auto MaybeToken = llvm::getAllocToken(Mode, *ATMD, MaxTokens); + if (!MaybeToken) { + S.CCEDiag(Call, diag::note_constexpr_infer_alloc_token_stateful_mode); + return false; + } + + pushInteger(S, llvm::APInt(BitWidth, *MaybeToken), ASTCtx.getSizeType()); + return true; +} + static bool interp__builtin_operator_new(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const CallExpr *Call) { @@ -3694,6 +3735,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case Builtin::BI__builtin_ptrauth_string_discriminator: return interp__builtin_ptrauth_string_discriminator(S, OpPC, Frame, Call); + case Builtin::BI__builtin_infer_alloc_token: + return interp__builtin_infer_alloc_token(S, OpPC, Frame, Call); + case Builtin::BI__noop: pushInteger(S, 0, Call->getType()); return true; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7db7a01998a4a..2bd4476b6f3a4 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -44,6 +44,7 @@ #include "clang/AST/CharUnits.h" #include "clang/AST/CurrentSourceLocExprScope.h" #include "clang/AST/Expr.h" +#include "clang/AST/InferAlloc.h" #include "clang/AST/OSLog.h" #include "clang/AST/OptionalDiagnostic.h" #include "clang/AST/RecordLayout.h" @@ -14663,6 +14664,27 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return Success(Result, E); } + case Builtin::BI__builtin_infer_alloc_token: { + // If we fail to infer a type, this fails to be a constant expression; this + // can be checked with __builtin_constant_p(...). + QualType AllocType = infer_alloc::inferPossibleType(E, Info.Ctx, nullptr); + if (AllocType.isNull()) + return Error( + E, diag::note_constexpr_infer_alloc_token_type_inference_failed); + auto ATMD = infer_alloc::getAllocTokenMetadata(AllocType, Info.Ctx); + if (!ATMD) + return Error(E, diag::note_constexpr_infer_alloc_token_no_metadata); + auto Mode = + Info.getLangOpts().AllocTokenMode.value_or(llvm::DefaultAllocTokenMode); + uint64_t BitWidth = Info.Ctx.getTypeSize(Info.Ctx.getSizeType()); + uint64_t MaxTokens = + Info.getLangOpts().AllocTokenMax.value_or(~0ULL >> (64 - BitWidth)); + auto MaybeToken = llvm::getAllocToken(Mode, *ATMD, MaxTokens); + if (!MaybeToken) + return Error(E, diag::note_constexpr_infer_alloc_token_stateful_mode); + return Success(llvm::APInt(BitWidth, *MaybeToken), E); + } + case Builtin::BI__builtin_ffs: case Builtin::BI__builtin_ffsl: case Builtin::BI__builtin_ffsll: { diff --git a/clang/test/SemaCXX/alloc-token.cpp b/clang/test/SemaCXX/alloc-token.cpp index a267442409886..be7acb7d42ef2 100644 --- a/clang/test/SemaCXX/alloc-token.cpp +++ b/clang/test/SemaCXX/alloc-token.cpp @@ -1,11 +1,65 @@ // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -verify %s // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -verify %s -fexperimental-new-constant-interpreter // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -verify %s -falloc-token-mode=typehash -DMODE_TYPEHASH +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -verify %s -falloc-token-max=2 -DTOKEN_MAX=2 #if !__has_builtin(__builtin_infer_alloc_token) #error "missing __builtin_infer_alloc_token" #endif +struct NoPtr { + int x; + long y; +}; + +struct WithPtr { + int a; + char *buf; +}; + +// Check specific known values; these are guaranteed to be stable. +#ifdef MODE_TYPEHASH +static_assert(__builtin_infer_alloc_token(sizeof(int)) == 2689373973731826898ULL); +static_assert(__builtin_infer_alloc_token(sizeof(char*)) == 2250492667400517147ULL); +static_assert(__builtin_infer_alloc_token(sizeof(NoPtr)) == 7465259095297095368ULL); +static_assert(__builtin_infer_alloc_token(sizeof(WithPtr)) == 11898882936532569145ULL); +#elif defined(TOKEN_MAX) +# if TOKEN_MAX == 2 +static_assert(__builtin_infer_alloc_token(sizeof(int)) == 0); +static_assert(__builtin_infer_alloc_token(sizeof(char*)) == 1); +static_assert(__builtin_infer_alloc_token(sizeof(NoPtr)) == 0); +static_assert(__builtin_infer_alloc_token(sizeof(WithPtr)) == 1); +# else +# error "unhandled TOKEN_MAX case" +# endif +#else +static_assert(__builtin_infer_alloc_token(sizeof(int)) == 2689373973731826898ULL); +static_assert(__builtin_infer_alloc_token(sizeof(char*)) == 11473864704255292954ULL); +static_assert(__builtin_infer_alloc_token(sizeof(NoPtr)) == 7465259095297095368ULL); +static_assert(__builtin_infer_alloc_token(sizeof(WithPtr)) == 11898882936532569145ULL); +#endif + +// Template function. +template <typename T> +constexpr unsigned long get_token() { + return __builtin_infer_alloc_token(sizeof(T)); +} +static_assert(__builtin_infer_alloc_token(sizeof(int)) == get_token<int>()); + +// Test complex expressions. +static_assert(__builtin_constant_p(__builtin_infer_alloc_token(sizeof(int)))); +static_assert(__builtin_infer_alloc_token(sizeof(NoPtr) * 2, 1) == get_token<NoPtr>()); +static_assert(__builtin_infer_alloc_token(1, 4 + sizeof(NoPtr)) == get_token<NoPtr>()); +static_assert(__builtin_infer_alloc_token(sizeof(NoPtr) << 8) == get_token<NoPtr>()); + +// Test usable as a template param. +template <unsigned long ID, typename T> +struct token_for_type { + static_assert(ID == get_token<T>()); + static constexpr unsigned long value = ID; +}; +static_assert(token_for_type<__builtin_infer_alloc_token(sizeof(int)), int>::value == get_token<int>()); + template <typename T = void> void template_test() { __builtin_infer_alloc_token(T()); // no error if not instantiated @@ -20,4 +74,6 @@ void negative_tests() { __builtin_infer_alloc_token(); // expected-error {{too few arguments to function call}} __builtin_infer_alloc_token((void)0); // expected-error {{argument may not have 'void' type}} negative_template_test<void>(); // expected-note {{in instantiation of function template specialization 'negative_template_test<void>' requested here}} + constexpr auto inference_fail = __builtin_infer_alloc_token(123); // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{could not infer allocation type for __builtin_infer_alloc_token}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
