Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
47 changes: 35 additions & 12 deletions clang/docs/AllocToken.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ The default mode to calculate tokens is:
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>``:
change or removal. These may (experimentally) be selected with ``-Xclang
-falloc-token-mode=<mode>``:

* ``typehash``: This mode assigns a token ID based on the hash of the allocated
type's name.
Expand All @@ -49,6 +49,39 @@ change or removal. These may (experimentally) be selected with ``-mllvm
* ``increment``: This mode assigns a simple, incrementally increasing token ID
to each allocation site.

The following command-line options affect generated token IDs:

* ``-falloc-token-max=<N>``
Configures the maximum number of tokens. No max by default (tokens bounded
by ``SIZE_MAX``).

Querying Token IDs with ``__builtin_infer_alloc_token``
=======================================================

For use cases where the token ID must be known at compile time, Clang provides
a builtin function:

.. code-block:: c

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.

For example, it can be used as follows:

.. code-block:: c

struct MyType { ... };
void *__partition_alloc(size_t size, size_t partition);
#define partition_alloc(...) __partition_alloc(__VA_ARGS__, __builtin_infer_alloc_token(__VA_ARGS__))

void foo(void) {
MyType *x = partition_alloc(sizeof(*x));
}

Allocation Token Instrumentation
================================

Expand All @@ -70,16 +103,6 @@ example:
// Instrumented:
ptr = __alloc_token_malloc(size, <token id>);

The following command-line options affect generated token IDs:

* ``-falloc-token-max=<N>``
Configures the maximum number of tokens. No max by default (tokens bounded
by ``SIZE_MAX``).

.. code-block:: console

% clang++ -fsanitize=alloc-token -falloc-token-max=512 example.cc

Runtime Interface
-----------------

Expand Down
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ Non-comprehensive list of changes in this release
allocator-level heap organization strategies. A feature to instrument all
allocation functions with a token ID can be enabled via the
``-fsanitize=alloc-token`` flag.
- A builtin ``__builtin_infer_alloc_token(<args>, ...)`` is provided to allow
compile-time querying of allocation token IDs, where the builtin arguments
mirror those normally passed to an allocation function.

New Compiler Flags
------------------
Expand Down
35 changes: 35 additions & 0 deletions clang/include/clang/AST/InferAlloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===--- InferAlloc.h - Allocation type inference ---------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines interfaces for allocation-related type inference.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_AST_INFERALLOC_H
#define LLVM_CLANG_AST_INFERALLOC_H

#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "llvm/Support/AllocToken.h"
#include <optional>

namespace clang {
namespace infer_alloc {

/// Infer the possible allocated type from an allocation call expression.
QualType inferPossibleType(const CallExpr *E, const ASTContext &Ctx,
const CastExpr *CastE);

/// Get the information required for construction of an allocation token ID.
std::optional<llvm::AllocTokenMetadata>
getAllocTokenMetadata(QualType T, const ASTContext &Ctx);

} // namespace infer_alloc
} // namespace clang

#endif // LLVM_CLANG_AST_INFERALLOC_H
7 changes: 7 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4727,6 +4727,13 @@ def PtrauthStringDiscriminator : Builtin {
let Prototype = "size_t(char const*)";
}

// AllocToken builtins.
def InferAllocToken : Builtin {
let Spellings = ["__builtin_infer_alloc_token"];
let Attributes = [NoThrow, Const, Pure, CustomTypeChecking, Constexpr, UnevaluatedArguments];
let Prototype = "size_t(...)";
}

// OpenCL v2.0 s6.13.16, s9.17.3.5 - Pipe functions.
// We need the generic prototype, since the packet type could be anything.
def ReadPipe : OCLPipeLangBuiltin {
Expand Down
4 changes: 0 additions & 4 deletions clang/include/clang/Basic/CodeGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -447,10 +447,6 @@ class CodeGenOptions : public CodeGenOptionsBase {

std::optional<double> AllowRuntimeCheckSkipHotCutoff;

/// Maximum number of allocation tokens (0 = no max), nullopt if none set (use
/// pass default).
std::optional<uint64_t> AllocTokenMax;

/// List of backend command-line options for -fembed-bitcode.
std::vector<uint8_t> CmdArgs;

Expand Down
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
8 changes: 8 additions & 0 deletions clang/include/clang/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "llvm/ADT/FloatingPointMode.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/DXContainer.h"
#include "llvm/Support/AllocToken.h"
#include "llvm/TargetParser/Triple.h"
#include <optional>
#include <string>
Expand Down Expand Up @@ -565,6 +566,13 @@ class LangOptions : public LangOptionsBase {
bool AtomicFineGrainedMemory = false;
bool AtomicIgnoreDenormalMode = false;

/// Maximum number of allocation tokens (0 = no max), nullopt if none set (use
/// target default).
std::optional<uint64_t> AllocTokenMax;

/// The allocation token mode.
std::optional<llvm::AllocTokenMode> AllocTokenMode;

LangOptions();

/// Set language defaults for the given input language and
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2751,6 +2751,10 @@ def falloc_token_max_EQ : Joined<["-"], "falloc-token-max=">,
MetaVarName<"<N>">,
HelpText<"Limit to maximum N allocation tokens (0 = no max)">;

def falloc_token_mode_EQ : Joined<["-"], "falloc-token-mode=">,
Group<f_Group>, Visibility<[CC1Option]>,
HelpText<"Set the allocation token mode (experimental)">;

def fallow_runtime_check_skip_hot_cutoff_EQ
: Joined<["-"], "fallow-runtime-check-skip-hot-cutoff=">,
Group<f_clang_Group>,
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 @@ -11,12 +11,14 @@
#include "Interp.h"
#include "InterpBuiltinBitCast.h"
#include "PrimType.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 @@ -1306,6 +1308,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 @@ -3489,6 +3530,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
1 change: 1 addition & 0 deletions clang/lib/AST/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ add_clang_library(clangAST
ExternalASTMerger.cpp
ExternalASTSource.cpp
FormatString.cpp
InferAlloc.cpp
InheritViz.cpp
ByteCode/BitcastBuffer.cpp
ByteCode/ByteCodeEmitter.cpp
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 @@ -14415,6 +14416,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
Loading
Loading