Skip to content

Commit 1709eac

Browse files
authored
[clang][Interp] Integral pointers (#84159)
This turns the current `Pointer` class into a discriminated union of `BlockPointer` and `IntPointer`. The former is what `Pointer` currently is while the latter is just an integer value and an optional `Descriptor*`. The `Pointer` then has type check functions like `isBlockPointer()`/`isIntegralPointer()`/`asBlockPointer()`/`asIntPointer()`, which can be used to access its data. Right now, the `IntPointer` and `BlockPointer` structs do not have any methods of their own and everything is instead implemented in Pointer (like it was before) and the functions now just either assert for the right type or decide what to do based on it. This also implements bitcasts by decaying the pointer to an integral pointer. `test/AST/Interp/const-eval.c` is a new test testing all kinds of stuff related to this. It still has a few tests `#ifdef`-ed out but that mostly depends on other unimplemented things like `__builtin_constant_p`.
1 parent ec40097 commit 1709eac

File tree

14 files changed

+798
-182
lines changed

14 files changed

+798
-182
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,18 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
173173
return this->emitCastFloatingIntegral(*ToT, CE);
174174
}
175175

176-
case CK_NullToPointer:
176+
case CK_NullToPointer: {
177177
if (DiscardResult)
178178
return true;
179-
return this->emitNull(classifyPrim(CE->getType()), CE);
179+
180+
const Descriptor *Desc = nullptr;
181+
const QualType PointeeType = CE->getType()->getPointeeType();
182+
if (!PointeeType.isNull()) {
183+
if (std::optional<PrimType> T = classify(PointeeType))
184+
Desc = P.createDescriptor(SubExpr, *T);
185+
}
186+
return this->emitNull(classifyPrim(CE->getType()), Desc, CE);
187+
}
180188

181189
case CK_PointerToIntegral: {
182190
if (DiscardResult)
@@ -199,6 +207,41 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
199207
return true;
200208
}
201209

210+
case CK_IntegralToPointer: {
211+
QualType IntType = SubExpr->getType();
212+
assert(IntType->isIntegralOrEnumerationType());
213+
if (!this->visit(SubExpr))
214+
return false;
215+
// FIXME: I think the discard is wrong since the int->ptr cast might cause a
216+
// diagnostic.
217+
PrimType T = classifyPrim(IntType);
218+
if (DiscardResult)
219+
return this->emitPop(T, CE);
220+
221+
QualType PtrType = CE->getType();
222+
assert(PtrType->isPointerType());
223+
224+
const Descriptor *Desc;
225+
if (std::optional<PrimType> T = classify(PtrType->getPointeeType()))
226+
Desc = P.createDescriptor(SubExpr, *T);
227+
else if (PtrType->getPointeeType()->isVoidType())
228+
Desc = nullptr;
229+
else
230+
Desc = P.createDescriptor(CE, PtrType->getPointeeType().getTypePtr(),
231+
Descriptor::InlineDescMD, true, false,
232+
/*IsMutable=*/false, nullptr);
233+
234+
if (!this->emitGetIntPtr(T, Desc, CE))
235+
return false;
236+
237+
PrimType DestPtrT = classifyPrim(PtrType);
238+
if (DestPtrT == PT_Ptr)
239+
return true;
240+
241+
// In case we're converting the integer to a non-Pointer.
242+
return this->emitDecayPtr(PT_Ptr, DestPtrT, CE);
243+
}
244+
202245
case CK_AtomicToNonAtomic:
203246
case CK_ConstructorConversion:
204247
case CK_FunctionToPointerDecay:
@@ -207,13 +250,31 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
207250
case CK_UserDefinedConversion:
208251
return this->delegate(SubExpr);
209252

210-
case CK_BitCast:
253+
case CK_BitCast: {
254+
// Reject bitcasts to atomic types.
211255
if (CE->getType()->isAtomicType()) {
212256
if (!this->discard(SubExpr))
213257
return false;
214258
return this->emitInvalidCast(CastKind::Reinterpret, CE);
215259
}
216-
return this->delegate(SubExpr);
260+
261+
if (DiscardResult)
262+
return this->discard(SubExpr);
263+
264+
std::optional<PrimType> FromT = classify(SubExpr->getType());
265+
std::optional<PrimType> ToT = classifyPrim(CE->getType());
266+
if (!FromT || !ToT)
267+
return false;
268+
269+
assert(isPtrType(*FromT));
270+
assert(isPtrType(*ToT));
271+
if (FromT == ToT)
272+
return this->delegate(SubExpr);
273+
274+
if (!this->visit(SubExpr))
275+
return false;
276+
return this->emitDecayPtr(*FromT, *ToT, CE);
277+
}
217278

218279
case CK_IntegralToBoolean:
219280
case CK_IntegralCast: {
@@ -245,7 +306,7 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
245306
if (!this->visit(SubExpr))
246307
return false;
247308

248-
if (!this->emitNull(PtrT, CE))
309+
if (!this->emitNull(PtrT, nullptr, CE))
249310
return false;
250311

251312
return this->emitNE(PtrT, CE);
@@ -455,7 +516,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
455516

456517
// Pointer arithmetic special case.
457518
if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) {
458-
if (T == PT_Ptr || (LT == PT_Ptr && RT == PT_Ptr))
519+
if (isPtrType(*T) || (isPtrType(*LT) && isPtrType(*RT)))
459520
return this->VisitPointerArithBinOp(BO);
460521
}
461522

@@ -2354,7 +2415,7 @@ bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) {
23542415

23552416
// Convert pointers to bool.
23562417
if (T == PT_Ptr || T == PT_FnPtr) {
2357-
if (!this->emitNull(*T, E))
2418+
if (!this->emitNull(*T, nullptr, E))
23582419
return false;
23592420
return this->emitNE(*T, E);
23602421
}
@@ -2394,9 +2455,9 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
23942455
case PT_IntAPS:
23952456
return this->emitZeroIntAPS(Ctx.getBitWidth(QT), E);
23962457
case PT_Ptr:
2397-
return this->emitNullPtr(E);
2458+
return this->emitNullPtr(nullptr, E);
23982459
case PT_FnPtr:
2399-
return this->emitNullFnPtr(E);
2460+
return this->emitNullFnPtr(nullptr, E);
24002461
case PT_Float: {
24012462
return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
24022463
}
@@ -2980,7 +3041,7 @@ bool ByteCodeExprGen<Emitter>::VisitCXXNullPtrLiteralExpr(
29803041
if (DiscardResult)
29813042
return true;
29823043

2983-
return this->emitNullPtr(E);
3044+
return this->emitNullPtr(nullptr, E);
29843045
}
29853046

29863047
template <class Emitter>

clang/lib/AST/Interp/ByteCodeStmtGen.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ bool ByteCodeStmtGen<Emitter>::emitLambdaStaticInvokerBody(
110110
// one here, and we don't need one either because the lambda cannot have
111111
// any captures, as verified above. Emit a null pointer. This is then
112112
// special-cased when interpreting to not emit any misleading diagnostics.
113-
if (!this->emitNullPtr(MD))
113+
if (!this->emitNullPtr(nullptr, MD))
114114
return false;
115115

116116
// Forward all arguments from the static invoker to the lambda call operator.

clang/lib/AST/Interp/Descriptor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ struct Descriptor final {
168168

169169
const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
170170
const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); }
171+
const DeclTy &getSource() const { return Source; }
171172

172173
const ValueDecl *asValueDecl() const {
173174
return dyn_cast_if_present<ValueDecl>(asDecl());

clang/lib/AST/Interp/FunctionPointer.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ class FunctionPointer final {
2222
const Function *Func;
2323

2424
public:
25-
FunctionPointer() : Func(nullptr) {}
25+
// FIXME: We might want to track the fact that the Function pointer
26+
// has been created from an integer and is most likely garbage anyway.
27+
FunctionPointer(int IntVal = 0, const Descriptor *Desc = nullptr)
28+
: Func(reinterpret_cast<const Function *>(IntVal)) {}
29+
2630
FunctionPointer(const Function *Func) : Func(Func) { assert(Func); }
2731

2832
const Function *getFunction() const { return Func; }
@@ -53,6 +57,10 @@ class FunctionPointer final {
5357
return toAPValue().getAsString(Ctx, Func->getDecl()->getType());
5458
}
5559

60+
uint64_t getIntegerRepresentation() const {
61+
return static_cast<uint64_t>(reinterpret_cast<uintptr_t>(Func));
62+
}
63+
5664
ComparisonCategoryResult compare(const FunctionPointer &RHS) const {
5765
if (Func == RHS.Func)
5866
return ComparisonCategoryResult::Equal;

clang/lib/AST/Interp/Interp.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,8 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
282282
}
283283

284284
static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
285+
if (Ptr.isIntegralPointer())
286+
return true;
285287
return CheckConstant(S, OpPC, Ptr.getDeclDesc());
286288
}
287289

@@ -335,6 +337,9 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
335337
return true;
336338
}
337339

340+
if (!Ptr.isBlockPointer())
341+
return false;
342+
338343
const QualType Ty = Ptr.getType();
339344
const SourceInfo &Loc = S.Current->getSource(OpPC);
340345
S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;

clang/lib/AST/Interp/Interp.h

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -804,8 +804,7 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
804804
for (const auto &P : {LHS, RHS}) {
805805
if (P.isZero())
806806
continue;
807-
if (const ValueDecl *VD = P.getDeclDesc()->asValueDecl();
808-
VD && VD->isWeak()) {
807+
if (P.isWeak()) {
809808
const SourceInfo &Loc = S.Current->getSource(OpPC);
810809
S.FFDiag(Loc, diag::note_constexpr_pointer_weak_comparison)
811810
<< P.toDiagnosticString(S.getCtx());
@@ -824,9 +823,9 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
824823
// element in the same array are NOT equal. They have the same Base value,
825824
// but a different Offset. This is a pretty rare case, so we fix this here
826825
// by comparing pointers to the first elements.
827-
if (!LHS.isDummy() && LHS.isArrayRoot())
826+
if (!LHS.isZero() && !LHS.isDummy() && LHS.isArrayRoot())
828827
VL = LHS.atIndex(0).getByteOffset();
829-
if (!RHS.isDummy() && RHS.isArrayRoot())
828+
if (!RHS.isZero() && !RHS.isDummy() && RHS.isArrayRoot())
830829
VR = RHS.atIndex(0).getByteOffset();
831830

832831
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
@@ -1387,6 +1386,8 @@ bool Load(InterpState &S, CodePtr OpPC) {
13871386
const Pointer &Ptr = S.Stk.peek<Pointer>();
13881387
if (!CheckLoad(S, OpPC, Ptr))
13891388
return false;
1389+
if (!Ptr.isBlockPointer())
1390+
return false;
13901391
S.Stk.push<T>(Ptr.deref<T>());
13911392
return true;
13921393
}
@@ -1396,6 +1397,8 @@ bool LoadPop(InterpState &S, CodePtr OpPC) {
13961397
const Pointer &Ptr = S.Stk.pop<Pointer>();
13971398
if (!CheckLoad(S, OpPC, Ptr))
13981399
return false;
1400+
if (!Ptr.isBlockPointer())
1401+
return false;
13991402
S.Stk.push<T>(Ptr.deref<T>());
14001403
return true;
14011404
}
@@ -1534,8 +1537,12 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
15341537
return true;
15351538
}
15361539

1537-
if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
1538-
return false;
1540+
if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) {
1541+
// The CheckNull will have emitted a note already, but we only
1542+
// abort in C++, since this is fine in C.
1543+
if (S.getLangOpts().CPlusPlus)
1544+
return false;
1545+
}
15391546

15401547
// Arrays of unknown bounds cannot have pointers into them.
15411548
if (!CheckArray(S, OpPC, Ptr))
@@ -1561,23 +1568,25 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
15611568
Invalid = true;
15621569
};
15631570

1564-
T MaxOffset = T::from(MaxIndex - Index, Offset.bitWidth());
1565-
if constexpr (Op == ArithOp::Add) {
1566-
// If the new offset would be negative, bail out.
1567-
if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
1568-
DiagInvalidOffset();
1569-
1570-
// If the new offset would be out of bounds, bail out.
1571-
if (Offset.isPositive() && Offset > MaxOffset)
1572-
DiagInvalidOffset();
1573-
} else {
1574-
// If the new offset would be negative, bail out.
1575-
if (Offset.isPositive() && Index < Offset)
1576-
DiagInvalidOffset();
1577-
1578-
// If the new offset would be out of bounds, bail out.
1579-
if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
1580-
DiagInvalidOffset();
1571+
if (Ptr.isBlockPointer()) {
1572+
T MaxOffset = T::from(MaxIndex - Index, Offset.bitWidth());
1573+
if constexpr (Op == ArithOp::Add) {
1574+
// If the new offset would be negative, bail out.
1575+
if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
1576+
DiagInvalidOffset();
1577+
1578+
// If the new offset would be out of bounds, bail out.
1579+
if (Offset.isPositive() && Offset > MaxOffset)
1580+
DiagInvalidOffset();
1581+
} else {
1582+
// If the new offset would be negative, bail out.
1583+
if (Offset.isPositive() && Index < Offset)
1584+
DiagInvalidOffset();
1585+
1586+
// If the new offset would be out of bounds, bail out.
1587+
if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
1588+
DiagInvalidOffset();
1589+
}
15811590
}
15821591

15831592
if (Invalid && !Ptr.isDummy() && S.getLangOpts().CPlusPlus)
@@ -1661,6 +1670,11 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC) {
16611670
const Pointer &LHS = S.Stk.pop<Pointer>();
16621671
const Pointer &RHS = S.Stk.pop<Pointer>();
16631672

1673+
if (RHS.isZero()) {
1674+
S.Stk.push<T>(T::from(LHS.getIndex()));
1675+
return true;
1676+
}
1677+
16641678
if (!Pointer::hasSameBase(LHS, RHS) && S.getLangOpts().CPlusPlus) {
16651679
// TODO: Diagnose.
16661680
return false;
@@ -1839,8 +1853,9 @@ static inline bool ZeroIntAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
18391853
}
18401854

18411855
template <PrimType Name, class T = typename PrimConv<Name>::T>
1842-
inline bool Null(InterpState &S, CodePtr OpPC) {
1843-
S.Stk.push<T>();
1856+
inline bool Null(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
1857+
// Note: Desc can be null.
1858+
S.Stk.push<T>(0, Desc);
18441859
return true;
18451860
}
18461861

@@ -2244,6 +2259,14 @@ inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) {
22442259
return true;
22452260
}
22462261

2262+
template <PrimType Name, class T = typename PrimConv<Name>::T>
2263+
inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
2264+
const T &IntVal = S.Stk.pop<T>();
2265+
2266+
S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
2267+
return true;
2268+
}
2269+
22472270
/// Just emit a diagnostic. The expression that caused emission of this
22482271
/// op is not valid in a constant context.
22492272
inline bool Invalid(InterpState &S, CodePtr OpPC) {
@@ -2300,6 +2323,18 @@ inline bool CheckNonNullArg(InterpState &S, CodePtr OpPC) {
23002323
return false;
23012324
}
23022325

2326+
/// OldPtr -> Integer -> NewPtr.
2327+
template <PrimType TIn, PrimType TOut>
2328+
inline bool DecayPtr(InterpState &S, CodePtr OpPC) {
2329+
static_assert(isPtrType(TIn) && isPtrType(TOut));
2330+
using FromT = typename PrimConv<TIn>::T;
2331+
using ToT = typename PrimConv<TOut>::T;
2332+
2333+
const FromT &OldPtr = S.Stk.pop<FromT>();
2334+
S.Stk.push<ToT>(ToT(OldPtr.getIntegerRepresentation(), nullptr));
2335+
return true;
2336+
}
2337+
23032338
//===----------------------------------------------------------------------===//
23042339
// Read opcode arguments
23052340
//===----------------------------------------------------------------------===//

clang/lib/AST/Interp/InterpBlock.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void Block::replacePointer(Pointer *Old, Pointer *New) {
7373
removePointer(Old);
7474
addPointer(New);
7575

76-
Old->Pointee = nullptr;
76+
Old->PointeeStorage.BS.Pointee = nullptr;
7777

7878
#ifndef NDEBUG
7979
assert(!hasPointer(Old));
@@ -104,7 +104,7 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
104104
// Transfer pointers.
105105
B.Pointers = Blk->Pointers;
106106
for (Pointer *P = Blk->Pointers; P; P = P->Next)
107-
P->Pointee = &B;
107+
P->PointeeStorage.BS.Pointee = &B;
108108
}
109109

110110
void DeadBlock::free() {

0 commit comments

Comments
 (0)