[clang] [clang][Interp] Integral pointers (PR #84159)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 6 04:05:59 PST 2024


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/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`.

>From 0ffa1ff8e909d4fa7a324b3623348fac9969572f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Wed, 6 Mar 2024 08:36:52 +0100
Subject: [PATCH] Integral pointers

---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp |  71 ++++-
 clang/lib/AST/Interp/ByteCodeStmtGen.cpp |   2 +-
 clang/lib/AST/Interp/Descriptor.h        |   1 +
 clang/lib/AST/Interp/FunctionPointer.h   |  10 +-
 clang/lib/AST/Interp/Interp.cpp          |   5 +
 clang/lib/AST/Interp/Interp.h            |  89 ++++--
 clang/lib/AST/Interp/InterpBlock.cpp     |   4 +-
 clang/lib/AST/Interp/Opcodes.td          |  12 +
 clang/lib/AST/Interp/Pointer.cpp         | 173 ++++++++---
 clang/lib/AST/Interp/Pointer.h           | 366 ++++++++++++++++-------
 clang/lib/AST/Interp/PrimType.h          |   4 +
 clang/test/AST/Interp/c.c                |  18 ++
 clang/test/AST/Interp/const-eval.c       | 194 ++++++++++++
 13 files changed, 769 insertions(+), 180 deletions(-)
 create mode 100644 clang/test/AST/Interp/const-eval.c

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index d887170cbc5d2d..ace571dd6bd821 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -161,10 +161,18 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
     return this->emitCastFloatingIntegral(*ToT, CE);
   }
 
-  case CK_NullToPointer:
+  case CK_NullToPointer: {
     if (DiscardResult)
       return true;
-    return this->emitNull(classifyPrim(CE->getType()), CE);
+
+    const Descriptor *Desc = nullptr;
+    const QualType PointeeType = CE->getType()->getPointeeType();
+    if (!PointeeType.isNull()) {
+      if (std::optional<PrimType> T = classify(PointeeType))
+        Desc = P.createDescriptor(SubExpr, *T);
+    }
+    return this->emitNull(classifyPrim(CE->getType()), Desc, CE);
+  }
 
   case CK_PointerToIntegral: {
     if (DiscardResult)
@@ -187,6 +195,34 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
     return true;
   }
 
+  case CK_IntegralToPointer: {
+    QualType IntType = SubExpr->getType();
+    assert(IntType->isIntegralOrEnumerationType());
+    if (!this->visit(SubExpr))
+      return false;
+    // FIXME: I think the discard is wrong since the int->ptr cast might cause a
+    // diagnostic.
+    PrimType T = classifyPrim(IntType);
+    if (DiscardResult)
+      return this->emitPop(T, CE);
+
+    QualType PtrType = CE->getType();
+    assert(PtrType->isPointerType());
+
+    const Descriptor *Desc;
+    if (std::optional<PrimType> T = classify(PtrType->getPointeeType())) {
+      Desc = P.createDescriptor(SubExpr, *T);
+    } else if (PtrType->getPointeeType()->isVoidType())
+      Desc = nullptr;
+    else {
+      Desc = P.createDescriptor(CE, PtrType->getPointeeType().getTypePtr(),
+                                Descriptor::InlineDescMD, true, false,
+                                /*IsMutable=*/false, nullptr);
+    }
+
+    return this->emitGetIntPtr(T, Desc, CE);
+  }
+
   case CK_AtomicToNonAtomic:
   case CK_ConstructorConversion:
   case CK_FunctionToPointerDecay:
@@ -195,13 +231,28 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
   case CK_UserDefinedConversion:
     return this->delegate(SubExpr);
 
-  case CK_BitCast:
+  case CK_BitCast: {
+    // Reject bitcasts to atomic types.
     if (CE->getType()->isAtomicType()) {
       if (!this->discard(SubExpr))
         return false;
       return this->emitInvalidCast(CastKind::Reinterpret, CE);
     }
-    return this->delegate(SubExpr);
+
+    if (DiscardResult)
+      return this->discard(SubExpr);
+
+    PrimType FromT = classifyPrim(SubExpr->getType());
+    PrimType ToT = classifyPrim(CE->getType());
+    assert(isPtrType(FromT));
+    assert(isPtrType(ToT));
+    if (FromT == ToT)
+      return this->delegate(SubExpr);
+
+    if (!this->visit(SubExpr))
+      return false;
+    return this->emitDecayPtr(FromT, ToT, CE);
+  }
 
   case CK_IntegralToBoolean:
   case CK_IntegralCast: {
@@ -233,7 +284,7 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
     if (!this->visit(SubExpr))
       return false;
 
-    if (!this->emitNull(PtrT, CE))
+    if (!this->emitNull(PtrT, nullptr, CE))
       return false;
 
     return this->emitNE(PtrT, CE);
@@ -445,7 +496,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
 
   // Pointer arithmetic special case.
   if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) {
-    if (T == PT_Ptr || (LT == PT_Ptr && RT == PT_Ptr))
+    if (isPtrType(*T) || (isPtrType(*LT) && isPtrType(*RT)))
       return this->VisitPointerArithBinOp(BO);
   }
 
@@ -2296,7 +2347,7 @@ bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) {
 
   // Convert pointers to bool.
   if (T == PT_Ptr || T == PT_FnPtr) {
-    if (!this->emitNull(*T, E))
+    if (!this->emitNull(*T, nullptr, E))
       return false;
     return this->emitNE(*T, E);
   }
@@ -2336,9 +2387,9 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, QualType QT,
   case PT_IntAPS:
     return this->emitZeroIntAPS(Ctx.getBitWidth(QT), E);
   case PT_Ptr:
-    return this->emitNullPtr(E);
+    return this->emitNullPtr(nullptr, E);
   case PT_FnPtr:
-    return this->emitNullFnPtr(E);
+    return this->emitNullFnPtr(nullptr, E);
   case PT_Float: {
     return this->emitConstFloat(APFloat::getZero(Ctx.getFloatSemantics(QT)), E);
   }
@@ -2930,7 +2981,7 @@ bool ByteCodeExprGen<Emitter>::VisitCXXNullPtrLiteralExpr(
   if (DiscardResult)
     return true;
 
-  return this->emitNullPtr(E);
+  return this->emitNullPtr(nullptr, E);
 }
 
 template <class Emitter>
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 6da3860f98d8c0..95b482c9464654 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -110,7 +110,7 @@ bool ByteCodeStmtGen<Emitter>::emitLambdaStaticInvokerBody(
   // one here, and we don't need one either because the lambda cannot have
   // any captures, as verified above. Emit a null pointer. This is then
   // special-cased when interpreting to not emit any misleading diagnostics.
-  if (!this->emitNullPtr(MD))
+  if (!this->emitNullPtr(nullptr, MD))
     return false;
 
   // Forward all arguments from the static invoker to the lambda call operator.
diff --git a/clang/lib/AST/Interp/Descriptor.h b/clang/lib/AST/Interp/Descriptor.h
index 0f64d678f3ef6b..fa170e328325ac 100644
--- a/clang/lib/AST/Interp/Descriptor.h
+++ b/clang/lib/AST/Interp/Descriptor.h
@@ -160,6 +160,7 @@ struct Descriptor final {
 
   const Decl *asDecl() const { return Source.dyn_cast<const Decl *>(); }
   const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); }
+  const DeclTy &getSource() const { return Source; }
 
   const ValueDecl *asValueDecl() const {
     return dyn_cast_if_present<ValueDecl>(asDecl());
diff --git a/clang/lib/AST/Interp/FunctionPointer.h b/clang/lib/AST/Interp/FunctionPointer.h
index bb3da9b50aa552..717d385e6307ca 100644
--- a/clang/lib/AST/Interp/FunctionPointer.h
+++ b/clang/lib/AST/Interp/FunctionPointer.h
@@ -22,7 +22,11 @@ class FunctionPointer final {
   const Function *Func;
 
 public:
-  FunctionPointer() : Func(nullptr) {}
+  // FIXME: We might want to track the fact that the Function pointer
+  // has been created from an integer and is most likely garbage anyway.
+  FunctionPointer(int IntVal = 0, const Descriptor *Desc = nullptr)
+      : Func(reinterpret_cast<const Function *>(IntVal)) {}
+
   FunctionPointer(const Function *Func) : Func(Func) { assert(Func); }
 
   const Function *getFunction() const { return Func; }
@@ -53,6 +57,10 @@ class FunctionPointer final {
     return toAPValue().getAsString(Ctx, Func->getDecl()->getType());
   }
 
+  uint32_t getIntegerRepresentation() const {
+    return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(Func));
+  }
+
   ComparisonCategoryResult compare(const FunctionPointer &RHS) const {
     if (Func == RHS.Func)
       return ComparisonCategoryResult::Equal;
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 4f3cd6cd21a151..e4d421839bbbc4 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -282,6 +282,8 @@ bool CheckConstant(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
 }
 
 static bool CheckConstant(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+  if (Ptr.isIntegralPointer())
+    return true;
   return CheckConstant(S, OpPC, Ptr.getDeclDesc());
 }
 
@@ -335,6 +337,9 @@ bool CheckConst(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
     return true;
   }
 
+  if (!Ptr.isBlockPointer())
+    return false;
+
   const QualType Ty = Ptr.getType();
   const SourceInfo &Loc = S.Current->getSource(OpPC);
   S.FFDiag(Loc, diag::note_constexpr_modify_const_type) << Ty;
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 9c3d58d5619c46..6dcfa3f8c354c4 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -809,9 +809,9 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
     // element in the same array are NOT equal. They have the same Base value,
     // but a different Offset. This is a pretty rare case, so we fix this here
     // by comparing pointers to the first elements.
-    if (!LHS.isDummy() && LHS.isArrayRoot())
+    if (!LHS.isZero() && !LHS.isDummy() && LHS.isArrayRoot())
       VL = LHS.atIndex(0).getByteOffset();
-    if (!RHS.isDummy() && RHS.isArrayRoot())
+    if (!RHS.isDummy() && !RHS.isDummy() && RHS.isArrayRoot())
       VR = RHS.atIndex(0).getByteOffset();
 
     S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
@@ -1368,6 +1368,8 @@ bool Load(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.peek<Pointer>();
   if (!CheckLoad(S, OpPC, Ptr))
     return false;
+  if (!Ptr.isBlockPointer())
+    return false;
   S.Stk.push<T>(Ptr.deref<T>());
   return true;
 }
@@ -1377,6 +1379,8 @@ bool LoadPop(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
   if (!CheckLoad(S, OpPC, Ptr))
     return false;
+  if (!Ptr.isBlockPointer())
+    return false;
   S.Stk.push<T>(Ptr.deref<T>());
   return true;
 }
@@ -1505,12 +1509,17 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
     return true;
   }
 
-  if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
-    return false;
+  if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex)) {
+    // The CheckNul will have emitted a note already, but we only
+    // abort in C++, since this is fine in C.
+    if (S.getLangOpts().CPlusPlus)
+      return false;
+  }
 
   // Arrays of unknown bounds cannot have pointers into them.
-  if (!CheckArray(S, OpPC, Ptr))
+  if (!CheckArray(S, OpPC, Ptr)) {
     return false;
+  }
 
   // Get a version of the index comparable to the type.
   T Index = T::from(Ptr.getIndex(), Offset.bitWidth());
@@ -1532,23 +1541,25 @@ bool OffsetHelper(InterpState &S, CodePtr OpPC, const T &Offset,
     Invalid = true;
   };
 
-  T MaxOffset = T::from(MaxIndex - Index, Offset.bitWidth());
-  if constexpr (Op == ArithOp::Add) {
-    // If the new offset would be negative, bail out.
-    if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
-      DiagInvalidOffset();
-
-    // If the new offset would be out of bounds, bail out.
-    if (Offset.isPositive() && Offset > MaxOffset)
-      DiagInvalidOffset();
-  } else {
-    // If the new offset would be negative, bail out.
-    if (Offset.isPositive() && Index < Offset)
-      DiagInvalidOffset();
-
-    // If the new offset would be out of bounds, bail out.
-    if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
-      DiagInvalidOffset();
+  if (Ptr.isBlockPointer()) {
+    T MaxOffset = T::from(MaxIndex - Index, Offset.bitWidth());
+    if constexpr (Op == ArithOp::Add) {
+      // If the new offset would be negative, bail out.
+      if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
+        DiagInvalidOffset();
+
+      // If the new offset would be out of bounds, bail out.
+      if (Offset.isPositive() && Offset > MaxOffset)
+        DiagInvalidOffset();
+    } else {
+      // If the new offset would be negative, bail out.
+      if (Offset.isPositive() && Index < Offset)
+        DiagInvalidOffset();
+
+      // If the new offset would be out of bounds, bail out.
+      if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
+        DiagInvalidOffset();
+    }
   }
 
   if (Invalid && !Ptr.isDummy() && S.getLangOpts().CPlusPlus)
@@ -1632,6 +1643,11 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC) {
   const Pointer &LHS = S.Stk.pop<Pointer>();
   const Pointer &RHS = S.Stk.pop<Pointer>();
 
+  if (RHS.isZero()) {
+    S.Stk.push<T>(T::from(LHS.getIndex()));
+    return true;
+  }
+
   if (!Pointer::hasSameBase(LHS, RHS) && S.getLangOpts().CPlusPlus) {
     // TODO: Diagnose.
     return false;
@@ -1810,8 +1826,9 @@ static inline bool ZeroIntAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
 }
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
-inline bool Null(InterpState &S, CodePtr OpPC) {
-  S.Stk.push<T>();
+inline bool Null(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
+  // Note: Desc can be null.
+  S.Stk.push<T>(0, Desc);
   return true;
 }
 
@@ -1912,6 +1929,10 @@ inline bool NoRet(InterpState &S, CodePtr OpPC) {
 
 inline bool NarrowPtr(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
+  if (!S.getLangOpts().CPlusPlus) {
+    S.Stk.push<Pointer>(Ptr);
+    return true;
+  }
   S.Stk.push<Pointer>(Ptr.narrow());
   return true;
 }
@@ -2180,6 +2201,14 @@ inline bool GetFnPtr(InterpState &S, CodePtr OpPC, const Function *Func) {
   return true;
 }
 
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool GetIntPtr(InterpState &S, CodePtr OpPC, const Descriptor *Desc) {
+  const T &IntVal = S.Stk.pop<T>();
+
+  S.Stk.push<Pointer>(static_cast<int64_t>(IntVal), Desc);
+  return true;
+}
+
 /// Just emit a diagnostic. The expression that caused emission of this
 /// op is not valid in a constant context.
 inline bool Invalid(InterpState &S, CodePtr OpPC) {
@@ -2233,6 +2262,18 @@ inline bool CheckNonNullArg(InterpState &S, CodePtr OpPC) {
   return false;
 }
 
+/// OldPtr -> Integer -> NewPtr.
+template <PrimType TIn, PrimType TOut>
+inline bool DecayPtr(InterpState &S, CodePtr OpPC) {
+  static_assert(isPtrType(TIn) && isPtrType(TOut));
+  using FromT = typename PrimConv<TIn>::T;
+  using ToT = typename PrimConv<TOut>::T;
+
+  const FromT &OldPtr = S.Stk.pop<FromT>();
+  S.Stk.push<ToT>(ToT(OldPtr.getIntegerRepresentation(), nullptr));
+  return true;
+}
+
 //===----------------------------------------------------------------------===//
 // Read opcode arguments
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/Interp/InterpBlock.cpp b/clang/lib/AST/Interp/InterpBlock.cpp
index a62128d9cfaedd..9b33d1b778fb2c 100644
--- a/clang/lib/AST/Interp/InterpBlock.cpp
+++ b/clang/lib/AST/Interp/InterpBlock.cpp
@@ -73,7 +73,7 @@ void Block::replacePointer(Pointer *Old, Pointer *New) {
   removePointer(Old);
   addPointer(New);
 
-  Old->Pointee = nullptr;
+  Old->PointeeStorage.BS.Pointee = nullptr;
 
 #ifndef NDEBUG
   assert(!hasPointer(Old));
@@ -104,7 +104,7 @@ DeadBlock::DeadBlock(DeadBlock *&Root, Block *Blk)
   // Transfer pointers.
   B.Pointers = Blk->Pointers;
   for (Pointer *P = Blk->Pointers; P; P = P->Next)
-    P->Pointee = &B;
+    P->PointeeStorage.BS.Pointee = &B;
 }
 
 void DeadBlock::free() {
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index ffc54646f0279e..68bd2218a2d6bb 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -59,6 +59,7 @@ def ArgCastKind : ArgType { let Name = "CastKind"; }
 def ArgCallExpr : ArgType { let Name = "const CallExpr *"; }
 def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; }
 def ArgDeclRef : ArgType { let Name = "const DeclRefExpr *"; }
+def ArgDesc : ArgType { let Name = "const Descriptor *"; }
 def ArgCCI : ArgType { let Name = "const ComparisonCategoryInfo *"; }
 
 //===----------------------------------------------------------------------===//
@@ -272,6 +273,7 @@ def ZeroIntAPS : Opcode {
 // [] -> [Pointer]
 def Null : Opcode {
   let Types = [PtrTypeClass];
+  let Args = [ArgDesc];
   let HasGroup = 1;
 }
 
@@ -522,6 +524,11 @@ def GetFnPtr : Opcode {
   let Args = [ArgFunction];
 }
 
+def GetIntPtr : Opcode {
+  let Types = [IntegerTypeClass];
+  let Args = [ArgDesc];
+  let HasGroup = 1;
+}
 
 //===----------------------------------------------------------------------===//
 // Binary operators.
@@ -654,6 +661,11 @@ def CastPointerIntegral : Opcode {
   let HasGroup = 1;
 }
 
+def DecayPtr : Opcode {
+  let Types = [PtrTypeClass, PtrTypeClass];
+  let HasGroup = 1;
+}
+
 //===----------------------------------------------------------------------===//
 // Comparison opcodes.
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp
index 3f85635f43674d..ac0e3184aa782d 100644
--- a/clang/lib/AST/Interp/Pointer.cpp
+++ b/clang/lib/AST/Interp/Pointer.cpp
@@ -26,60 +26,98 @@ Pointer::Pointer(Block *Pointee)
 Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
     : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
 
-Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
+Pointer::Pointer(const Pointer &P)
+    : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
+      StorageKind(P.StorageKind) {
 
-Pointer::Pointer(Pointer &&P)
-    : Pointee(P.Pointee), Base(P.Base), Offset(P.Offset) {
-  if (Pointee)
-    Pointee->replacePointer(&P, this);
+  if (isBlockPointer() && PointeeStorage.BS.Pointee)
+    PointeeStorage.BS.Pointee->addPointer(this);
 }
 
 Pointer::Pointer(Block *Pointee, unsigned Base, unsigned Offset)
-    : Pointee(Pointee), Base(Base), Offset(Offset) {
+    : Offset(Offset), StorageKind(Storage::Block) {
   assert((Base == RootPtrMark || Base % alignof(void *) == 0) && "wrong base");
+
+  PointeeStorage.BS = {Pointee, Base};
+
   if (Pointee)
     Pointee->addPointer(this);
 }
 
+Pointer::Pointer(Pointer &&P)
+    : Offset(P.Offset), PointeeStorage(P.PointeeStorage),
+      StorageKind(P.StorageKind) {
+
+  if (StorageKind == Storage::Block && PointeeStorage.BS.Pointee) {
+    PointeeStorage.BS.Pointee->replacePointer(&P, this);
+  }
+}
+
 Pointer::~Pointer() {
-  if (Pointee) {
-    Pointee->removePointer(this);
-    Pointee->cleanup();
+  if (isIntegralPointer())
+    return;
+
+  if (PointeeStorage.BS.Pointee) {
+    PointeeStorage.BS.Pointee->removePointer(this);
+    PointeeStorage.BS.Pointee->cleanup();
   }
 }
 
 void Pointer::operator=(const Pointer &P) {
-  Block *Old = Pointee;
 
-  if (Pointee)
-    Pointee->removePointer(this);
+  if (this->isIntegralPointer() && P.isBlockPointer()) {
+  } else {
+    assert(P.StorageKind == StorageKind);
+  }
 
-  Offset = P.Offset;
-  Base = P.Base;
+  bool WasBlockPointer = isBlockPointer();
+  StorageKind = P.StorageKind;
+  if (StorageKind == Storage::Block) {
+    Block *Old = PointeeStorage.BS.Pointee;
+    if (WasBlockPointer && PointeeStorage.BS.Pointee)
+      PointeeStorage.BS.Pointee->removePointer(this);
 
-  Pointee = P.Pointee;
-  if (Pointee)
-    Pointee->addPointer(this);
+    Offset = P.Offset;
+    PointeeStorage.BS = P.PointeeStorage.BS;
+
+    if (PointeeStorage.BS.Pointee)
+      PointeeStorage.BS.Pointee->addPointer(this);
+
+    if (WasBlockPointer && Old)
+      Old->cleanup();
 
-  if (Old)
-    Old->cleanup();
+  } else if (StorageKind == Storage::Int) {
+    PointeeStorage.Int = P.PointeeStorage.Int;
+  } else
+    assert(false);
 }
 
 void Pointer::operator=(Pointer &&P) {
-  Block *Old = Pointee;
+  if (this->isIntegralPointer() && P.isBlockPointer()) {
+  } else {
+    assert(P.StorageKind == StorageKind);
+  }
 
-  if (Pointee)
-    Pointee->removePointer(this);
+  bool WasBlockPointer = isBlockPointer();
+  StorageKind = P.StorageKind;
+  if (StorageKind == Storage::Block) {
+    Block *Old = PointeeStorage.BS.Pointee;
+    if (WasBlockPointer && PointeeStorage.BS.Pointee)
+      PointeeStorage.BS.Pointee->removePointer(this);
 
-  Offset = P.Offset;
-  Base = P.Base;
+    Offset = P.Offset;
+    PointeeStorage.BS = P.PointeeStorage.BS;
 
-  Pointee = P.Pointee;
-  if (Pointee)
-    Pointee->replacePointer(&P, this);
+    if (PointeeStorage.BS.Pointee)
+      PointeeStorage.BS.Pointee->addPointer(this);
 
-  if (Old)
-    Old->cleanup();
+    if (WasBlockPointer && Old)
+      Old->cleanup();
+
+  } else if (StorageKind == Storage::Int) {
+    PointeeStorage.Int = P.PointeeStorage.Int;
+  } else
+    assert(false);
 }
 
 APValue Pointer::toAPValue() const {
@@ -88,6 +126,12 @@ APValue Pointer::toAPValue() const {
   if (isZero())
     return APValue(static_cast<const Expr *>(nullptr), CharUnits::Zero(), Path,
                    /*IsOnePastEnd=*/false, /*IsNullPtr=*/true);
+  if (isIntegralPointer()) {
+    return APValue(static_cast<const Expr *>(nullptr),
+                   CharUnits::fromQuantity(asIntPointer().Value + this->Offset),
+                   Path,
+                   /*IsOnePastEnd=*/false, /*IsNullPtr=*/false);
+  }
 
   // Build the lvalue base from the block.
   const Descriptor *Desc = getDeclDesc();
@@ -123,8 +167,9 @@ APValue Pointer::toAPValue() const {
         Path.push_back(APValue::LValuePathEntry({BaseOrMember, IsVirtual}));
         Ptr = Ptr.getBase();
         continue;
+
+        llvm_unreachable("Invalid field type");
       }
-      llvm_unreachable("Invalid field type");
     }
   }
 
@@ -137,19 +182,53 @@ APValue Pointer::toAPValue() const {
   return APValue(Base, Offset, Path, IsOnePastEnd, /*IsNullPtr=*/false);
 }
 
+void Pointer::print(llvm::raw_ostream &OS) const {
+  OS << PointeeStorage.BS.Pointee << " (";
+  if (isBlockPointer()) {
+    OS << "Block) {";
+
+    if (PointeeStorage.BS.Base == RootPtrMark)
+      OS << "rootptr, ";
+    else
+      OS << PointeeStorage.BS.Base << ", ";
+
+    if (Offset == PastEndMark)
+      OS << "pastend, ";
+    else
+      OS << Offset << ", ";
+
+    if (isBlockPointer() && PointeeStorage.BS.Pointee)
+      OS << PointeeStorage.BS.Pointee->getSize();
+    else
+      OS << "nullptr";
+  } else {
+    OS << "Int) {";
+    OS << PointeeStorage.Int.Value << ", " << PointeeStorage.Int.Desc;
+  }
+  OS << "}";
+}
+
 std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const {
-  if (!Pointee)
+  if (isZero())
     return "nullptr";
 
+  if (isIntegralPointer())
+    return std::string("&(") + std::to_string(asIntPointer().Value + Offset) +
+           ")";
+
   return toAPValue().getAsString(Ctx, getType());
 }
 
 bool Pointer::isInitialized() const {
-  assert(Pointee && "Cannot check if null pointer was initialized");
+  if (isIntegralPointer())
+    return true;
+
+  assert(PointeeStorage.BS.Pointee &&
+         "Cannot check if null pointer was initialized");
   const Descriptor *Desc = getFieldDesc();
   assert(Desc);
   if (Desc->isPrimitiveArray()) {
-    if (isStatic() && Base == 0)
+    if (isStatic() && PointeeStorage.BS.Base == 0)
       return true;
 
     InitMapPtr &IM = getInitMap();
@@ -164,17 +243,20 @@ bool Pointer::isInitialized() const {
   }
 
   // Field has its bit in an inline descriptor.
-  return Base == 0 || getInlineDesc()->IsInitialized;
+  return PointeeStorage.BS.Base == 0 || getInlineDesc()->IsInitialized;
 }
 
 void Pointer::initialize() const {
-  assert(Pointee && "Cannot initialize null pointer");
+  if (isIntegralPointer())
+    return;
+
+  assert(PointeeStorage.BS.Pointee && "Cannot initialize null pointer");
   const Descriptor *Desc = getFieldDesc();
 
   assert(Desc);
   if (Desc->isPrimitiveArray()) {
     // Primitive global arrays don't have an initmap.
-    if (isStatic() && Base == 0)
+    if (isStatic() && PointeeStorage.BS.Base == 0)
       return;
 
     InitMapPtr &IM = getInitMap();
@@ -196,13 +278,15 @@ void Pointer::initialize() const {
   }
 
   // Field has its bit in an inline descriptor.
-  assert(Base != 0 && "Only composite fields can be initialised");
+  assert(PointeeStorage.BS.Base != 0 &&
+         "Only composite fields can be initialised");
   getInlineDesc()->IsInitialized = true;
 }
 
 void Pointer::activate() const {
   // Field has its bit in an inline descriptor.
-  assert(Base != 0 && "Only composite fields can be initialised");
+  assert(PointeeStorage.BS.Base != 0 &&
+         "Only composite fields can be initialised");
   getInlineDesc()->IsActive = true;
 }
 
@@ -211,11 +295,20 @@ void Pointer::deactivate() const {
 }
 
 bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
-  return A.Pointee == B.Pointee;
+  // Two null pointers always have the same base.
+  if (A.isZero() && B.isZero())
+    return true;
+
+  if (A.isIntegralPointer() && B.isIntegralPointer())
+    return true;
+
+  return A.getSource() == B.getSource();
 }
 
 bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
-  return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
+  return hasSameBase(A, B) &&
+         A.PointeeStorage.BS.Base == B.PointeeStorage.BS.Base &&
+         A.getFieldDesc()->IsArray;
 }
 
 std::optional<APValue> Pointer::toRValue(const Context &Ctx) const {
diff --git a/clang/lib/AST/Interp/Pointer.h b/clang/lib/AST/Interp/Pointer.h
index 34ecdb967960d5..0c861c11a2b25b 100644
--- a/clang/lib/AST/Interp/Pointer.h
+++ b/clang/lib/AST/Interp/Pointer.h
@@ -28,11 +28,26 @@ class Block;
 class DeadBlock;
 class Pointer;
 class Context;
+template <unsigned A, bool B> class Integral;
 enum PrimType : unsigned;
 
 class Pointer;
 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P);
 
+struct BlockPointer {
+  /// The block the pointer is pointing to.
+  Block *Pointee;
+  /// Start of the current subfield.
+  unsigned Base;
+};
+
+struct IntPointer {
+  const Descriptor *Desc;
+  int Value;
+};
+
+enum class Storage { Block, Int };
+
 /// A pointer to a memory block, live or dead.
 ///
 /// This object can be allocated into interpreter stack frames. If pointing to
@@ -68,11 +83,20 @@ class Pointer {
   static constexpr unsigned RootPtrMark = ~0u;
 
 public:
-  Pointer() {}
+  Pointer() {
+    StorageKind = Storage::Int;
+    PointeeStorage.Int.Value = 0;
+    PointeeStorage.Int.Desc = nullptr;
+  }
   Pointer(Block *B);
   Pointer(Block *B, unsigned BaseAndOffset);
   Pointer(const Pointer &P);
   Pointer(Pointer &&P);
+  Pointer(uint64_t Address, const Descriptor *Desc, unsigned Offset = 0)
+      : Offset(Offset), StorageKind(Storage::Int) {
+    PointeeStorage.Int.Value = Address;
+    PointeeStorage.Int.Desc = Desc;
+  }
   ~Pointer();
 
   void operator=(const Pointer &P);
@@ -80,13 +104,20 @@ class Pointer {
 
   /// Equality operators are just for tests.
   bool operator==(const Pointer &P) const {
-    return Pointee == P.Pointee && Base == P.Base && Offset == P.Offset;
-  }
+    if (P.StorageKind != StorageKind)
+      return false;
+    if (isIntegralPointer())
+      return P.asIntPointer().Value == asIntPointer().Value &&
+             Offset == P.Offset;
 
-  bool operator!=(const Pointer &P) const {
-    return Pointee != P.Pointee || Base != P.Base || Offset != P.Offset;
+    assert(isBlockPointer());
+    return P.asBlockPointer().Pointee == asBlockPointer().Pointee &&
+           P.asBlockPointer().Base == asBlockPointer().Base &&
+           Offset == P.Offset;
   }
 
+  bool operator!=(const Pointer &P) const { return !(P == *this); }
+
   /// Converts the pointer to an APValue.
   APValue toAPValue() const;
 
@@ -94,7 +125,9 @@ class Pointer {
   std::string toDiagnosticString(const ASTContext &Ctx) const;
 
   unsigned getIntegerRepresentation() const {
-    return reinterpret_cast<uintptr_t>(Pointee) + Offset;
+    if (isIntegralPointer())
+      return asIntPointer().Value + (Offset * elemSize());
+    return reinterpret_cast<uintptr_t>(asBlockPointer().Pointee) + Offset;
   }
 
   /// Converts the pointer to an APValue that is an rvalue.
@@ -102,20 +135,27 @@ class Pointer {
 
   /// Offsets a pointer inside an array.
   [[nodiscard]] Pointer atIndex(unsigned Idx) const {
-    if (Base == RootPtrMark)
-      return Pointer(Pointee, RootPtrMark, getDeclDesc()->getSize());
+    if (isIntegralPointer())
+      return Pointer(asIntPointer().Value, asIntPointer().Desc, Idx);
+
+    if (asBlockPointer().Base == RootPtrMark)
+      return Pointer(asBlockPointer().Pointee, RootPtrMark,
+                     getDeclDesc()->getSize());
     unsigned Off = Idx * elemSize();
     if (getFieldDesc()->ElemDesc)
       Off += sizeof(InlineDescriptor);
     else
       Off += sizeof(InitMapPtr);
-    return Pointer(Pointee, Base, Base + Off);
+    return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
+                   asBlockPointer().Base + Off);
   }
 
   /// Creates a pointer to a field.
   [[nodiscard]] Pointer atField(unsigned Off) const {
     unsigned Field = Offset + Off;
-    return Pointer(Pointee, Field, Field);
+    if (isIntegralPointer())
+      return Pointer(asIntPointer().Value + Field, asIntPointer().Desc);
+    return Pointer(asBlockPointer().Pointee, Field, Field);
   }
 
   /// Subtract the given offset from the current Base and Offset
@@ -123,44 +163,49 @@ class Pointer {
   [[nodiscard]]  Pointer atFieldSub(unsigned Off) const {
     assert(Offset >= Off);
     unsigned O = Offset - Off;
-    return Pointer(Pointee, O, O);
+    return Pointer(asBlockPointer().Pointee, O, O);
   }
 
   /// Restricts the scope of an array element pointer.
   [[nodiscard]] Pointer narrow() const {
+    if (!isBlockPointer())
+      return *this;
+    assert(isBlockPointer());
     // Null pointers cannot be narrowed.
     if (isZero() || isUnknownSizeArray())
       return *this;
 
     // Pointer to an array of base types - enter block.
-    if (Base == RootPtrMark)
-      return Pointer(Pointee, sizeof(InlineDescriptor),
+    if (asBlockPointer().Base == RootPtrMark)
+      return Pointer(asBlockPointer().Pointee, sizeof(InlineDescriptor),
                      Offset == 0 ? Offset : PastEndMark);
 
     // Pointer is one past end - magic offset marks that.
     if (isOnePastEnd())
-      return Pointer(Pointee, Base, PastEndMark);
+      return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
+                     PastEndMark);
 
     // Primitive arrays are a bit special since they do not have inline
     // descriptors. If Offset != Base, then the pointer already points to
     // an element and there is nothing to do. Otherwise, the pointer is
     // adjusted to the first element of the array.
     if (inPrimitiveArray()) {
-      if (Offset != Base)
+      if (Offset != asBlockPointer().Base)
         return *this;
-      return Pointer(Pointee, Base, Offset + sizeof(InitMapPtr));
+      return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
+                     Offset + sizeof(InitMapPtr));
     }
 
     // Pointer is to a field or array element - enter it.
-    if (Offset != Base)
-      return Pointer(Pointee, Offset, Offset);
+    if (Offset != asBlockPointer().Base)
+      return Pointer(asBlockPointer().Pointee, Offset, Offset);
 
     // Enter the first element of an array.
     if (!getFieldDesc()->isArray())
       return *this;
 
-    const unsigned NewBase = Base + sizeof(InlineDescriptor);
-    return Pointer(Pointee, NewBase, NewBase);
+    const unsigned NewBase = asBlockPointer().Base + sizeof(InlineDescriptor);
+    return Pointer(asBlockPointer().Pointee, NewBase, NewBase);
   }
 
   /// Expands a pointer to the containing array, undoing narrowing.
@@ -172,72 +217,107 @@ class Pointer {
         Adjust = sizeof(InitMapPtr);
       else
         Adjust = sizeof(InlineDescriptor);
-      return Pointer(Pointee, Base, Base + getSize() + Adjust);
+      return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
+                     asBlockPointer().Base + getSize() + Adjust);
     }
 
     // Do not step out of array elements.
-    if (Base != Offset)
+    if (asBlockPointer().Base != Offset)
       return *this;
 
     // If at base, point to an array of base types.
-    if (Base == 0 || Base == sizeof(InlineDescriptor))
-      return Pointer(Pointee, RootPtrMark, 0);
+    if (asBlockPointer().Base == 0 ||
+        asBlockPointer().Base == sizeof(InlineDescriptor))
+      return Pointer(asBlockPointer().Pointee, RootPtrMark, 0);
 
     // Step into the containing array, if inside one.
-    unsigned Next = Base - getInlineDesc()->Offset;
+    unsigned Next = asBlockPointer().Base - getInlineDesc()->Offset;
     const Descriptor *Desc =
         Next == 0 ? getDeclDesc() : getDescriptor(Next)->Desc;
     if (!Desc->IsArray)
       return *this;
-    return Pointer(Pointee, Next, Offset);
+    return Pointer(asBlockPointer().Pointee, Next, Offset);
   }
 
   /// Checks if the pointer is null.
-  bool isZero() const { return Pointee == nullptr; }
+  bool isZero() const {
+    if (Offset != 0)
+      return false;
+
+    if (isBlockPointer())
+      return asBlockPointer().Pointee == nullptr;
+    else if (isIntegralPointer())
+      return asIntPointer().Value == 0;
+    llvm_unreachable("zomg");
+  }
   /// Checks if the pointer is live.
-  bool isLive() const { return Pointee && !Pointee->IsDead; }
+  bool isLive() const {
+    if (isIntegralPointer())
+      return true;
+    return asBlockPointer().Pointee && !asBlockPointer().Pointee->IsDead;
+  }
   /// Checks if the item is a field in an object.
   bool isField() const {
+    if (isIntegralPointer())
+      return false;
+
+    unsigned Base = asBlockPointer().Base;
     return Base != 0 && Base != sizeof(InlineDescriptor) &&
            Base != RootPtrMark && getFieldDesc()->asDecl();
   }
 
   /// Accessor for information about the declaration site.
   const Descriptor *getDeclDesc() const {
-    assert(Pointee);
-    return Pointee->Desc;
+    assert(isBlockPointer());
+    assert(asBlockPointer().Pointee);
+    return asBlockPointer().Pointee->Desc;
   }
   SourceLocation getDeclLoc() const { return getDeclDesc()->getLocation(); }
 
+  /// Returns the expression or declaration the pointer has been created for.
+  DeclTy getSource() const {
+    if (isBlockPointer())
+      return getDeclDesc()->getSource();
+
+    assert(isIntegralPointer());
+    return asIntPointer().Desc ? asIntPointer().Desc->getSource() : DeclTy();
+  }
+
   /// Returns a pointer to the object of which this pointer is a field.
   [[nodiscard]] Pointer getBase() const {
-    if (Base == RootPtrMark) {
+    if (asBlockPointer().Base == RootPtrMark) {
       assert(Offset == PastEndMark && "cannot get base of a block");
-      return Pointer(Pointee, Base, 0);
+      return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0);
     }
-    unsigned NewBase = Base - getInlineDesc()->Offset;
-    return Pointer(Pointee, NewBase, NewBase);
+    unsigned NewBase = asBlockPointer().Base - getInlineDesc()->Offset;
+    return Pointer(asBlockPointer().Pointee, NewBase, NewBase);
   }
   /// Returns the parent array.
   [[nodiscard]] Pointer getArray() const {
-    if (Base == RootPtrMark) {
+    if (asBlockPointer().Base == RootPtrMark) {
       assert(Offset != 0 && Offset != PastEndMark && "not an array element");
-      return Pointer(Pointee, Base, 0);
+      return Pointer(asBlockPointer().Pointee, asBlockPointer().Base, 0);
     }
-    assert(Offset != Base && "not an array element");
-    return Pointer(Pointee, Base, Base);
+    assert(Offset != asBlockPointer().Base && "not an array element");
+    return Pointer(asBlockPointer().Pointee, asBlockPointer().Base,
+                   asBlockPointer().Base);
   }
 
   /// Accessors for information about the innermost field.
   const Descriptor *getFieldDesc() const {
-    if (Base == 0 || Base == sizeof(InlineDescriptor) || Base == RootPtrMark)
+    if (isIntegralPointer())
+      return asIntPointer().Desc;
+    if (isBlockPointer() &&
+        (asBlockPointer().Base == 0 ||
+         asBlockPointer().Base == sizeof(InlineDescriptor) ||
+         asBlockPointer().Base == RootPtrMark))
       return getDeclDesc();
     return getInlineDesc()->Desc;
   }
 
   /// Returns the type of the innermost field.
   QualType getType() const {
-    if (inPrimitiveArray() && Offset != Base) {
+    if (inPrimitiveArray() && Offset != asBlockPointer().Base) {
       // Unfortunately, complex types are not array types in clang, but they are
       // for us.
       if (const auto *AT = getFieldDesc()->getType()->getAsArrayTypeUnsafe())
@@ -248,53 +328,98 @@ class Pointer {
     return getFieldDesc()->getType();
   }
 
-  [[nodiscard]] Pointer getDeclPtr() const { return Pointer(Pointee); }
+  [[nodiscard]] Pointer getDeclPtr() const {
+    return Pointer(asBlockPointer().Pointee);
+  }
 
   /// Returns the element size of the innermost field.
   size_t elemSize() const {
-    if (Base == RootPtrMark)
+    if (isIntegralPointer()) {
+      if (!asIntPointer().Desc)
+        return 1;
+      return asIntPointer().Desc->getElemSize();
+    }
+
+    if (asBlockPointer().Base == RootPtrMark)
       return getDeclDesc()->getSize();
     return getFieldDesc()->getElemSize();
   }
   /// Returns the total size of the innermost field.
-  size_t getSize() const { return getFieldDesc()->getSize(); }
+  size_t getSize() const {
+    assert(isBlockPointer());
+    return getFieldDesc()->getSize();
+  }
 
   /// Returns the offset into an array.
   unsigned getOffset() const {
     assert(Offset != PastEndMark && "invalid offset");
-    if (Base == RootPtrMark)
+    if (asBlockPointer().Base == RootPtrMark)
       return Offset;
 
     unsigned Adjust = 0;
-    if (Offset != Base) {
+    if (Offset != asBlockPointer().Base) {
       if (getFieldDesc()->ElemDesc)
         Adjust = sizeof(InlineDescriptor);
       else
         Adjust = sizeof(InitMapPtr);
     }
-    return Offset - Base - Adjust;
+    return Offset - asBlockPointer().Base - Adjust;
   }
 
   /// Whether this array refers to an array, but not
   /// to the first element.
-  bool isArrayRoot() const { return inArray() && Offset == Base; }
+  bool isArrayRoot() const {
+    return inArray() && Offset == asBlockPointer().Base;
+  }
 
   /// Checks if the innermost field is an array.
-  bool inArray() const { return getFieldDesc()->IsArray; }
+  bool inArray() const {
+    if (isBlockPointer())
+      return getFieldDesc()->IsArray;
+    return false;
+  }
   /// Checks if the structure is a primitive array.
-  bool inPrimitiveArray() const { return getFieldDesc()->isPrimitiveArray(); }
+  bool inPrimitiveArray() const {
+    if (isBlockPointer())
+      return getFieldDesc()->isPrimitiveArray();
+    return false;
+  }
   /// Checks if the structure is an array of unknown size.
   bool isUnknownSizeArray() const {
+    if (!isBlockPointer())
+      return false;
     return getFieldDesc()->isUnknownSizeArray();
   }
   /// Checks if the pointer points to an array.
-  bool isArrayElement() const { return inArray() && Base != Offset; }
+  bool isArrayElement() const {
+    if (isBlockPointer())
+      return inArray() && asBlockPointer().Base != Offset;
+    return false;
+  }
   /// Pointer points directly to a block.
   bool isRoot() const {
-    return (Base == 0 || Base == RootPtrMark) && Offset == 0;
+    return (asBlockPointer().Base == 0 ||
+            asBlockPointer().Base == RootPtrMark) &&
+           Offset == 0;
   }
   /// If this pointer has an InlineDescriptor we can use to initialize.
-  bool canBeInitialized() const { return Pointee && Base > 0; }
+  bool canBeInitialized() const {
+    if (!isBlockPointer())
+      return false;
+
+    return asBlockPointer().Pointee && asBlockPointer().Base > 0;
+  }
+
+  [[nodiscard]] const BlockPointer &asBlockPointer() const {
+    assert(isBlockPointer());
+    return PointeeStorage.BS;
+  }
+  [[nodiscard]] const IntPointer &asIntPointer() const {
+    assert(isIntegralPointer());
+    return PointeeStorage.Int;
+  }
+  bool isBlockPointer() const { return StorageKind == Storage::Block; }
+  bool isIntegralPointer() const { return StorageKind == Storage::Int; }
 
   /// Returns the record descriptor of a class.
   const Record *getRecord() const { return getFieldDesc()->ElemRecord; }
@@ -310,71 +435,109 @@ class Pointer {
   bool isUnion() const;
 
   /// Checks if the storage is extern.
-  bool isExtern() const { return Pointee && Pointee->isExtern(); }
+  bool isExtern() const {
+    if (isBlockPointer())
+      return asBlockPointer().Pointee && asBlockPointer().Pointee->isExtern();
+    return false;
+  }
   /// Checks if the storage is static.
   bool isStatic() const {
-    assert(Pointee);
-    return Pointee->isStatic();
+    if (isIntegralPointer())
+      return true;
+    assert(asBlockPointer().Pointee);
+    return asBlockPointer().Pointee->isStatic();
   }
   /// Checks if the storage is temporary.
   bool isTemporary() const {
-    assert(Pointee);
-    return Pointee->isTemporary();
+    if (isBlockPointer()) {
+      assert(asBlockPointer().Pointee);
+      return asBlockPointer().Pointee->isTemporary();
+    }
+    return false;
   }
   /// Checks if the storage is a static temporary.
   bool isStaticTemporary() const { return isStatic() && isTemporary(); }
 
   /// Checks if the field is mutable.
   bool isMutable() const {
-    return Base != 0 && Base != sizeof(InlineDescriptor) &&
+    if (!isBlockPointer())
+      return false;
+    return asBlockPointer().Base != 0 &&
+           asBlockPointer().Base != sizeof(InlineDescriptor) &&
            getInlineDesc()->IsFieldMutable;
   }
   /// Checks if an object was initialized.
   bool isInitialized() const;
   /// Checks if the object is active.
   bool isActive() const {
-    return Base == 0 || Base == sizeof(InlineDescriptor) ||
+    if (!isBlockPointer())
+      return true;
+    return asBlockPointer().Base == 0 ||
+           asBlockPointer().Base == sizeof(InlineDescriptor) ||
            getInlineDesc()->IsActive;
   }
   /// Checks if a structure is a base class.
   bool isBaseClass() const { return isField() && getInlineDesc()->IsBase; }
   /// Checks if the pointer points to a dummy value.
   bool isDummy() const {
-    if (!Pointee)
+    if (!isBlockPointer())
+      return false;
+
+    if (!asBlockPointer().Pointee)
       return false;
+
     return getDeclDesc()->isDummy();
   }
 
   /// Checks if an object or a subfield is mutable.
   bool isConst() const {
-    return (Base == 0 || Base == sizeof(InlineDescriptor))
+    if (isIntegralPointer())
+      return true;
+    return (asBlockPointer().Base == 0 ||
+            asBlockPointer().Base == sizeof(InlineDescriptor))
                ? getDeclDesc()->IsConst
                : getInlineDesc()->IsConst;
   }
 
   /// Returns the declaration ID.
   std::optional<unsigned> getDeclID() const {
-    assert(Pointee);
-    return Pointee->getDeclID();
+    if (isBlockPointer()) {
+      assert(asBlockPointer().Pointee);
+      return asBlockPointer().Pointee->getDeclID();
+    }
+    return std::nullopt;
   }
 
   /// Returns the byte offset from the start.
   unsigned getByteOffset() const {
+    if (isIntegralPointer())
+      return asIntPointer().Value + Offset;
     return Offset;
   }
 
   /// Returns the number of elements.
-  unsigned getNumElems() const { return getSize() / elemSize(); }
+  unsigned getNumElems() const {
+    if (isIntegralPointer())
+      return ~unsigned(0);
+    return getSize() / elemSize();
+  }
 
-  const Block *block() const { return Pointee; }
+  const Block *block() const { return asBlockPointer().Pointee; }
 
   /// Returns the index into an array.
   int64_t getIndex() const {
+    if (!isBlockPointer())
+      return 0;
+
+    if (isZero())
+      return 0;
+
     if (isElementPastEnd())
       return 1;
 
     // narrow()ed element in a composite array.
-    if (Base > sizeof(InlineDescriptor) && Base == Offset)
+    if (asBlockPointer().Base > sizeof(InlineDescriptor) &&
+        asBlockPointer().Base == Offset)
       return 0;
 
     if (auto ElemSize = elemSize())
@@ -384,7 +547,10 @@ class Pointer {
 
   /// Checks if the index is one past end.
   bool isOnePastEnd() const {
-    if (!Pointee)
+    if (isIntegralPointer())
+      return false;
+
+    if (!asBlockPointer().Pointee)
       return false;
     return isElementPastEnd() || getSize() == getOffset();
   }
@@ -395,20 +561,25 @@ class Pointer {
   /// Dereferences the pointer, if it's live.
   template <typename T> T &deref() const {
     assert(isLive() && "Invalid pointer");
-    assert(Pointee);
+    assert(isBlockPointer());
+    assert(asBlockPointer().Pointee);
+    assert(Offset + sizeof(T) <=
+           asBlockPointer().Pointee->getDescriptor()->getAllocSize());
+
     if (isArrayRoot())
-      return *reinterpret_cast<T *>(Pointee->rawData() + Base +
-                                    sizeof(InitMapPtr));
+      return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() +
+                                    asBlockPointer().Base + sizeof(InitMapPtr));
 
-    assert(Offset + sizeof(T) <= Pointee->getDescriptor()->getAllocSize());
-    return *reinterpret_cast<T *>(Pointee->rawData() + Offset);
+    return *reinterpret_cast<T *>(asBlockPointer().Pointee->rawData() + Offset);
   }
 
   /// Dereferences a primitive element.
   template <typename T> T &elem(unsigned I) const {
     assert(I < getNumElems());
-    assert(Pointee);
-    return reinterpret_cast<T *>(Pointee->data() + sizeof(InitMapPtr))[I];
+    assert(isBlockPointer());
+    assert(asBlockPointer().Pointee);
+    return reinterpret_cast<T *>(asBlockPointer().Pointee->data() +
+                                 sizeof(InitMapPtr))[I];
   }
 
   /// Initializes a field.
@@ -437,24 +608,7 @@ class Pointer {
   static bool hasSameArray(const Pointer &A, const Pointer &B);
 
   /// Prints the pointer.
-  void print(llvm::raw_ostream &OS) const {
-    OS << Pointee << " {";
-    if (Base == RootPtrMark)
-      OS << "rootptr, ";
-    else
-      OS << Base << ", ";
-
-    if (Offset == PastEndMark)
-      OS << "pastend, ";
-    else
-      OS << Offset << ", ";
-
-    if (Pointee)
-      OS << Pointee->getSize();
-    else
-      OS << "nullptr";
-    OS << "}";
-  }
+  void print(llvm::raw_ostream &OS) const;
 
 private:
   friend class Block;
@@ -464,33 +618,41 @@ class Pointer {
   Pointer(Block *Pointee, unsigned Base, unsigned Offset);
 
   /// Returns the embedded descriptor preceding a field.
-  InlineDescriptor *getInlineDesc() const { return getDescriptor(Base); }
+  InlineDescriptor *getInlineDesc() const {
+    return getDescriptor(asBlockPointer().Base);
+  }
 
   /// Returns a descriptor at a given offset.
   InlineDescriptor *getDescriptor(unsigned Offset) const {
     assert(Offset != 0 && "Not a nested pointer");
-    assert(Pointee);
-    return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) -
+    assert(isBlockPointer());
+    assert(!isZero());
+    return reinterpret_cast<InlineDescriptor *>(
+               asBlockPointer().Pointee->rawData() + Offset) -
            1;
   }
 
   /// Returns a reference to the InitMapPtr which stores the initialization map.
   InitMapPtr &getInitMap() const {
-    assert(Pointee);
-    return *reinterpret_cast<InitMapPtr *>(Pointee->rawData() + Base);
+    assert(isBlockPointer());
+    assert(!isZero());
+    return *reinterpret_cast<InitMapPtr *>(asBlockPointer().Pointee->rawData() +
+                                           asBlockPointer().Base);
   }
 
-  /// The block the pointer is pointing to.
-  Block *Pointee = nullptr;
-  /// Start of the current subfield.
-  unsigned Base = 0;
-  /// Offset into the block.
+  /// Offset into the storage.
   unsigned Offset = 0;
 
   /// Previous link in the pointer chain.
   Pointer *Prev = nullptr;
   /// Next link in the pointer chain.
   Pointer *Next = nullptr;
+
+  union {
+    BlockPointer BS;
+    IntPointer Int;
+  } PointeeStorage;
+  Storage StorageKind = Storage::Int;
 };
 
 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Pointer &P) {
diff --git a/clang/lib/AST/Interp/PrimType.h b/clang/lib/AST/Interp/PrimType.h
index 24a24a71a07b57..dd2055d951660e 100644
--- a/clang/lib/AST/Interp/PrimType.h
+++ b/clang/lib/AST/Interp/PrimType.h
@@ -46,6 +46,10 @@ enum PrimType : unsigned {
   PT_FnPtr,
 };
 
+inline constexpr bool isPtrType(PrimType T) {
+  return T == PT_Ptr || T == PT_FnPtr;
+}
+
 enum class CastKind : uint8_t {
   Reinterpret,
   Atomic,
diff --git a/clang/test/AST/Interp/c.c b/clang/test/AST/Interp/c.c
index 8de6139efbea09..531e02aafbbbb6 100644
--- a/clang/test/AST/Interp/c.c
+++ b/clang/test/AST/Interp/c.c
@@ -201,3 +201,21 @@ void localCompoundLiteral(void) {
        // pedantic-ref-warning {{use of an empty initializer}}
   };
 }
+
+_Static_assert(((void*)0 + 1) != (void*)0, ""); // pedantic-expected-warning {{arithmetic on a pointer to void is a GNU extension}} \
+                                                // pedantic-expected-warning {{not an integer constant expression}} \
+                                                // pedantic-expected-note {{cannot perform pointer arithmetic on null pointer}} \
+                                                // pedantic-ref-warning {{arithmetic on a pointer to void is a GNU extension}} \
+                                                // pedantic-ref-warning {{not an integer constant expression}} \
+                                                // pedantic-ref-note {{cannot perform pointer arithmetic on null pointer}}
+
+typedef __INTPTR_TYPE__ intptr_t;
+int array[(intptr_t)(int*)1]; // ref-warning {{variable length array folded to constant array}} \
+                              // pedantic-ref-warning {{variable length array folded to constant array}} \
+                              // expected-warning {{variable length array folded to constant array}} \
+                              // pedantic-expected-warning {{variable length array folded to constant array}}
+
+int castViaInt[*(int*)(unsigned long)"test"]; // ref-error {{variable length array}} \
+                                              // pedantic-ref-error {{variable length array}} \
+                                              // expected-error {{variable length array}} \
+                                              // pedantic-expected-error {{variable length array}}
diff --git a/clang/test/AST/Interp/const-eval.c b/clang/test/AST/Interp/const-eval.c
new file mode 100644
index 00000000000000..d3096611a47fa7
--- /dev/null
+++ b/clang/test/AST/Interp/const-eval.c
@@ -0,0 +1,194 @@
+// RUN: %clang_cc1 -fsyntax-only -verify=both,ref      -triple x86_64-linux %s -Wno-tautological-pointer-compare -Wno-pointer-to-int-cast
+// RUN: %clang_cc1 -fsyntax-only -verify=both,expected -triple x86_64-linux %s -Wno-tautological-pointer-compare -Wno-pointer-to-int-cast -fexperimental-new-constant-interpreter -DNEW_INTERP
+// RUN: %clang_cc1 -fsyntax-only -verify=both,ref      -triple powerpc64-ibm-aix-xcoff %s -Wno-tautological-pointer-compare -Wno-pointer-to-int-cast
+// RUN: %clang_cc1 -fsyntax-only -verify=both,expected -triple powerpc64-ibm-aix-xcoff %s -Wno-tautological-pointer-compare -Wno-pointer-to-int-cast -fexperimental-new-constant-interpreter -DNEW_INTERP
+
+/// This is a version of test/Sema/const-eval.c with the
+/// tests commented out that the new constant expression interpreter does
+/// not support yet. They are all marked with the NEW_INTERP define:
+///
+///   - builtin_constant_p
+///   - unions
+
+
+#define EVAL_EXPR(testno, expr) enum { test##testno = (expr) }; struct check_positive##testno { int a[test##testno]; };
+int x;
+EVAL_EXPR(1, (_Bool)&x)
+EVAL_EXPR(2, (int)(1.0+(double)4))
+EVAL_EXPR(3, (int)(1.0+(float)4.0))
+EVAL_EXPR(4, (_Bool)(1 ? (void*)&x : 0))
+EVAL_EXPR(5, (_Bool)(int[]){0})
+struct y {int x,y;};
+EVAL_EXPR(6, (int)(1+(struct y*)0))
+_Static_assert((long)&((struct y*)0)->y > 0, "");
+EVAL_EXPR(7, (int)&((struct y*)0)->y)
+EVAL_EXPR(8, (_Bool)"asdf")
+EVAL_EXPR(9, !!&x)
+EVAL_EXPR(10, ((void)1, 12))
+void g0(void);
+EVAL_EXPR(11, (g0(), 12)) // both-error {{not an integer constant expression}}
+EVAL_EXPR(12, 1.0&&2.0)
+EVAL_EXPR(13, x || 3.0) // both-error {{not an integer constant expression}}
+
+unsigned int l_19 = 1;
+EVAL_EXPR(14, (1 ^ l_19) && 1); // both-error {{not an integer constant expression}}
+
+void f(void)
+{
+  int a;
+  EVAL_EXPR(15, (_Bool)&a);
+}
+
+_Complex float g16 = (1.0f + 1.0fi);
+
+// ?: in constant expressions.
+int g17[(3?:1) - 2];
+
+EVAL_EXPR(18, ((int)((void*)10 + 10)) == 20 ? 1 : -1);
+
+struct s {
+  int a[(int)-1.0f]; // both-error {{array size is negative}}
+};
+
+EVAL_EXPR(19, ((int)&*(char*)10 == 10 ? 1 : -1));
+
+#ifndef NEW_INTERP
+EVAL_EXPR(20, __builtin_constant_p(*((int*) 10)));
+#endif
+
+EVAL_EXPR(21, (__imag__ 2i) == 2 ? 1 : -1);
+
+EVAL_EXPR(22, (__real__ (2i+3)) == 3 ? 1 : -1);
+
+int g23[(int)(1.0 / 1.0)] = { 1 }; // both-warning {{folded to constant array}}
+int g24[(int)(1.0 / 1.0)] = { 1 , 2 }; // both-warning {{folded to constant array}} \
+                                       // both-warning {{excess elements in array initializer}}
+int g25[(int)(1.0 + 1.0)], g26 = sizeof(g25); // both-warning {{folded to constant array}}
+
+EVAL_EXPR(26, (_Complex double)0 ? -1 : 1)
+EVAL_EXPR(27, (_Complex int)0 ? -1 : 1)
+EVAL_EXPR(28, (_Complex double)1 ? 1 : -1)
+EVAL_EXPR(29, (_Complex int)1 ? 1 : -1)
+
+// PR4027
+struct a { int x, y; };
+static struct a V2 = (struct a)(struct a){ 1, 2};
+static const struct a V1 = (struct a){ 1, 2};
+
+EVAL_EXPR(30, (int)(_Complex float)((1<<30)-1) == (1<<30) ? 1 : -1)
+EVAL_EXPR(31, (int*)0 == (int*)0 ? 1 : -1)
+EVAL_EXPR(32, (int*)0 != (int*)0 ? -1 : 1)
+EVAL_EXPR(33, (void*)0 - (void*)0 == 0 ? 1 : -1)
+
+void foo(void) {}
+EVAL_EXPR(34, (foo == (void *)0) ? -1 : 1)
+
+// No PR. Mismatched bitwidths lead to a crash on second evaluation.
+const _Bool constbool = 0;
+EVAL_EXPR(35, constbool)
+EVAL_EXPR(36, constbool)
+
+EVAL_EXPR(37, ((void)1,2.0) == 2.0 ? 1 : -1)
+EVAL_EXPR(38, __builtin_expect(1,1) == 1 ? 1 : -1)
+
+// PR7884
+EVAL_EXPR(39, __real__(1.f) == 1 ? 1 : -1)
+EVAL_EXPR(40, __imag__(1.f) == 0 ? 1 : -1)
+
+// From gcc testsuite
+EVAL_EXPR(41, (int)(1+(_Complex unsigned)2))
+
+void rdar8875946(void) {
+  double _Complex  P;
+  float _Complex  P2 = 3.3f + P;
+}
+
+double d = (d = 0.0); // both-error {{not a compile-time constant}}
+double d2 = ++d; // both-error {{not a compile-time constant}}
+
+int n = 2;
+int intLvalue[*(int*)((long)&n ?: 1)] = { 1, 2 }; // both-error {{variable length array}}
+
+union u { int a; char b[4]; };
+char c = ((union u)(123456)).b[0]; // both-error {{not a compile-time constant}}
+
+#ifndef NEW_INTERP
+extern const int weak_int __attribute__((weak));
+const int weak_int = 42;
+int weak_int_test = weak_int; // both-error {{not a compile-time constant}}
+#endif
+
+int literalVsNull1 = "foo" == 0;
+int literalVsNull2 = 0 == "foo";
+
+// PR11385.
+int castViaInt[*(int*)(unsigned long)"test"]; // both-error {{variable length array}}
+
+// PR11391.
+#ifndef NEW_INTERP
+struct PR11391 { _Complex float f; } pr11391;
+EVAL_EXPR(42, __builtin_constant_p(pr11391.f = 1))
+#endif
+
+// PR12043
+float varfloat;
+const float constfloat = 0;
+EVAL_EXPR(43, varfloat && constfloat) // both-error {{not an integer constant expression}}
+EVAL_EXPR(45, ((char*)-1) + 1 == 0 ? 1 : -1)
+EVAL_EXPR(46, ((char*)-1) + 1 < (char*) -1 ? 1 : -1)
+EVAL_EXPR(47, &x < &x + 1 ? 1 : -1)
+EVAL_EXPR(48, &x != &x - 1 ? 1 : -1)
+EVAL_EXPR(49, &x < &x - 100 ? 1 : -1) // ref-error {{not an integer constant expression}}
+
+/// FIXME: Rejecting this is correct, BUT when converting the innermost pointer
+/// to an integer, we do not preserve the information where it came from. So when we later
+/// create a pointer from it, it also doesn't have that information, which means
+/// hasSameBase() for those two pointers will return false. And in those cases, we emit
+/// the diagnostic:
+///   comparison between '&Test50' and '&(631578)' has unspecified value
+extern struct Test50S Test50;
+EVAL_EXPR(50, &Test50 < (struct Test50S*)((unsigned long)&Test50 + 10)) // both-error {{not an integer constant expression}} \
+                                                                        // expected-note {{comparison between}}
+
+EVAL_EXPR(51, 0 != (float)1e99)
+
+// PR21945
+void PR21945(void) { int i = (({}), 0l); }
+
+#ifndef NEW_INTERP
+void PR24622(void);
+struct PR24622 {} pr24622;
+EVAL_EXPR(52, &pr24622 == (void *)&PR24622); // ref-error {{not an integer constant expression}}
+                                             // ref-note at -1 {{past the end}}
+#endif
+
+// We evaluate these by providing 2s' complement semantics in constant
+// expressions, like we do for integers.
+void *PR28739a = (__int128)(unsigned long)-1 + &PR28739a;                  // both-warning {{the pointer incremented by 18446744073709551615 refers past the last possible element for an array in 64-bit address space containing 64-bit (8-byte) elements (max possible 2305843009213693952 elements)}}
+
+void *PR28739b = &PR28739b + (__int128)(unsigned long)-1;                  // both-warning {{refers past the last possible element}}
+__int128 PR28739c = (&PR28739c + (__int128)(unsigned long)-1) - &PR28739c; // both-warning {{refers past the last possible element}}
+void *PR28739d = &(&PR28739d)[(__int128)(unsigned long)-1];                // both-warning {{refers past the last possible element}}
+
+struct PR35214_X {
+  int k;
+  int arr[];
+};
+int PR35214_x;
+int PR35214_y = ((struct PR35214_X *)&PR35214_x)->arr[1]; // both-error {{not a compile-time constant}}
+int *PR35214_z = &((struct PR35214_X *)&PR35214_x)->arr[1]; // ok, &PR35214_x + 2
+
+
+/// From const-eval-64.c
+EVAL_EXPR(53, ((char*)-1LL) + 1 == 0 ? 1 : -1)
+EVAL_EXPR(54, ((char*)-1LL) + 1 < (char*) -1 ? 1 : -1)
+
+/// === Additions ===
+#if __SIZEOF_INT__ == 4
+typedef __INTPTR_TYPE__ intptr_t;
+const intptr_t A = (intptr_t)(((int*) 0) + 1);
+const intptr_t B = (intptr_t)(((char*)0) + 3);
+_Static_assert(A > B, "");
+#else
+#error :(
+#endif



More information about the cfe-commits mailing list