Skip to content
Draft
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
4 changes: 4 additions & 0 deletions clang/include/clang/AST/APValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ class APValue {
/// The QualType, if this is a DynamicAllocLValue.
void *DynamicAllocType;
};
public:
uint64_t Metadata{0};
};

/// A FieldDecl or CXXRecordDecl, along with a flag indicating whether we
Expand Down Expand Up @@ -483,6 +485,8 @@ class APValue {
}

const LValueBase getLValueBase() const;
uint64_t getLValueMetadata() const;
uint64_t & getLValueMetadata();
CharUnits &getLValueOffset();
const CharUnits &getLValueOffset() const {
return const_cast<APValue*>(this)->getLValueOffset();
Expand Down
37 changes: 37 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4774,3 +4774,40 @@ def ArithmeticFence : LangBuiltin<"ALL_LANGUAGES"> {
let Attributes = [CustomTypeChecking, Constexpr];
let Prototype = "void(...)";
}

// support for pointer tagging
// (ptr & mask) | (val & ~mask)
def TagPointerMaskOr : Builtin {
let Spellings = ["__builtin_tag_pointer_mask_or"];
let Attributes = [Constexpr, NoThrow];
let Prototype = "void*(void*, size_t, size_t)";
}

// (ptr & mask) -> void *
def TagPointerMask : Builtin {
let Spellings = ["__builtin_tag_pointer_mask"];
let Attributes = [Constexpr, NoThrow];
let Prototype = "void*(void*, size_t)";
}

// (ptr & mask) -> uintptr_t
def TagPointerMaskAsInt : Builtin {
let Spellings = ["__builtin_tag_pointer_mask_as_int"];
let Attributes = [Constexpr, NoThrow];
let Prototype = "size_t(void*, size_t)";
}

// (ptr << shift) | (value & ~mask) -> void *
// mask = (1 << shift) - 1
def TagPointerShiftOr : Builtin {
let Spellings = ["__builtin_tag_pointer_shift_or"];
let Attributes = [Constexpr, NoThrow];
let Prototype = "void*(void*, size_t, size_t)";
}

// (ptr >> unshift) -> void *
def TagPointerUnshift : Builtin {
let Spellings = ["__builtin_tag_pointer_unshift"];
let Attributes = [Constexpr, NoThrow];
let Prototype = "void*(void*, size_t)";
}
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticASTKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@ def note_constexpr_access_past_end : Note<
"destruction of}0 "
"dereferenced one-past-the-end pointer is not allowed "
"in a constant expression">;
def note_constexpr_dereferencing_tagged_pointer: Note<
"dereferencing tagged pointer">;
def note_constexpr_tagging_with_shift_zero: Note<
"you must shift pointer at least by one bit to store a tag">;
def note_constexpr_tagging_with_empty_mask: Note<
"you must provide non-zero mask for pointer tagging">;
def note_constexpr_access_unsized_array : Note<
"%select{read of|read of|assignment to|increment of|decrement of|"
"member call on|dynamic_cast of|typeid applied to|construction of|"
Expand Down
10 changes: 10 additions & 0 deletions clang/lib/AST/APValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -976,6 +976,16 @@ const APValue::LValueBase APValue::getLValueBase() const {
return ((const LV *)(const void *)&Data)->Base;
}

uint64_t APValue::getLValueMetadata() const {
assert(isLValue() && "Invalid accessor");
return ((const LV *)(const void *)&Data)->Base.Metadata;
}

uint64_t & APValue::getLValueMetadata() {
assert(isLValue() && "Invalid accessor");
return ((LV *)(void *)&Data)->Base.Metadata;
}

bool APValue::isLValueOnePastTheEnd() const {
assert(isLValue() && "Invalid accessor");
return ((const LV *)(const void *)&Data)->IsOnePastTheEnd;
Expand Down
112 changes: 112 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4395,6 +4395,11 @@ handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type,
bool WantObjectRepresentation = false) {
if (LVal.Designator.Invalid)
return false;

if (LVal.Base.Metadata != 0) {
Info.FFDiag(Conv, diag::note_constexpr_dereferencing_tagged_pointer);
return false;
}

// Check for special cases where there is no existing APValue to look at.
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
Expand Down Expand Up @@ -9627,6 +9632,87 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
return Success(E);

switch (BuiltinOp) {
// emulation of pointer tagging without actually touching pointer value
// as there is no such thing as address here, so tag is stored as a metadata in lvalue base
case Builtin::BI__builtin_tag_pointer_mask_or: {
APSInt Value, Mask;
if (!evaluatePointer(E->getArg(0), Result))
return Error(E);

if (!EvaluateInteger(E->getArg(1), Value, Info))
return Error(E);

if (!EvaluateInteger(E->getArg(2), Mask, Info))
return Error(E);

if (Mask.getLimitedValue() == 0) {
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
return false;
}

Result.Base.Metadata = (Result.Base.Metadata & ~Mask.getLimitedValue()) | (Value.getLimitedValue() & Mask.getLimitedValue());
return true;
}

// alternative approach to tagging which shifts pointer
// here we are only shifting metadata
case Builtin::BI__builtin_tag_pointer_shift_or: {
APSInt Value, Shift;
if (!evaluatePointer(E->getArg(0), Result))
return Error(E);

if (!EvaluateInteger(E->getArg(1), Value, Info))
return Error(E);

if (!EvaluateInteger(E->getArg(2), Shift, Info))
return Error(E);

if (Shift.getLimitedValue() == 0) {
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_shift_zero);
return false;
}

const uint64_t Mask = (1ull << static_cast<uint64_t>(Shift.getLimitedValue())) - 1ull;
Result.Base.Metadata = (Result.Base.Metadata << static_cast<uint64_t>(Shift.getLimitedValue())) | (Value.getLimitedValue() & Mask);
return true;
}

// recover pointer by masking metadata
// exprconstant allows dereferencing only metadata == 0 pointer
case Builtin::BI__builtin_tag_pointer_mask: {
APSInt Mask;
if (!evaluatePointer(E->getArg(0), Result))
return Error(E);

if (!EvaluateInteger(E->getArg(1), Mask, Info))
return Error(E);

if (Mask.getLimitedValue() == 0) {
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
return false;
}

Result.Base.Metadata = (Result.Base.Metadata & Mask.getLimitedValue());
return true;
}

// shifting back pointer (also can convert tagged pointer back to normal pointer)
case Builtin::BI__builtin_tag_pointer_unshift: {
APSInt Shift;
if (!evaluatePointer(E->getArg(0), Result))
return Error(E);

if (!EvaluateInteger(E->getArg(1), Shift, Info))
return Error(E);

if (Shift.getLimitedValue() == 0) {
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_shift_zero);
return false;
}

Result.Base.Metadata = (Result.Base.Metadata >> static_cast<uint64_t>(Shift.getLimitedValue()));
return true;
}
case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
Expand Down Expand Up @@ -12520,6 +12606,25 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
default:
return false;

case Builtin::BI__builtin_tag_pointer_mask_as_int: {
LValue Pointer;
APSInt Mask;

if (!EvaluatePointer(E->getArg(0), Pointer, Info))
return Error(E);

if (!EvaluateInteger(E->getArg(1), Mask, Info))
return Error(E);

if (Mask.getLimitedValue() == 0) {
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
return false;
}

const uint64_t Result = Pointer.Base.Metadata & (static_cast<uint64_t>(Mask.getLimitedValue()));
return Success(Result, E);
}

case Builtin::BI__builtin_dynamic_object_size:
case Builtin::BI__builtin_object_size: {
// The type was checked when we built the expression.
Expand Down Expand Up @@ -13904,6 +14009,13 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
return Success(CmpResult::Less, E);
if (CompareLHS > CompareRHS)
return Success(CmpResult::Greater, E);

// this makes tagged pointer not equal to original pointer
if (LHSValue.Base.Metadata < RHSValue.Base.Metadata)
return Success(CmpResult::Less, E);
if (LHSValue.Base.Metadata > RHSValue.Base.Metadata)
return Success(CmpResult::Greater, E);

return Success(CmpResult::Equal, E);
}

Expand Down
102 changes: 102 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5254,6 +5254,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,

return RValue::get(Carry);
}

// support for pointer tagging
case Builtin::BI__builtin_tag_pointer_mask_or:
return EmitBuiltinTagPointerMaskOr(E);
case Builtin::BI__builtin_tag_pointer_mask:
return EmitBuiltinTagPointerMask(E);
case Builtin::BI__builtin_tag_pointer_mask_as_int:
return EmitBuiltinTagPointerMaskAsInt(E);
case Builtin::BI__builtin_tag_pointer_shift_or:
return EmitBuiltinTagPointerShiftOr(E);
case Builtin::BI__builtin_tag_pointer_unshift:
return EmitBuiltinTagPointerUnshift(E);

case Builtin::BIaddressof:
case Builtin::BI__addressof:
case Builtin::BI__builtin_addressof:
Expand Down Expand Up @@ -21030,6 +21043,95 @@ Value *CodeGenFunction::EmitNVPTXBuiltinExpr(unsigned BuiltinID,
}
}

/// Generate (x & ~mask) | (value & mask).
RValue CodeGenFunction::EmitBuiltinTagPointerMaskOr(const CallExpr *E) {
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
llvm::Value * Value = EmitScalarExpr(E->getArg(1));
llvm::Value * Mask = EmitScalarExpr(E->getArg(2));

llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));

// TODO: avoid using bitcast and go path of ptr.tag (mirror to ptr.mask)
// to keep pointer's provenance, but this turns out a bit harder to do as it touches
// a lot of places in llvm
llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
llvm::Value * InvertedMask = Builder.CreateNot(Mask, "inverted_mask");

llvm::Value * MaskedPtr = Builder.CreateAnd(PointerInt, InvertedMask, "masked_ptr");
llvm::Value * MaskedValue = Builder.CreateAnd(Value, Mask, "masked_value");

llvm::Value * ResultInt = Builder.CreateOr(MaskedPtr, MaskedValue, "result_int");
llvm::Value * Result = Builder.CreateBitOrPointerCast(ResultInt, Ptr->getType(), "result_ptr");

return RValue::get(Result);
}

/// Generate (x << shift) | (value & ((1 << shift) - 1)).
RValue CodeGenFunction::EmitBuiltinTagPointerShiftOr(const CallExpr *E) {
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
llvm::Value * Value = EmitScalarExpr(E->getArg(1));
llvm::Value * Shift = EmitScalarExpr(E->getArg(2));

llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));

// TODO: again, for now a bitcast, later ptr.shift_tag
llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
llvm::Value * ShiftedPointerInt = Builder.CreateShl(PointerInt, Shift);

auto *One = llvm::ConstantInt::get(IntType, 1);

llvm::Value * Mask = Builder.CreateSub(Builder.CreateShl(One, Shift), One, "mask");
llvm::Value * MaskedValue = Builder.CreateAnd(Value, Mask, "masked_value");
llvm::Value * PointerWithTag = Builder.CreateOr(ShiftedPointerInt, MaskedValue, "pointer_with_tag_int");

llvm::Value * Result = Builder.CreateBitOrPointerCast(PointerWithTag, Ptr->getType(), "result_ptr");
return RValue::get(Result);
}

/// Generate (x >> shift)
RValue CodeGenFunction::EmitBuiltinTagPointerUnshift(const CallExpr *E) {
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
llvm::Value * Shift = EmitScalarExpr(E->getArg(1));

llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));

// for now I'm going path of bitcast
llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
llvm::Value * UnShiftedPointerInt = Builder.CreateAShr(PointerInt, Shift, "unshifted_pointer_int");

llvm::Value * Result = Builder.CreateBitOrPointerCast(UnShiftedPointerInt, Ptr->getType(), "result_ptr");
return RValue::get(Result);
}

/// Generate (x & mask).
RValue CodeGenFunction::EmitBuiltinTagPointerMask(const CallExpr *E) {
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
llvm::Value * Mask = EmitScalarExpr(E->getArg(1));

llvm::Value *Result = Builder.CreateIntrinsic(
Intrinsic::ptrmask, {Ptr->getType(), Mask->getType()},
{Ptr, Mask}, nullptr, "result");

return RValue::get(Result);
}

/// Generate (x & mask) (but return it as number).
RValue CodeGenFunction::EmitBuiltinTagPointerMaskAsInt(const CallExpr *E) {
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
llvm::Value * Mask = EmitScalarExpr(E->getArg(1));

llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));

llvm::Value *Result = Builder.CreateIntrinsic(
Intrinsic::ptrmask, {Ptr->getType(), Mask->getType()},
{Ptr, Mask}, nullptr, "result");

llvm::Value * IntResult = Builder.CreateBitOrPointerCast(Result, IntType, "int_result");

return RValue::get(IntResult);
}


namespace {
struct BuiltinAlignArgs {
llvm::Value *Src = nullptr;
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -4546,6 +4546,13 @@ class CodeGenFunction : public CodeGenTypeCache {

RValue emitRotate(const CallExpr *E, bool IsRotateRight);

/// Emit IR for pointer tagging
RValue EmitBuiltinTagPointerMaskOr(const CallExpr *E);
RValue EmitBuiltinTagPointerMask(const CallExpr *E);
RValue EmitBuiltinTagPointerMaskAsInt(const CallExpr *E);
RValue EmitBuiltinTagPointerShiftOr(const CallExpr *E);
RValue EmitBuiltinTagPointerUnshift(const CallExpr *E);

/// Emit IR for __builtin_os_log_format.
RValue emitBuiltinOSLogFormat(const CallExpr &E);

Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,7 @@ set(files
__memory/inout_ptr.h
__memory/out_ptr.h
__memory/pointer_traits.h
__memory/pointer_tag_pair.h
__memory/ranges_construct_at.h
__memory/ranges_uninitialized_algorithms.h
__memory/raw_storage_iterator.h
Expand Down
Loading