Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;

Expand Down
44 changes: 44 additions & 0 deletions clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
22 changes: 22 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -14649,6 +14650,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: {
Expand Down
56 changes: 56 additions & 0 deletions clang/test/SemaCXX/alloc-token.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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}}
}
Loading