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

Reply via email to