Skip to content

Commit 6679082

Browse files
committed
[clang & libcxx] constexpr pointer tagging (not for merging, just for review)
1 parent 545e059 commit 6679082

File tree

11 files changed

+731
-0
lines changed

11 files changed

+731
-0
lines changed

clang/include/clang/AST/APValue.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ class APValue {
198198
/// The QualType, if this is a DynamicAllocLValue.
199199
void *DynamicAllocType;
200200
};
201+
public:
202+
uint64_t Metadata{0};
201203
};
202204

203205
/// A FieldDecl or CXXRecordDecl, along with a flag indicating whether we
@@ -527,6 +529,8 @@ class APValue {
527529
}
528530

529531
const LValueBase getLValueBase() const;
532+
uint64_t getLValueMetadata() const;
533+
uint64_t & getLValueMetadata();
530534
CharUnits &getLValueOffset();
531535
const CharUnits &getLValueOffset() const {
532536
return const_cast<APValue*>(this)->getLValueOffset();

clang/include/clang/Basic/Builtins.td

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4867,3 +4867,40 @@ def ArithmeticFence : LangBuiltin<"ALL_LANGUAGES"> {
48674867
let Attributes = [CustomTypeChecking, Constexpr];
48684868
let Prototype = "void(...)";
48694869
}
4870+
4871+
// support for pointer tagging
4872+
// (ptr & mask) | (val & ~mask)
4873+
def TagPointerMaskOr : Builtin {
4874+
let Spellings = ["__builtin_tag_pointer_mask_or"];
4875+
let Attributes = [Constexpr, NoThrow];
4876+
let Prototype = "void*(void*, size_t, size_t)";
4877+
}
4878+
4879+
// (ptr & mask) -> void *
4880+
def TagPointerMask : Builtin {
4881+
let Spellings = ["__builtin_tag_pointer_mask"];
4882+
let Attributes = [Constexpr, NoThrow];
4883+
let Prototype = "void*(void*, size_t)";
4884+
}
4885+
4886+
// (ptr & mask) -> uintptr_t
4887+
def TagPointerMaskAsInt : Builtin {
4888+
let Spellings = ["__builtin_tag_pointer_mask_as_int"];
4889+
let Attributes = [Constexpr, NoThrow];
4890+
let Prototype = "size_t(void*, size_t)";
4891+
}
4892+
4893+
// (ptr << shift) | (value & ~mask) -> void *
4894+
// mask = (1 << shift) - 1
4895+
def TagPointerShiftOr : Builtin {
4896+
let Spellings = ["__builtin_tag_pointer_shift_or"];
4897+
let Attributes = [Constexpr, NoThrow];
4898+
let Prototype = "void*(void*, size_t, size_t)";
4899+
}
4900+
4901+
// (ptr >> unshift) -> void *
4902+
def TagPointerUnshift : Builtin {
4903+
let Spellings = ["__builtin_tag_pointer_unshift"];
4904+
let Attributes = [Constexpr, NoThrow];
4905+
let Prototype = "void*(void*, size_t)";
4906+
}

clang/include/clang/Basic/DiagnosticASTKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,12 @@ def note_constexpr_access_null : Note<
218218
def note_constexpr_access_past_end : Note<
219219
"%sub{access_kind}0 dereferenced one-past-the-end pointer "
220220
"is not allowed in a constant expression">;
221+
def note_constexpr_dereferencing_tagged_pointer: Note<
222+
"dereferencing tagged pointer">;
223+
def note_constexpr_tagging_with_shift_zero: Note<
224+
"you must shift pointer at least by one bit to store a tag">;
225+
def note_constexpr_tagging_with_empty_mask: Note<
226+
"you must provide non-zero mask for pointer tagging">;
221227
def note_constexpr_access_unsized_array : Note<
222228
"%sub{access_kind}0 element of array without known bound "
223229
"is not allowed in a constant expression">;

clang/lib/AST/APValue.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,16 @@ const APValue::LValueBase APValue::getLValueBase() const {
975975
return ((const LV *)(const void *)&Data)->Base;
976976
}
977977

978+
uint64_t APValue::getLValueMetadata() const {
979+
assert(isLValue() && "Invalid accessor");
980+
return ((const LV *)(const void *)&Data)->Base.Metadata;
981+
}
982+
983+
uint64_t & APValue::getLValueMetadata() {
984+
assert(isLValue() && "Invalid accessor");
985+
return ((LV *)(void *)&Data)->Base.Metadata;
986+
}
987+
978988
bool APValue::isLValueOnePastTheEnd() const {
979989
assert(isLValue() && "Invalid accessor");
980990
return ((const LV *)(const void *)&Data)->IsOnePastTheEnd;

clang/lib/AST/ExprConstant.cpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4498,6 +4498,11 @@ handleLValueToRValueConversion(EvalInfo &Info, const Expr *Conv, QualType Type,
44984498
bool WantObjectRepresentation = false) {
44994499
if (LVal.Designator.Invalid)
45004500
return false;
4501+
4502+
if (LVal.Base.Metadata != 0) {
4503+
Info.FFDiag(Conv, diag::note_constexpr_dereferencing_tagged_pointer);
4504+
return false;
4505+
}
45014506

45024507
// Check for special cases where there is no existing APValue to look at.
45034508
const Expr *Base = LVal.Base.dyn_cast<const Expr*>();
@@ -9735,6 +9740,87 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
97359740
return Success(E);
97369741

97379742
switch (BuiltinOp) {
9743+
// emulation of pointer tagging without actually touching pointer value
9744+
// as there is no such thing as address here, so tag is stored as a metadata in lvalue base
9745+
case Builtin::BI__builtin_tag_pointer_mask_or: {
9746+
APSInt Value, Mask;
9747+
if (!evaluatePointer(E->getArg(0), Result))
9748+
return Error(E);
9749+
9750+
if (!EvaluateInteger(E->getArg(1), Value, Info))
9751+
return Error(E);
9752+
9753+
if (!EvaluateInteger(E->getArg(2), Mask, Info))
9754+
return Error(E);
9755+
9756+
if (Mask.getLimitedValue() == 0) {
9757+
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
9758+
return false;
9759+
}
9760+
9761+
Result.Base.Metadata = (Result.Base.Metadata & ~Mask.getLimitedValue()) | (Value.getLimitedValue() & Mask.getLimitedValue());
9762+
return true;
9763+
}
9764+
9765+
// alternative approach to tagging which shifts pointer
9766+
// here we are only shifting metadata
9767+
case Builtin::BI__builtin_tag_pointer_shift_or: {
9768+
APSInt Value, Shift;
9769+
if (!evaluatePointer(E->getArg(0), Result))
9770+
return Error(E);
9771+
9772+
if (!EvaluateInteger(E->getArg(1), Value, Info))
9773+
return Error(E);
9774+
9775+
if (!EvaluateInteger(E->getArg(2), Shift, Info))
9776+
return Error(E);
9777+
9778+
if (Shift.getLimitedValue() == 0) {
9779+
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_shift_zero);
9780+
return false;
9781+
}
9782+
9783+
const uint64_t Mask = (1ull << static_cast<uint64_t>(Shift.getLimitedValue())) - 1ull;
9784+
Result.Base.Metadata = (Result.Base.Metadata << static_cast<uint64_t>(Shift.getLimitedValue())) | (Value.getLimitedValue() & Mask);
9785+
return true;
9786+
}
9787+
9788+
// recover pointer by masking metadata
9789+
// exprconstant allows dereferencing only metadata == 0 pointer
9790+
case Builtin::BI__builtin_tag_pointer_mask: {
9791+
APSInt Mask;
9792+
if (!evaluatePointer(E->getArg(0), Result))
9793+
return Error(E);
9794+
9795+
if (!EvaluateInteger(E->getArg(1), Mask, Info))
9796+
return Error(E);
9797+
9798+
if (Mask.getLimitedValue() == 0) {
9799+
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
9800+
return false;
9801+
}
9802+
9803+
Result.Base.Metadata = (Result.Base.Metadata & Mask.getLimitedValue());
9804+
return true;
9805+
}
9806+
9807+
// shifting back pointer (also can convert tagged pointer back to normal pointer)
9808+
case Builtin::BI__builtin_tag_pointer_unshift: {
9809+
APSInt Shift;
9810+
if (!evaluatePointer(E->getArg(0), Result))
9811+
return Error(E);
9812+
9813+
if (!EvaluateInteger(E->getArg(1), Shift, Info))
9814+
return Error(E);
9815+
9816+
if (Shift.getLimitedValue() == 0) {
9817+
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_shift_zero);
9818+
return false;
9819+
}
9820+
9821+
Result.Base.Metadata = (Result.Base.Metadata >> static_cast<uint64_t>(Shift.getLimitedValue()));
9822+
return true;
9823+
}
97389824
case Builtin::BIaddressof:
97399825
case Builtin::BI__addressof:
97409826
case Builtin::BI__builtin_addressof:
@@ -12662,6 +12748,25 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1266212748
default:
1266312749
return false;
1266412750

12751+
case Builtin::BI__builtin_tag_pointer_mask_as_int: {
12752+
LValue Pointer;
12753+
APSInt Mask;
12754+
12755+
if (!EvaluatePointer(E->getArg(0), Pointer, Info))
12756+
return Error(E);
12757+
12758+
if (!EvaluateInteger(E->getArg(1), Mask, Info))
12759+
return Error(E);
12760+
12761+
if (Mask.getLimitedValue() == 0) {
12762+
CCEDiag(E->getArg(2), diag::note_constexpr_tagging_with_empty_mask);
12763+
return false;
12764+
}
12765+
12766+
const uint64_t Result = Pointer.Base.Metadata & (static_cast<uint64_t>(Mask.getLimitedValue()));
12767+
return Success(Result, E);
12768+
}
12769+
1266512770
case Builtin::BI__builtin_dynamic_object_size:
1266612771
case Builtin::BI__builtin_object_size: {
1266712772
// The type was checked when we built the expression.
@@ -14219,6 +14324,13 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
1421914324
return Success(CmpResult::Less, E);
1422014325
if (CompareLHS > CompareRHS)
1422114326
return Success(CmpResult::Greater, E);
14327+
14328+
// this makes tagged pointer not equal to original pointer
14329+
if (LHSValue.Base.Metadata < RHSValue.Base.Metadata)
14330+
return Success(CmpResult::Less, E);
14331+
if (LHSValue.Base.Metadata > RHSValue.Base.Metadata)
14332+
return Success(CmpResult::Greater, E);
14333+
1422214334
return Success(CmpResult::Equal, E);
1422314335
}
1422414336

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5320,6 +5320,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
53205320

53215321
return RValue::get(Carry);
53225322
}
5323+
5324+
// support for pointer tagging
5325+
case Builtin::BI__builtin_tag_pointer_mask_or:
5326+
return EmitBuiltinTagPointerMaskOr(E);
5327+
case Builtin::BI__builtin_tag_pointer_mask:
5328+
return EmitBuiltinTagPointerMask(E);
5329+
case Builtin::BI__builtin_tag_pointer_mask_as_int:
5330+
return EmitBuiltinTagPointerMaskAsInt(E);
5331+
case Builtin::BI__builtin_tag_pointer_shift_or:
5332+
return EmitBuiltinTagPointerShiftOr(E);
5333+
case Builtin::BI__builtin_tag_pointer_unshift:
5334+
return EmitBuiltinTagPointerUnshift(E);
5335+
53235336
case Builtin::BIaddressof:
53245337
case Builtin::BI__addressof:
53255338
case Builtin::BI__builtin_addressof:
@@ -21245,6 +21258,95 @@ Value *CodeGenFunction::EmitNVPTXBuiltinExpr(unsigned BuiltinID,
2124521258
}
2124621259
}
2124721260

21261+
/// Generate (x & ~mask) | (value & mask).
21262+
RValue CodeGenFunction::EmitBuiltinTagPointerMaskOr(const CallExpr *E) {
21263+
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
21264+
llvm::Value * Value = EmitScalarExpr(E->getArg(1));
21265+
llvm::Value * Mask = EmitScalarExpr(E->getArg(2));
21266+
21267+
llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));
21268+
21269+
// TODO: avoid using bitcast and go path of ptr.tag (mirror to ptr.mask)
21270+
// to keep pointer's provenance, but this turns out a bit harder to do as it touches
21271+
// a lot of places in llvm
21272+
llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
21273+
llvm::Value * InvertedMask = Builder.CreateNot(Mask, "inverted_mask");
21274+
21275+
llvm::Value * MaskedPtr = Builder.CreateAnd(PointerInt, InvertedMask, "masked_ptr");
21276+
llvm::Value * MaskedValue = Builder.CreateAnd(Value, Mask, "masked_value");
21277+
21278+
llvm::Value * ResultInt = Builder.CreateOr(MaskedPtr, MaskedValue, "result_int");
21279+
llvm::Value * Result = Builder.CreateBitOrPointerCast(ResultInt, Ptr->getType(), "result_ptr");
21280+
21281+
return RValue::get(Result);
21282+
}
21283+
21284+
/// Generate (x << shift) | (value & ((1 << shift) - 1)).
21285+
RValue CodeGenFunction::EmitBuiltinTagPointerShiftOr(const CallExpr *E) {
21286+
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
21287+
llvm::Value * Value = EmitScalarExpr(E->getArg(1));
21288+
llvm::Value * Shift = EmitScalarExpr(E->getArg(2));
21289+
21290+
llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));
21291+
21292+
// TODO: again, for now a bitcast, later ptr.shift_tag
21293+
llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
21294+
llvm::Value * ShiftedPointerInt = Builder.CreateShl(PointerInt, Shift);
21295+
21296+
auto *One = llvm::ConstantInt::get(IntType, 1);
21297+
21298+
llvm::Value * Mask = Builder.CreateSub(Builder.CreateShl(One, Shift), One, "mask");
21299+
llvm::Value * MaskedValue = Builder.CreateAdd(Value, Mask, "masked_value");
21300+
llvm::Value * PointerWithTag = Builder.CreateOr(ShiftedPointerInt, MaskedValue, "pointer_with_tag_int");
21301+
21302+
llvm::Value * Result = Builder.CreateBitOrPointerCast(PointerWithTag, Ptr->getType(), "result_ptr");
21303+
return RValue::get(Result);
21304+
}
21305+
21306+
/// Generate (x >> shift)
21307+
RValue CodeGenFunction::EmitBuiltinTagPointerUnshift(const CallExpr *E) {
21308+
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
21309+
llvm::Value * Shift = EmitScalarExpr(E->getArg(1));
21310+
21311+
llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));
21312+
21313+
// for now I'm going path of bitcast
21314+
llvm::Value * PointerInt = Builder.CreateBitOrPointerCast(Ptr, IntType, "pointer_int");
21315+
llvm::Value * UnShiftedPointerInt = Builder.CreateAShr(PointerInt, Shift, "unshifted_pointer_int");
21316+
21317+
llvm::Value * Result = Builder.CreateBitOrPointerCast(UnShiftedPointerInt, Ptr->getType(), "result_ptr");
21318+
return RValue::get(Result);
21319+
}
21320+
21321+
/// Generate (x & mask).
21322+
RValue CodeGenFunction::EmitBuiltinTagPointerMask(const CallExpr *E) {
21323+
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
21324+
llvm::Value * Mask = EmitScalarExpr(E->getArg(1));
21325+
21326+
llvm::Value *Result = Builder.CreateIntrinsic(
21327+
Intrinsic::ptrmask, {Ptr->getType(), Mask->getType()},
21328+
{Ptr, Mask}, nullptr, "result");
21329+
21330+
return RValue::get(Result);
21331+
}
21332+
21333+
/// Generate (x & mask) (but return it as number).
21334+
RValue CodeGenFunction::EmitBuiltinTagPointerMaskAsInt(const CallExpr *E) {
21335+
llvm::Value * Ptr = EmitScalarExpr(E->getArg(0));
21336+
llvm::Value * Mask = EmitScalarExpr(E->getArg(1));
21337+
21338+
llvm::IntegerType * IntType = IntegerType::get(getLLVMContext(), CGM.getDataLayout().getIndexTypeSizeInBits(Ptr->getType()));
21339+
21340+
llvm::Value *Result = Builder.CreateIntrinsic(
21341+
Intrinsic::ptrmask, {Ptr->getType(), Mask->getType()},
21342+
{Ptr, Mask}, nullptr, "result");
21343+
21344+
llvm::Value * IntResult = Builder.CreateBitOrPointerCast(Result, IntType, "int_result");
21345+
21346+
return RValue::get(IntResult);
21347+
}
21348+
21349+
2124821350
namespace {
2124921351
struct BuiltinAlignArgs {
2125021352
llvm::Value *Src = nullptr;

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4556,6 +4556,13 @@ class CodeGenFunction : public CodeGenTypeCache {
45564556

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

4559+
/// Emit IR for pointer tagging
4560+
RValue EmitBuiltinTagPointerMaskOr(const CallExpr *E);
4561+
RValue EmitBuiltinTagPointerMask(const CallExpr *E);
4562+
RValue EmitBuiltinTagPointerMaskAsInt(const CallExpr *E);
4563+
RValue EmitBuiltinTagPointerShiftOr(const CallExpr *E);
4564+
RValue EmitBuiltinTagPointerUnshift(const CallExpr *E);
4565+
45594566
/// Emit IR for __builtin_os_log_format.
45604567
RValue emitBuiltinOSLogFormat(const CallExpr &E);
45614568

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,7 @@ set(files
553553
__memory/raw_storage_iterator.h
554554
__memory/shared_ptr.h
555555
__memory/swap_allocator.h
556+
__memory/tagged_ptr.h
556557
__memory/temp_value.h
557558
__memory/temporary_buffer.h
558559
__memory/uninitialized_algorithms.h

0 commit comments

Comments
 (0)