https://github.com/tbaederr updated 
https://github.com/llvm/llvm-project/pull/185028

>From e82cd39f93553b17d34121f033b66ae8696ff6f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <[email protected]>
Date: Mon, 17 Nov 2025 13:58:23 +0100
Subject: [PATCH] [clang][bytecode] Support different integral types (e.g.
 addresses)

---
 clang/lib/AST/ByteCode/Boolean.h              |   3 +-
 clang/lib/AST/ByteCode/Char.h                 | 225 +++++++++++++++
 clang/lib/AST/ByteCode/Context.cpp            |   3 +-
 clang/lib/AST/ByteCode/Descriptor.cpp         |   8 +
 clang/lib/AST/ByteCode/Descriptor.h           |   5 +
 clang/lib/AST/ByteCode/Disasm.cpp             |   1 +
 clang/lib/AST/ByteCode/Integral.h             | 188 +++++++++----
 clang/lib/AST/ByteCode/IntegralAP.h           |   1 +
 clang/lib/AST/ByteCode/Interp.cpp             |  34 ++-
 clang/lib/AST/ByteCode/Interp.h               | 262 +++++++++++++++++-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      |  61 ++--
 .../lib/AST/ByteCode/InterpBuiltinBitCast.cpp |  10 +-
 clang/lib/AST/ByteCode/InterpFrame.cpp        |   1 +
 clang/lib/AST/ByteCode/InterpStack.cpp        |   1 +
 clang/lib/AST/ByteCode/InterpStack.h          |  17 +-
 clang/lib/AST/ByteCode/Pointer.cpp            |  10 +
 clang/lib/AST/ByteCode/Pointer.h              |   4 +-
 clang/lib/AST/ByteCode/PrimType.cpp           |   1 +
 clang/lib/AST/ByteCode/PrimType.h             |  30 +-
 clang/lib/AST/ByteCode/Primitives.h           |  40 +++
 clang/lib/AST/ByteCode/Program.cpp            |   1 +
 clang/test/AST/ByteCode/addr-label-diff.c     |  19 ++
 clang/test/AST/ByteCode/addr-label-diff.cpp   |  16 ++
 clang/test/AST/ByteCode/builtin-bit-cast.cpp  |   9 +
 clang/test/AST/ByteCode/const-eval.c          |   9 +-
 clang/test/AST/ByteCode/cxx11.cpp             |  12 +
 clang/test/AST/ByteCode/int-as-ptr-arith.c    |  25 ++
 clang/test/CodeGen/const-init.c               |   1 +
 clang/test/CodeGen/const-label-addr.c         |   1 +
 clang/test/CodeGen/statements.c               |   1 +
 clang/test/CodeGen/staticinit.c               |   1 +
 .../CodeGenCXX/2008-05-07-CrazyOffsetOf.cpp   |   1 +
 clang/test/CodeGenCXX/const-init-cxx11.cpp    |   3 +
 clang/test/CodeGenCXX/const-init.cpp          |   6 +
 clang/test/Sema/array-init.c                  |   2 +
 clang/test/Sema/compound-literal.c            |   1 +
 clang/test/Sema/const-ptr-int-ptr-cast.c      |   1 +
 clang/test/Sema/init.c                        |   1 +
 clang/test/SemaCXX/constexpr-string.cpp       |   1 +
 clang/unittests/AST/ByteCode/Descriptor.cpp   |   5 +-
 40 files changed, 890 insertions(+), 131 deletions(-)
 create mode 100644 clang/lib/AST/ByteCode/Char.h
 create mode 100644 clang/test/AST/ByteCode/addr-label-diff.c
 create mode 100644 clang/test/AST/ByteCode/addr-label-diff.cpp
 create mode 100644 clang/test/AST/ByteCode/int-as-ptr-arith.c

diff --git a/clang/lib/AST/ByteCode/Boolean.h b/clang/lib/AST/ByteCode/Boolean.h
index fd8d546656881..09eefee14a854 100644
--- a/clang/lib/AST/ByteCode/Boolean.h
+++ b/clang/lib/AST/ByteCode/Boolean.h
@@ -61,11 +61,10 @@ class Boolean final {
   bool isMin() const { return isZero(); }
 
   constexpr static bool isMinusOne() { return false; }
-
   constexpr static bool isSigned() { return false; }
-
   constexpr static bool isNegative() { return false; }
   constexpr static bool isPositive() { return !isNegative(); }
+  constexpr static bool isNumber() { return true; }
 
   ComparisonCategoryResult compare(const Boolean &RHS) const {
     return Compare(V, RHS.V);
diff --git a/clang/lib/AST/ByteCode/Char.h b/clang/lib/AST/ByteCode/Char.h
new file mode 100644
index 0000000000000..79219f2a3adb6
--- /dev/null
+++ b/clang/lib/AST/ByteCode/Char.h
@@ -0,0 +1,225 @@
+//===------- Char.h - Wrapper for numeric types for the VM ------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_INTERP_CHAR_H
+#define LLVM_CLANG_AST_INTERP_CHAR_H
+
+#include "Integral.h"
+#include <limits>
+
+namespace clang {
+namespace interp {
+
+template <unsigned N, bool Signed> class Integral;
+
+template <bool Signed> struct CharRepr;
+template <> struct CharRepr<false> {
+  using Type = uint8_t;
+};
+template <> struct CharRepr<true> {
+  using Type = int8_t;
+};
+
+template <bool Signed> class Char final {
+private:
+  template <bool OtherSigned> friend class Char;
+  using ReprT = typename CharRepr<Signed>::Type;
+  ReprT V = 0;
+  static_assert(std::is_trivially_copyable_v<ReprT>);
+
+public:
+  using AsUnsigned = Char<false>;
+
+  constexpr Char() = default;
+  constexpr Char(ReprT V) : V(V) {}
+  // constexpr Char(const Char &C) : V(C.V) {}
+  explicit Char(const APSInt &V)
+      : V(V.isSigned() ? V.getSExtValue() : V.getZExtValue()) {}
+
+  template <typename T> static Char from(T t) {
+    return Char(static_cast<ReprT>(t));
+  }
+  template <typename T> static Char from(T t, unsigned BitWidth) {
+    return Char(static_cast<ReprT>(t));
+  }
+
+  static bool isSigned() { return Signed; }
+  static unsigned bitWidth() { return 8; }
+  static bool isNumber() { return true; }
+  static Char zero(unsigned BitWidth = 8) { return Char(0); }
+
+  constexpr bool isMin() const {
+    return V == std::numeric_limits<ReprT>::min();
+  }
+  constexpr bool isNegative() const { return Signed && V < 0; }
+  constexpr bool isPositive() const { return !isNegative(); }
+  constexpr bool isZero() const { return V == 0; }
+  constexpr bool isMinusOne() const { return Signed && V == -1; }
+
+  template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>>
+  explicit operator Ty() const {
+    return V;
+  }
+
+  bool operator<(Char RHS) const { return V < RHS.V; }
+  bool operator>(Char RHS) const { return V > RHS.V; }
+  bool operator<=(Char RHS) const { return V <= RHS.V; }
+  bool operator>=(Char RHS) const { return V >= RHS.V; }
+  bool operator==(Char RHS) const { return V == RHS.V; }
+  bool operator!=(Char RHS) const { return V != RHS.V; }
+  bool operator>=(unsigned RHS) const {
+    return static_cast<unsigned>(V) >= RHS;
+  }
+
+  bool operator>(unsigned RHS) const {
+    return V >= 0 && static_cast<unsigned>(V) > RHS;
+  }
+
+  Char operator-() const { return Char(-V); }
+  Char operator-(Char Other) const { return Char(V - Other.V); }
+
+  ComparisonCategoryResult compare(Char RHS) const { return Compare(V, RHS.V); 
}
+
+  void bitcastToMemory(std::byte *Dest) const {
+    std::memcpy(Dest, &V, sizeof(V));
+  }
+
+  static Char bitcastFromMemory(const std::byte *Src, unsigned BitWidth) {
+    assert(BitWidth == 8);
+    ReprT V;
+
+    std::memcpy(&V, Src, sizeof(ReprT));
+    return Char(V);
+  }
+
+  APSInt toAPSInt() const {
+    return APSInt(APInt(8, static_cast<uint64_t>(V), Signed), !Signed);
+  }
+  APSInt toAPSInt(unsigned BitWidth) const {
+    return APSInt(toAPInt(BitWidth), !Signed);
+  }
+  APInt toAPInt(unsigned BitWidth) const {
+    if constexpr (Signed)
+      return APInt(8, static_cast<uint64_t>(V), Signed).sextOrTrunc(BitWidth);
+    else
+      return APInt(8, static_cast<uint64_t>(V), Signed).zextOrTrunc(BitWidth);
+  }
+  APValue toAPValue(const ASTContext &) const { return APValue(toAPSInt()); }
+  std::string toDiagnosticString(const ASTContext &Ctx) const {
+    std::string NameStr;
+    llvm::raw_string_ostream OS(NameStr);
+    OS << V;
+    return NameStr;
+  }
+  Char<false> toUnsigned() const { return Char<false>(V); }
+
+  Char truncate(unsigned TruncBits) const {
+    assert(TruncBits >= 1);
+    if (TruncBits >= 8)
+      return *this;
+    const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1;
+    const ReprT SignBit = ReprT(1) << (TruncBits - 1);
+    const ReprT ExtMask = ~BitMask;
+    return Char((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0));
+  }
+
+  unsigned countLeadingZeros() const {
+    if constexpr (!Signed)
+      return llvm::countl_zero<ReprT>(V);
+    if (isPositive())
+      return llvm::countl_zero<typename AsUnsigned::ReprT>(
+          static_cast<typename AsUnsigned::ReprT>(V));
+    llvm_unreachable("Don't call countLeadingZeros() on negative values.");
+  }
+
+  static bool increment(Char A, Char *R) {
+    return add(A, Char(ReprT(1)), A.bitWidth(), R);
+  }
+
+  static bool decrement(Char A, Char *R) {
+    return sub(A, Char(ReprT(1)), A.bitWidth(), R);
+  }
+
+  static bool add(Char A, Char B, unsigned OpBits, Char *R) {
+    return CheckAddUB(A.V, B.V, R->V);
+  }
+
+  static bool sub(Char A, Char B, unsigned OpBits, Char *R) {
+    return CheckSubUB(A.V, B.V, R->V);
+  }
+
+  static bool mul(Char A, Char B, unsigned OpBits, Char *R) {
+    return CheckMulUB(A.V, B.V, R->V);
+  }
+
+  static bool rem(Char A, Char B, unsigned OpBits, Char *R) {
+    *R = Char(A.V % B.V);
+    return false;
+  }
+
+  static bool div(Char A, Char B, unsigned OpBits, Char *R) {
+    *R = Char(A.V / B.V);
+    return false;
+  }
+
+  static bool bitAnd(Char A, Char B, unsigned OpBits, Char *R) {
+    *R = Char(A.V & B.V);
+    return false;
+  }
+
+  static bool bitOr(Char A, Char B, unsigned OpBits, Char *R) {
+    *R = Char(A.V | B.V);
+    return false;
+  }
+
+  static bool bitXor(Char A, Char B, unsigned OpBits, Char *R) {
+    *R = Char(A.V ^ B.V);
+    return false;
+  }
+
+  static bool neg(Char A, Char *R) {
+    if (Signed && A.isMin())
+      return true;
+
+    *R = Char(-A.V);
+    return false;
+  }
+
+  static bool comp(Char A, Char *R) {
+    *R = Char(~A.V);
+    return false;
+  }
+
+  template <bool RHSSign>
+  static void shiftLeft(const Char A, const Char<RHSSign> B, unsigned OpBits,
+                        Char *R) {
+    *R = Char(A.V << B.V);
+  }
+
+  template <bool RHSSign>
+  static void shiftRight(const Char A, const Char<RHSSign> B, unsigned OpBits,
+                         Char *R) {
+    *R = Char(A.V >> B.V);
+  }
+
+  void print(llvm::raw_ostream &OS) const { OS << V; }
+};
+
+static_assert(sizeof(Char<true>) == 1);
+static_assert(sizeof(Char<false>) == 1);
+
+template <bool Signed>
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Char<Signed> I) {
+  I.print(OS);
+  return OS;
+}
+
+} // namespace interp
+} // namespace clang
+
+#endif
diff --git a/clang/lib/AST/ByteCode/Context.cpp 
b/clang/lib/AST/ByteCode/Context.cpp
index 879d51e6a2c3e..50d94bb81824f 100644
--- a/clang/lib/AST/ByteCode/Context.cpp
+++ b/clang/lib/AST/ByteCode/Context.cpp
@@ -9,6 +9,7 @@
 #include "Context.h"
 #include "Boolean.h"
 #include "ByteCodeEmitter.h"
+#include "Char.h"
 #include "Compiler.h"
 #include "EvalEmitter.h"
 #include "Integral.h"
@@ -186,7 +187,7 @@ bool Context::evaluateStringRepr(State &Parent, const Expr 
*SizeExpr,
       return false;
 
     // Must be char.
-    if (Ptr.getFieldDesc()->getElemSize() != 1 /*bytes*/)
+    if (Ptr.getFieldDesc()->getElemDataSize() != 1 /*bytes*/)
       return false;
 
     if (Size > Ptr.getNumElems()) {
diff --git a/clang/lib/AST/ByteCode/Descriptor.cpp 
b/clang/lib/AST/ByteCode/Descriptor.cpp
index 5ebc726328fb7..93aedd5d1ac65 100644
--- a/clang/lib/AST/ByteCode/Descriptor.cpp
+++ b/clang/lib/AST/ByteCode/Descriptor.cpp
@@ -8,6 +8,7 @@
 
 #include "Descriptor.h"
 #include "Boolean.h"
+#include "Char.h"
 #include "FixedPoint.h"
 #include "Floating.h"
 #include "IntegralAP.h"
@@ -483,3 +484,10 @@ bool Descriptor::hasTrivialDtor() const {
 }
 
 bool Descriptor::isUnion() const { return isRecord() && ElemRecord->isUnion(); 
}
+
+unsigned Descriptor::getElemDataSize() const {
+  if ((isPrimitive() || isPrimitiveArray()) && isIntegralType(getPrimType())) {
+    FIXED_SIZE_INT_TYPE_SWITCH(getPrimType(), { return T::bitWidth() / 8; });
+  }
+  return ElemSize;
+}
diff --git a/clang/lib/AST/ByteCode/Descriptor.h 
b/clang/lib/AST/ByteCode/Descriptor.h
index b052971733567..9046801f4ebef 100644
--- a/clang/lib/AST/ByteCode/Descriptor.h
+++ b/clang/lib/AST/ByteCode/Descriptor.h
@@ -246,6 +246,11 @@ struct Descriptor final {
   unsigned getAllocSize() const { return AllocSize; }
   /// returns the size of an element when the structure is viewed as an array.
   unsigned getElemSize() const { return ElemSize; }
+  /// Returns the element data size, i.e. not what the size of
+  /// our primitive data type is, but what the data size of that is.
+  /// E.g., for PT_SInt32, that's 4 bytes.
+  unsigned getElemDataSize() const;
+
   /// Returns the size of the metadata.
   unsigned getMetadataSize() const { return MDSize; }
 
diff --git a/clang/lib/AST/ByteCode/Disasm.cpp 
b/clang/lib/AST/ByteCode/Disasm.cpp
index 35937e3483e38..01183d50014fb 100644
--- a/clang/lib/AST/ByteCode/Disasm.cpp
+++ b/clang/lib/AST/ByteCode/Disasm.cpp
@@ -11,6 +11,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "Boolean.h"
+#include "Char.h"
 #include "Context.h"
 #include "EvaluationResult.h"
 #include "FixedPoint.h"
diff --git a/clang/lib/AST/ByteCode/Integral.h 
b/clang/lib/AST/ByteCode/Integral.h
index e90f1a9a74e1c..59542aef69f71 100644
--- a/clang/lib/AST/ByteCode/Integral.h
+++ b/clang/lib/AST/ByteCode/Integral.h
@@ -14,6 +14,7 @@
 #define LLVM_CLANG_AST_INTERP_INTEGRAL_H
 
 #include "clang/AST/APValue.h"
+#include "clang/AST/CharUnits.h"
 #include "clang/AST/ComparisonCategories.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/Support/MathExtras.h"
@@ -22,6 +23,7 @@
 #include <cstdint>
 
 #include "Primitives.h"
+#include "Program.h"
 
 namespace clang {
 namespace interp {
@@ -64,13 +66,27 @@ template <> struct Repr<64, true> {
 /// builtin primitive numeral types, while optimising for storage and
 /// allowing methods operating on primitive type to compile to fast code.
 template <unsigned Bits, bool Signed> class Integral final {
-private:
-  template <unsigned OtherBits, bool OtherSigned> friend class Integral;
+  static_assert(Bits >= 16);
 
+public:
   // The primitive representing the integral.
   using ReprT = typename Repr<Bits, Signed>::Type;
-  ReprT V;
+
+private:
+  using OffsetT = intptr_t;
   static_assert(std::is_trivially_copyable_v<ReprT>);
+  template <unsigned OtherBits, bool OtherSigned> friend class Integral;
+
+  IntegralKind Kind = IntegralKind::Number;
+  union {
+    ReprT V;
+    struct {
+      const void *P;
+      OffsetT Offset;
+    } Ptr;
+  };
+
+  static_assert(sizeof(uintptr_t) >= sizeof(ReprT));
 
   /// Primitive representing limits.
   static const auto Min = std::numeric_limits<ReprT>::min();
@@ -78,16 +94,41 @@ template <unsigned Bits, bool Signed> class Integral final {
 
   /// Construct an integral from anything that is convertible to storage.
   template <typename T> explicit Integral(T V) : V(V) {}
+  template <typename T>
+  explicit Integral(IntegralKind Kind, T V) : Kind(Kind), V(V) {}
 
 public:
   using AsUnsigned = Integral<Bits, false>;
 
   /// Zero-initializes an integral.
-  Integral() : V(0) {}
+  Integral() = default;
 
   /// Constructs an integral from another integral.
   template <unsigned SrcBits, bool SrcSign>
-  explicit Integral(Integral<SrcBits, SrcSign> V) : V(V.V) {}
+  explicit Integral(Integral<SrcBits, SrcSign> V) : Kind(V.Kind), V(V) {}
+
+  explicit Integral(IntegralKind Kind, const void *P, OffsetT Offset = 0)
+      : Kind(Kind) {
+    Ptr.P = P;
+    Ptr.Offset = Offset;
+  }
+
+  explicit Integral(const void *P1, const void *P2)
+      : Kind(IntegralKind::AddrLabelDiff) {
+    Ptr.P = P1;
+    Ptr.Offset = reinterpret_cast<uintptr_t>(P2);
+  }
+
+  IntegralKind getKind() const { return Kind; }
+  bool isNumber() const { return Kind == IntegralKind::Number; }
+  const void *getPtr() const {
+    assert(!isNumber());
+    return Ptr.P;
+  }
+  ReprT getOffset() const {
+    assert(!isNumber());
+    return Ptr.Offset;
+  }
 
   /// Construct an integral from a value based on signedness.
   explicit Integral(const APSInt &V)
@@ -115,7 +156,7 @@ template <unsigned Bits, bool Signed> class Integral final {
 
   template <unsigned DstBits, bool DstSign>
   explicit operator Integral<DstBits, DstSign>() const {
-    return Integral<DstBits, DstSign>(V);
+    return Integral<DstBits, DstSign>(Kind, V);
   }
 
   template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>>
@@ -124,12 +165,16 @@ template <unsigned Bits, bool Signed> class Integral 
final {
   }
 
   APSInt toAPSInt() const {
+    assert(isNumber());
     return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed);
   }
+
   APSInt toAPSInt(unsigned BitWidth) const {
     return APSInt(toAPInt(BitWidth), !Signed);
   }
+
   APInt toAPInt(unsigned BitWidth) const {
+    assert(isNumber());
     if constexpr (Signed)
       return APInt(Bits, static_cast<uint64_t>(V), Signed)
           .sextOrTrunc(BitWidth);
@@ -137,22 +182,48 @@ template <unsigned Bits, bool Signed> class Integral 
final {
       return APInt(Bits, static_cast<uint64_t>(V), Signed)
           .zextOrTrunc(BitWidth);
   }
-  APValue toAPValue(const ASTContext &) const { return APValue(toAPSInt()); }
+
+  APValue toAPValue(const ASTContext &) const {
+    switch (Kind) {
+    case IntegralKind::Address: {
+      return APValue((const ValueDecl *)Ptr.P,
+                     CharUnits::fromQuantity(Ptr.Offset),
+                     APValue::NoLValuePath{});
+    }
+    case IntegralKind::LabelAddress: {
+      return APValue((const Expr *)Ptr.P, CharUnits::Zero(),
+                     APValue::NoLValuePath{});
+    }
+    case IntegralKind::BlockAddress: {
+      const Block *B = reinterpret_cast<const Block *>(Ptr.P);
+      const Descriptor *D = B->getDescriptor();
+      if (const Expr *E = D->asExpr())
+        return APValue(E, CharUnits::Zero(), APValue::NoLValuePath{});
+
+      return APValue(D->asValueDecl(), CharUnits::Zero(),
+                     APValue::NoLValuePath{});
+    }
+    case IntegralKind::AddrLabelDiff: {
+      return APValue(
+          (const AddrLabelExpr *)Ptr.P,
+          (const AddrLabelExpr *)reinterpret_cast<const void *>(Ptr.Offset));
+    }
+    case IntegralKind::Number:
+      return APValue(toAPSInt());
+    }
+    llvm_unreachable("Unhandled IntegralKind");
+  }
 
   Integral<Bits, false> toUnsigned() const {
     return Integral<Bits, false>(*this);
   }
 
   constexpr static unsigned bitWidth() { return Bits; }
+  constexpr static bool isSigned() { return Signed; }
 
   bool isZero() const { return !V; }
-
   bool isMin() const { return *this == min(bitWidth()); }
-
   bool isMinusOne() const { return Signed && V == ReprT(-1); }
-
-  constexpr static bool isSigned() { return Signed; }
-
   bool isNegative() const { return V < ReprT(0); }
   bool isPositive() const { return !isNegative(); }
 
@@ -161,6 +232,7 @@ template <unsigned Bits, bool Signed> class Integral final {
   }
 
   void bitcastToMemory(std::byte *Dest) const {
+    assert(isNumber());
     std::memcpy(Dest, &V, sizeof(V));
   }
 
@@ -180,6 +252,7 @@ template <unsigned Bits, bool Signed> class Integral final {
   }
 
   unsigned countLeadingZeros() const {
+    assert(isNumber());
     if constexpr (!Signed)
       return llvm::countl_zero<ReprT>(V);
     if (isPositive())
@@ -198,66 +271,112 @@ template <unsigned Bits, bool Signed> class Integral 
final {
     return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0));
   }
 
-  void print(llvm::raw_ostream &OS) const { OS << V; }
+  void print(llvm::raw_ostream &OS) const {
+    switch (Kind) {
+    case IntegralKind::Number:
+      OS << V;
+      break;
+    case IntegralKind::AddrLabelDiff:
+      OS << Ptr.P << " - " << (const void *)Ptr.Offset << " (AddrLabelDiff)";
+      break;
+    case IntegralKind::Address:
+      OS << Ptr.P << " + " << Ptr.Offset << " (Address)";
+      break;
+    case IntegralKind::BlockAddress:
+      OS << Ptr.P << " + " << Ptr.Offset << " (BlockAddress)";
+      break;
+    case IntegralKind::LabelAddress:
+      OS << Ptr.P << " + " << Ptr.Offset << " (LabelAddress)";
+    }
+  }
 
   static Integral min(unsigned NumBits) { return Integral(Min); }
   static Integral max(unsigned NumBits) { return Integral(Max); }
   static Integral zero(unsigned BitWidth = 0) { return from(0); }
 
   template <typename ValT>
-  static Integral from(ValT Value, unsigned NumBits = 0) {
+  static std::enable_if_t<!std::is_same_v<ValT, IntegralKind>, Integral>
+  from(ValT V, unsigned NumBits = 0) {
     if constexpr (std::is_integral_v<ValT>)
-      return Integral(Value);
+      return Integral(V);
     else
-      return Integral(static_cast<Integral::ReprT>(Value));
+      return Integral(static_cast<Integral::ReprT>(V));
   }
 
   template <unsigned SrcBits, bool SrcSign>
-  static Integral from(Integral<SrcBits, SrcSign> Value) {
-    return Integral(Value.V);
+  static std::enable_if_t<SrcBits != 0, Integral>
+  from(Integral<SrcBits, SrcSign> V) {
+    auto A = Integral(V.Kind, V.V);
+    switch (V.Kind) {
+    case IntegralKind::Number:
+      A.V = V.V;
+      break;
+    case IntegralKind::AddrLabelDiff:
+    case IntegralKind::Address:
+    case IntegralKind::BlockAddress:
+    case IntegralKind::LabelAddress:
+      A.Ptr.P = V.Ptr.P;
+      A.Ptr.Offset = V.Ptr.Offset;
+      break;
+    }
+    return A;
+  }
+
+  template <typename T> static Integral from(IntegralKind Kind, T V) {
+    return Integral(Kind, V);
   }
 
   static bool increment(Integral A, Integral *R) {
+    assert(A.isNumber());
     return add(A, Integral(ReprT(1)), A.bitWidth(), R);
   }
 
   static bool decrement(Integral A, Integral *R) {
+    assert(A.isNumber());
     return sub(A, Integral(ReprT(1)), A.bitWidth(), R);
   }
 
   static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) {
+    assert(A.isNumber() && B.isNumber());
     return CheckAddUB(A.V, B.V, R->V);
   }
 
   static bool sub(Integral A, Integral B, unsigned OpBits, Integral *R) {
+    assert(A.isNumber() && B.isNumber());
     return CheckSubUB(A.V, B.V, R->V);
   }
 
   static bool mul(Integral A, Integral B, unsigned OpBits, Integral *R) {
+    assert(A.isNumber() && B.isNumber());
     return CheckMulUB(A.V, B.V, R->V);
   }
 
   static bool rem(Integral A, Integral B, unsigned OpBits, Integral *R) {
+    assert(A.isNumber() && B.isNumber());
     *R = Integral(A.V % B.V);
     return false;
   }
 
   static bool div(Integral A, Integral B, unsigned OpBits, Integral *R) {
+    assert(A.isNumber() && B.isNumber());
     *R = Integral(A.V / B.V);
     return false;
   }
 
   static bool bitAnd(Integral A, Integral B, unsigned OpBits, Integral *R) {
+    assert(A.isNumber() && B.isNumber());
     *R = Integral(A.V & B.V);
     return false;
   }
 
   static bool bitOr(Integral A, Integral B, unsigned OpBits, Integral *R) {
+    assert(A.isNumber() && B.isNumber());
     *R = Integral(A.V | B.V);
     return false;
   }
 
   static bool bitXor(Integral A, Integral B, unsigned OpBits, Integral *R) {
+    assert(A.isNumber() && B.isNumber());
     *R = Integral(A.V ^ B.V);
     return false;
   }
@@ -286,39 +405,6 @@ template <unsigned Bits, bool Signed> class Integral final 
{
                          unsigned OpBits, Integral *R) {
     *R = Integral::from(A.V >> B.V, OpBits);
   }
-
-private:
-  template <typename T> static bool CheckAddUB(T A, T B, T &R) {
-    if constexpr (std::is_signed_v<T>) {
-      return llvm::AddOverflow<T>(A, B, R);
-    } else {
-      R = A + B;
-      return false;
-    }
-  }
-
-  template <typename T> static bool CheckSubUB(T A, T B, T &R) {
-    if constexpr (std::is_signed_v<T>) {
-      return llvm::SubOverflow<T>(A, B, R);
-    } else {
-      R = A - B;
-      return false;
-    }
-  }
-
-  template <typename T> static bool CheckMulUB(T A, T B, T &R) {
-    if constexpr (std::is_signed_v<T>) {
-      return llvm::MulOverflow<T>(A, B, R);
-    } else if constexpr (sizeof(T) < sizeof(int)) {
-      // Silly integer promotion rules will convert both A and B to int,
-      // even it T is unsigned. Prevent that by manually casting to uint first.
-      R = static_cast<T>(static_cast<unsigned>(A) * static_cast<unsigned>(B));
-      return false;
-    } else {
-      R = A * B;
-      return false;
-    }
-  }
 };
 
 template <unsigned Bits, bool Signed>
diff --git a/clang/lib/AST/ByteCode/IntegralAP.h 
b/clang/lib/AST/ByteCode/IntegralAP.h
index b11e6eea28e3f..9f53ac7716bba 100644
--- a/clang/lib/AST/ByteCode/IntegralAP.h
+++ b/clang/lib/AST/ByteCode/IntegralAP.h
@@ -139,6 +139,7 @@ template <bool Signed> class IntegralAP final {
   constexpr uint32_t bitWidth() const { return BitWidth; }
   constexpr unsigned numWords() const { return APInt::getNumWords(BitWidth); }
   constexpr bool singleWord() const { return numWords() == 1; }
+  constexpr static bool isNumber() { return true; }
 
   APSInt toAPSInt(unsigned Bits = 0) const {
     if (Bits == 0)
diff --git a/clang/lib/AST/ByteCode/Interp.cpp 
b/clang/lib/AST/ByteCode/Interp.cpp
index ebc7220aa5671..cd67d35dff8b2 100644
--- a/clang/lib/AST/ByteCode/Interp.cpp
+++ b/clang/lib/AST/ByteCode/Interp.cpp
@@ -2183,16 +2183,17 @@ bool InvalidShuffleVectorIndex(InterpState &S, CodePtr 
OpPC, uint32_t Index) {
 
 bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
                                 const Pointer &Ptr, unsigned BitWidth) {
-  const SourceInfo &E = S.Current->getSource(OpPC);
+  SourceInfo E = S.Current->getSource(OpPC);
   S.CCEDiag(E, diag::note_constexpr_invalid_cast)
       << 2 << S.getLangOpts().CPlusPlus << S.Current->getRange(OpPC);
 
-  if (Ptr.isDummy())
-    return false;
-  if (Ptr.isFunctionPointer())
+  if (Ptr.isIntegralPointer())
     return true;
 
-  if (Ptr.isBlockPointer() && !Ptr.isZero()) {
+  if (Ptr.isDummy())
+    return Ptr.getIndex() == 0;
+
+  if (!Ptr.isZero()) {
     // Only allow based lvalue casts if they are lossless.
     if (S.getASTContext().getTargetInfo().getPointerWidth(LangAS::Default) !=
         BitWidth)
@@ -2201,6 +2202,11 @@ bool CheckPointerToIntegralCast(InterpState &S, CodePtr 
OpPC,
   return true;
 }
 
+bool CheckIntegralAddressCast(InterpState &S, CodePtr OpPC, unsigned BitWidth) 
{
+  return (S.getASTContext().getTargetInfo().getPointerWidth(LangAS::Default) ==
+          BitWidth);
+}
+
 bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
 
@@ -2286,13 +2292,19 @@ bool DiagTypeid(InterpState &S, CodePtr OpPC) {
 
 bool arePotentiallyOverlappingStringLiterals(const Pointer &LHS,
                                              const Pointer &RHS) {
+  if (!LHS.pointsToStringLiteral() || !RHS.pointsToStringLiteral())
+    return false;
+
   unsigned LHSOffset = LHS.isOnePastEnd() ? LHS.getNumElems() : LHS.getIndex();
   unsigned RHSOffset = RHS.isOnePastEnd() ? RHS.getNumElems() : RHS.getIndex();
-  unsigned LHSLength = (LHS.getNumElems() - 1) * LHS.elemSize();
-  unsigned RHSLength = (RHS.getNumElems() - 1) * RHS.elemSize();
+  const auto *LHSLit = cast<StringLiteral>(LHS.getDeclDesc()->asExpr());
+  const auto *RHSLit = cast<StringLiteral>(RHS.getDeclDesc()->asExpr());
+
+  StringRef LHSStr(LHSLit->getBytes());
+  unsigned LHSLength = LHSStr.size();
+  StringRef RHSStr(RHSLit->getBytes());
+  unsigned RHSLength = RHSStr.size();
 
-  StringRef LHSStr((const char *)LHS.atIndex(0).getRawAddress(), LHSLength);
-  StringRef RHSStr((const char *)RHS.atIndex(0).getRawAddress(), RHSLength);
   int32_t IndexDiff = RHSOffset - LHSOffset;
   if (IndexDiff < 0) {
     if (static_cast<int32_t>(LHSLength) < -IndexDiff)
@@ -2308,11 +2320,11 @@ bool arePotentiallyOverlappingStringLiterals(const 
Pointer &LHS,
   StringRef Shorter;
   StringRef Longer;
   if (LHSLength < RHSLength) {
-    ShorterCharWidth = LHS.elemSize();
+    ShorterCharWidth = LHS.getFieldDesc()->getElemDataSize();
     Shorter = LHSStr;
     Longer = RHSStr;
   } else {
-    ShorterCharWidth = RHS.elemSize();
+    ShorterCharWidth = RHS.getFieldDesc()->getElemDataSize();
     Shorter = RHSStr;
     Longer = LHSStr;
   }
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 7f30def20cc36..6a7b0e694b5a7 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -16,6 +16,7 @@
 #include "../ExprConstShared.h"
 #include "BitcastBuffer.h"
 #include "Boolean.h"
+#include "Char.h"
 #include "DynamicAllocator.h"
 #include "FixedPoint.h"
 #include "Floating.h"
@@ -280,6 +281,11 @@ template <typename T, bool (*OpFW)(T, T, unsigned, T *),
           template <typename U> class OpAP>
 bool AddSubMulHelper(InterpState &S, CodePtr OpPC, unsigned Bits, const T &LHS,
                      const T &RHS) {
+  // Should've been handled before.
+  if constexpr (isIntegralOrPointer<T>()) {
+    assert(LHS.isNumber() && RHS.isNumber());
+  }
+
   // Fast path - add the numbers with fixed width.
   T Result;
   if constexpr (needsAlloc<T>())
@@ -318,12 +324,47 @@ bool AddSubMulHelper(InterpState &S, CodePtr OpPC, 
unsigned Bits, const T &LHS,
   return true;
 }
 
+// Add or subtract an integer-thats-actually-a-pointer and one real integer.
+template <typename T, template <typename U> class Op>
+static bool AddSubNonNumber(InterpState &S, CodePtr OpPC, T LHS, T RHS) {
+  assert(!LHS.isNumber() || !RHS.isNumber());
+
+  typename T::ReprT Number;
+  const void *Ptr;
+  typename T::ReprT Offset;
+  IntegralKind Kind;
+  if (LHS.isNumber()) {
+    Number = static_cast<typename T::ReprT>(LHS);
+    Ptr = RHS.getPtr();
+    Offset = RHS.getOffset();
+    Kind = RHS.getKind();
+  } else {
+    assert(RHS.isNumber());
+    Number = static_cast<typename T::ReprT>(RHS);
+    Ptr = LHS.getPtr();
+    Offset = LHS.getOffset();
+    Kind = LHS.getKind();
+  }
+
+  S.Stk.push<T>(Kind, Ptr, Op<int32_t>()(Offset, Number));
+  return true;
+}
+
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool Add(InterpState &S, CodePtr OpPC) {
   const T &RHS = S.Stk.pop<T>();
   const T &LHS = S.Stk.pop<T>();
   const unsigned Bits = RHS.bitWidth() + 1;
 
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (LHS.isNumber() != RHS.isNumber())
+      return AddSubNonNumber<T, std::plus>(S, OpPC, LHS, RHS);
+    else if (LHS.isNumber() && RHS.isNumber())
+      ; // Fall through to proper addition below.
+    else
+      return false; // Reject everything else.
+  }
+
   return AddSubMulHelper<T, T::add, std::plus>(S, OpPC, Bits, LHS, RHS);
 }
 
@@ -344,6 +385,35 @@ bool Sub(InterpState &S, CodePtr OpPC) {
   const T &LHS = S.Stk.pop<T>();
   const unsigned Bits = RHS.bitWidth() + 1;
 
+  if constexpr (isIntegralOrPointer<T>()) {
+    // Handle (int)&&a - (int)&&b.
+    // Both operands should be integrals that point to labels and the result is
+    // a AddrLabelDiff integral.
+    if (LHS.getKind() == IntegralKind::LabelAddress ||
+        RHS.getKind() == IntegralKind::LabelAddress) {
+      const auto *A = reinterpret_cast<const Expr *>(LHS.getPtr());
+      const auto *B = reinterpret_cast<const Expr *>(RHS.getPtr());
+      if (!isa<AddrLabelExpr>(A) || !isa<AddrLabelExpr>(B))
+        return false;
+      const auto *LHSAddrExpr = cast<AddrLabelExpr>(A);
+      const auto *RHSAddrExpr = cast<AddrLabelExpr>(B);
+
+      if (LHSAddrExpr->getLabel()->getDeclContext() !=
+          RHSAddrExpr->getLabel()->getDeclContext())
+        return Invalid(S, OpPC);
+
+      S.Stk.push<T>(LHSAddrExpr, RHSAddrExpr);
+      return true;
+    }
+
+    if (!LHS.isNumber() && RHS.isNumber())
+      return AddSubNonNumber<T, std::minus>(S, OpPC, LHS, RHS);
+    else if (LHS.isNumber() && RHS.isNumber())
+      ; // Fall through to proper addition below.
+    else
+      return false; // Reject everything else.
+  }
+
   return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, Bits, LHS, RHS);
 }
 
@@ -364,6 +434,11 @@ bool Mul(InterpState &S, CodePtr OpPC) {
   const T &LHS = S.Stk.pop<T>();
   const unsigned Bits = RHS.bitWidth() * 2;
 
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!LHS.isNumber() || !RHS.isNumber())
+      return Invalid(S, OpPC);
+  }
+
   return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
 }
 
@@ -547,6 +622,11 @@ bool BitAnd(InterpState &S, CodePtr OpPC) {
   const T &LHS = S.Stk.pop<T>();
   unsigned Bits = RHS.bitWidth();
 
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!LHS.isNumber() || !RHS.isNumber())
+      return false;
+  }
+
   T Result;
   if constexpr (needsAlloc<T>())
     Result = S.allocAP<T>(Bits);
@@ -567,6 +647,11 @@ bool BitOr(InterpState &S, CodePtr OpPC) {
   const T &LHS = S.Stk.pop<T>();
   unsigned Bits = RHS.bitWidth();
 
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!LHS.isNumber() || !RHS.isNumber())
+      return false;
+  }
+
   T Result;
   if constexpr (needsAlloc<T>())
     Result = S.allocAP<T>(Bits);
@@ -585,9 +670,13 @@ template <PrimType Name, class T = typename 
PrimConv<Name>::T>
 bool BitXor(InterpState &S, CodePtr OpPC) {
   const T &RHS = S.Stk.pop<T>();
   const T &LHS = S.Stk.pop<T>();
-
   unsigned Bits = RHS.bitWidth();
 
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!LHS.isNumber() || !RHS.isNumber())
+      return false;
+  }
+
   T Result;
   if constexpr (needsAlloc<T>())
     Result = S.allocAP<T>(Bits);
@@ -1049,6 +1138,12 @@ bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn 
Fn) {
   using BoolT = PrimConv<PT_Bool>::T;
   const T &RHS = S.Stk.pop<T>();
   const T &LHS = S.Stk.pop<T>();
+
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!LHS.isNumber() || !RHS.isNumber())
+      return Invalid(S, OpPC);
+  }
+
   S.Stk.push<BoolT>(BoolT::from(Fn(LHS.compare(RHS))));
   return true;
 }
@@ -1376,7 +1471,24 @@ bool Const(InterpState &S, CodePtr OpPC, const T &Arg) {
     S.Stk.push<T>(Result);
     return true;
   }
-  S.Stk.push<T>(Arg);
+
+  if constexpr (std::is_same_v<T, uint16_t>) {
+    S.Stk.push<Integral<16, false>>(Integral<16, false>::from(Arg));
+  } else if constexpr (std::is_same_v<T, int16_t>) {
+    S.Stk.push<Integral<16, true>>(Integral<16, true>::from(Arg));
+  } else if constexpr (std::is_same_v<T, uint32_t>) {
+    S.Stk.push<Integral<32, false>>(Integral<32, false>::from(Arg));
+  } else if constexpr (std::is_same_v<T, int32_t>) {
+    S.Stk.push<Integral<32, true>>(Integral<32, true>::from(Arg));
+  } else if constexpr (std::is_same_v<T, uint64_t>) {
+    S.Stk.push<Integral<64, false>>(Integral<64, false>::from(Arg));
+  } else if constexpr (std::is_same_v<T, int64_t>) {
+    S.Stk.push<Integral<64, true>>(Integral<64, true>::from(Arg));
+  } else {
+    // Bool.
+    S.Stk.push<T>(Arg);
+  }
+
   return true;
 }
 
@@ -1646,6 +1758,12 @@ bool InitThisBitField(InterpState &S, CodePtr OpPC, 
const Record::Field *F,
   const Pointer &Field = This.atField(FieldOffset);
   assert(Field.canBeInitialized());
   const auto &Value = S.Stk.pop<T>();
+
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!Value.isNumber())
+      return false;
+  }
+
   Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue());
   Field.initialize();
   return true;
@@ -1663,6 +1781,12 @@ bool InitThisBitFieldActivate(InterpState &S, CodePtr 
OpPC,
   const Pointer &Field = This.atField(FieldOffset);
   assert(Field.canBeInitialized());
   const auto &Value = S.Stk.pop<T>();
+
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!Value.isNumber())
+      return false;
+  }
+
   Field.deref<T>() = Value.truncate(F->Decl->getBitWidthValue());
   Field.initialize();
   Field.activate();
@@ -1691,6 +1815,7 @@ template <PrimType Name, class T = typename 
PrimConv<Name>::T>
 bool InitFieldActivate(InterpState &S, CodePtr OpPC, uint32_t I) {
   const T &Value = S.Stk.pop<T>();
   const Pointer &Ptr = S.Stk.peek<Pointer>();
+
   if (!CheckRange(S, OpPC, Ptr, CSK_Field))
     return false;
   if (!CheckArray(S, OpPC, Ptr))
@@ -1708,6 +1833,11 @@ bool InitBitField(InterpState &S, CodePtr OpPC, const 
Record::Field *F) {
   assert(F->isBitField());
   const T &Value = S.Stk.pop<T>();
   const Pointer &Ptr = S.Stk.peek<Pointer>();
+
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!Value.isNumber())
+      return false;
+  }
   if (!CheckRange(S, OpPC, Ptr, CSK_Field))
     return false;
   if (!CheckArray(S, OpPC, Ptr))
@@ -1739,6 +1869,11 @@ bool InitBitFieldActivate(InterpState &S, CodePtr OpPC,
   assert(F->isBitField());
   const T &Value = S.Stk.pop<T>();
   const Pointer &Ptr = S.Stk.peek<Pointer>();
+
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!Value.isNumber())
+      return false;
+  }
   if (!CheckRange(S, OpPC, Ptr, CSK_Field))
     return false;
   if (!CheckArray(S, OpPC, Ptr))
@@ -2405,7 +2540,7 @@ static inline bool IncDecPtrHelper(InterpState &S, 
CodePtr OpPC,
   if (Ptr.isDummy())
     return false;
 
-  using OneT = Integral<8, false>;
+  using OneT = Char<false>;
 
   const Pointer &P = Ptr.deref<Pointer>();
   if (!CheckNull(S, OpPC, P, CSK_ArrayIndex))
@@ -2451,6 +2586,26 @@ inline bool SubPtr(InterpState &S, CodePtr OpPC, bool 
ElemSizeIsZero) {
   const Pointer &LHS = S.Stk.pop<Pointer>().expand();
   const Pointer &RHS = S.Stk.pop<Pointer>().expand();
 
+  if (LHS.pointsToLabel() || RHS.pointsToLabel()) {
+    if constexpr (isIntegralOrPointer<T>()) {
+      const auto *LHSAddrExpr =
+          dyn_cast_if_present<AddrLabelExpr>(LHS.getDeclDesc()->asExpr());
+      const auto *RHSAddrExpr =
+          dyn_cast_if_present<AddrLabelExpr>(RHS.getDeclDesc()->asExpr());
+      if (!LHSAddrExpr || !RHSAddrExpr)
+        return false;
+
+      if (LHSAddrExpr->getLabel()->getDeclContext() !=
+          RHSAddrExpr->getLabel()->getDeclContext())
+        return Invalid(S, OpPC);
+
+      S.Stk.push<T>(LHSAddrExpr, RHSAddrExpr);
+      return true;
+    }
+    // Can't represent an address-label-diff in these types.
+    return false;
+  }
+
   if (!Pointer::hasSameBase(LHS, RHS) && S.getLangOpts().CPlusPlus) {
     S.FFDiag(S.Current->getSource(OpPC),
              diag::note_constexpr_pointer_arith_unspecified)
@@ -2520,7 +2675,27 @@ inline bool GetLocalEnabled(InterpState &S, CodePtr 
OpPC, uint32_t I) {
 template <PrimType TIn, PrimType TOut> bool Cast(InterpState &S, CodePtr OpPC) 
{
   using T = typename PrimConv<TIn>::T;
   using U = typename PrimConv<TOut>::T;
-  S.Stk.push<U>(U::from(S.Stk.pop<T>()));
+
+  auto In = S.Stk.pop<T>();
+
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (In.getKind() != IntegralKind::Number &&
+        In.getKind() != IntegralKind::AddrLabelDiff) {
+      if (!CheckIntegralAddressCast(S, OpPC, In.bitWidth()))
+        return Invalid(S, OpPC);
+    } else if (In.getKind() == IntegralKind::AddrLabelDiff) {
+      // Allow casts of address-of-label differences if they are no-ops
+      // or narrowing, if the result is at least 32 bits wide.
+      // (The narrowing case isn't actually guaranteed to
+      // be constant-evaluatable except in some narrow cases which are hard
+      // to detect here.  We let it through on the assumption the user knows
+      // what they are doing.)
+      if (!(U::bitWidth() >= 32 && U::bitWidth() <= In.bitWidth()))
+        return false;
+    }
+  }
+
+  S.Stk.push<U>(U::from(In));
   return true;
 }
 
@@ -2554,11 +2729,18 @@ inline bool CastFixedPoint(InterpState &S, CodePtr 
OpPC, uint32_t FPS) {
 /// to know what bitwidth the result should be.
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool CastAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
+  T Source = S.Stk.pop<T>();
+
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!Source.isNumber())
+      return false;
+  }
+
   auto Result = S.allocAP<IntegralAP<false>>(BitWidth);
   // Copy data.
   {
-    APInt Source = S.Stk.pop<T>().toAPSInt().extOrTrunc(BitWidth);
-    Result.copy(Source);
+    APInt SourceInt = Source.toAPSInt().extOrTrunc(BitWidth);
+    Result.copy(SourceInt);
   }
   S.Stk.push<IntegralAP<false>>(Result);
   return true;
@@ -2566,11 +2748,18 @@ bool CastAP(InterpState &S, CodePtr OpPC, uint32_t 
BitWidth) {
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool CastAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
+  T Source = S.Stk.pop<T>();
+
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (!Source.isNumber())
+      return false;
+  }
+
   auto Result = S.allocAP<IntegralAP<true>>(BitWidth);
   // Copy data.
   {
-    APInt Source = S.Stk.pop<T>().toAPSInt().extOrTrunc(BitWidth);
-    Result.copy(Source);
+    APInt SourceInt = Source.toAPSInt().extOrTrunc(BitWidth);
+    Result.copy(SourceInt);
   }
   S.Stk.push<IntegralAP<true>>(Result);
   return true;
@@ -2668,17 +2857,45 @@ static inline bool CastFloatingIntegralAPS(InterpState 
&S, CodePtr OpPC,
 
 bool CheckPointerToIntegralCast(InterpState &S, CodePtr OpPC,
                                 const Pointer &Ptr, unsigned BitWidth);
+bool CheckIntegralAddressCast(InterpState &S, CodePtr OpPC, unsigned BitWidth);
 bool CastPointerIntegralAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
 bool CastPointerIntegralAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth);
 
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
-
   if (!CheckPointerToIntegralCast(S, OpPC, Ptr, T::bitWidth()))
     return Invalid(S, OpPC);
 
-  S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
+  if constexpr (std::is_same_v<T, Boolean>) {
+    S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
+  } else if constexpr (isIntegralOrPointer<T>()) {
+    if (Ptr.isBlockPointer()) {
+      IntegralKind Kind = IntegralKind::Address;
+      const void *PtrVal;
+      if (Ptr.isDummy()) {
+        if (const Expr *E = Ptr.getDeclDesc()->asExpr()) {
+          PtrVal = E;
+          if (isa<AddrLabelExpr>(E))
+            Kind = IntegralKind::LabelAddress;
+        } else {
+          PtrVal = Ptr.getDeclDesc()->asDecl();
+        }
+      } else {
+        PtrVal = Ptr.block();
+        Kind = IntegralKind::BlockAddress;
+      }
+      S.Stk.push<T>(Kind, PtrVal, /*Offset=*/0);
+    } else if (Ptr.isFunctionPointer()) {
+      const void *FuncDecl = Ptr.asFunctionPointer().getFunction()->getDecl();
+      S.Stk.push<T>(IntegralKind::Address, FuncDecl, /*Offset=*/0);
+    } else {
+      // FIXME: Is this still possible?
+      S.Stk.push<T>(T::from(Ptr.getIntegerRepresentation()));
+    }
+  } else {
+    return false;
+  }
   return true;
 }
 
@@ -3253,7 +3470,27 @@ inline bool GetIntPtr(InterpState &S, CodePtr OpPC, 
const Descriptor *Desc) {
       << diag::ConstexprInvalidCastKind::ThisConversionOrReinterpret
       << S.getLangOpts().CPlusPlus;
 
-  S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
+  if constexpr (isIntegralOrPointer<T>()) {
+    if (IntVal.getKind() == IntegralKind::Address) {
+      if (IntVal.getOffset() != 0) {
+        return Invalid(S, OpPC);
+      }
+      const VarDecl *VD = (const VarDecl *)IntVal.getPtr();
+      unsigned GlobalIndex = *S.P.getOrCreateGlobal(VD);
+      S.Stk.push<Pointer>(S.P.getGlobal(GlobalIndex));
+    } else if (IntVal.getKind() == IntegralKind::BlockAddress) {
+      if (IntVal.getOffset() != 0)
+        return Invalid(S, OpPC);
+
+      const Block *B = (const Block *)IntVal.getPtr();
+      S.Stk.push<Pointer>(const_cast<Block *>(B));
+    } else {
+      S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
+    }
+  } else {
+    S.Stk.push<Pointer>(static_cast<uint64_t>(IntVal), Desc);
+  }
+
   return true;
 }
 
@@ -3396,7 +3633,8 @@ template <PrimType Name, class T = typename 
PrimConv<Name>::T>
 inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
   llvm::SmallVector<int64_t> ArrayIndices;
   for (size_t I = 0; I != E->getNumExpressions(); ++I)
-    ArrayIndices.emplace_back(S.Stk.pop<int64_t>());
+    ArrayIndices.emplace_back(
+        static_cast<int64_t>(S.Stk.pop<Integral<64, true>>()));
 
   int64_t Result;
   if (!InterpretOffsetOf(S, OpPC, E, ArrayIndices, Result))
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index c7d3c2e500592..b14fab4cb0a1a 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 #include "../ExprConstShared.h"
 #include "Boolean.h"
+#include "Char.h"
 #include "EvalEmitter.h"
 #include "InterpBuiltinBitCast.h"
 #include "InterpHelpers.h"
@@ -364,14 +365,15 @@ static bool interp__builtin_strlen(InterpState &S, 
CodePtr OpPC,
     return false;
 
   assert(StrPtr.getFieldDesc()->isPrimitiveArray());
-  unsigned ElemSize = StrPtr.getFieldDesc()->getElemSize();
+  PrimType ElemT = StrPtr.getFieldDesc()->getPrimType();
+  unsigned ElemSize = StrPtr.getFieldDesc()->getElemDataSize();
   if (ElemSize != 1 && ElemSize != 2 && ElemSize != 4)
     return Invalid(S, OpPC);
 
   if (ID == Builtin::BI__builtin_wcslen || ID == Builtin::BIwcslen) {
     const ASTContext &AC = S.getASTContext();
     unsigned WCharSize = 
AC.getTypeSizeInChars(AC.getWCharType()).getQuantity();
-    if (ElemSize != WCharSize)
+    if (StrPtr.getFieldDesc()->getElemDataSize() != WCharSize)
       return false;
   }
 
@@ -383,19 +385,8 @@ static bool interp__builtin_strlen(InterpState &S, CodePtr 
OpPC,
       return false;
 
     uint32_t Val;
-    switch (ElemSize) {
-    case 1:
-      Val = ElemPtr.deref<uint8_t>();
-      break;
-    case 2:
-      Val = ElemPtr.deref<uint16_t>();
-      break;
-    case 4:
-      Val = ElemPtr.deref<uint32_t>();
-      break;
-    default:
-      llvm_unreachable("Unsupported char size");
-    }
+    FIXED_SIZE_INT_TYPE_SWITCH(
+        ElemT, { Val = static_cast<uint32_t>(ElemPtr.deref<T>()); });
     if (Val == 0)
       break;
   }
@@ -1383,11 +1374,11 @@ 
interp__builtin_ptrauth_string_discriminator(InterpState &S, CodePtr OpPC,
   const auto &Ptr = S.Stk.pop<Pointer>();
   assert(Ptr.getFieldDesc()->isPrimitiveArray());
 
-  // This should be created for a StringLiteral, so should alway shold at least
+  // This should be created for a StringLiteral, so always holds at least
   // one array element.
   assert(Ptr.getFieldDesc()->getNumElems() >= 1);
-  StringRef R(&Ptr.deref<char>(), Ptr.getFieldDesc()->getNumElems() - 1);
-  uint64_t Result = getPointerAuthStableSipHash(R);
+  uint64_t Result = getPointerAuthStableSipHash(
+      cast<StringLiteral>(Ptr.getFieldDesc()->asExpr())->getString());
   pushInteger(S, Result, Call->getType());
   return true;
 }
@@ -1928,8 +1919,8 @@ static bool interp__builtin_memcpy(InterpState &S, 
CodePtr OpPC,
     Pointer SrcP = SrcPtr.stripBaseCasts();
     Pointer DestP = DestPtr.stripBaseCasts();
 
-    unsigned SrcIndex = SrcP.expand().getIndex() * SrcP.elemSize();
-    unsigned DstIndex = DestP.expand().getIndex() * DestP.elemSize();
+    unsigned SrcIndex = SrcP.expand().getIndex() * SrcElemSize;
+    unsigned DstIndex = DestP.expand().getIndex() * DestElemSize;
 
     if ((SrcIndex <= DstIndex && (SrcIndex + Size) > DstIndex) ||
         (DstIndex <= SrcIndex && (DstIndex + Size) > SrcIndex)) {
@@ -1997,6 +1988,7 @@ static bool interp__builtin_memcmp(InterpState &S, 
CodePtr OpPC,
   BitcastBuffer BufferA(
       Bits(ASTCtx.getTypeSize(ElemTypeA) * PtrA.getNumElems()));
   readPointerToBuffer(S.getContext(), PtrA, BufferA, false);
+
   // FIXME: The swapping here is UNDOING something we do when reading the
   // data into the buffer.
   if (ASTCtx.getTargetInfo().isBigEndian())
@@ -2023,21 +2015,22 @@ static bool interp__builtin_memcmp(InterpState &S, 
CodePtr OpPC,
 
   for (size_t I = 0; I != CmpSize; I += ElemSize) {
     if (IsWide) {
-      INT_TYPE_SWITCH(*S.getContext().classify(ASTCtx.getWCharType()), {
-        T A = *reinterpret_cast<T *>(BufferA.atByte(I));
-        T B = *reinterpret_cast<T *>(BufferB.atByte(I));
-        if (A < B) {
-          pushInteger(S, -1, Call->getType());
-          return true;
-        }
-        if (A > B) {
-          pushInteger(S, 1, Call->getType());
-          return true;
-        }
-      });
+      FIXED_SIZE_INT_TYPE_SWITCH(
+          *S.getContext().classify(ASTCtx.getWCharType()), {
+            T A = T::bitcastFromMemory(BufferA.atByte(I), T::bitWidth());
+            T B = T::bitcastFromMemory(BufferB.atByte(I), T::bitWidth());
+            if (A < B) {
+              pushInteger(S, -1, Call->getType());
+              return true;
+            }
+            if (A > B) {
+              pushInteger(S, 1, Call->getType());
+              return true;
+            }
+          });
     } else {
-      std::byte A = BufferA.deref<std::byte>(Bytes(I));
-      std::byte B = BufferB.deref<std::byte>(Bytes(I));
+      auto A = BufferA.deref<std::byte>(Bytes(I));
+      auto B = BufferB.deref<std::byte>(Bytes(I));
 
       if (A < B) {
         pushInteger(S, -1, Call->getType());
diff --git a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
index 4bd9c66fc9974..84d4509864029 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltinBitCast.cpp
@@ -8,6 +8,7 @@
 #include "InterpBuiltinBitCast.h"
 #include "BitcastBuffer.h"
 #include "Boolean.h"
+#include "Char.h"
 #include "Context.h"
 #include "Floating.h"
 #include "Integral.h"
@@ -311,7 +312,12 @@ bool clang::interp::readPointerToBuffer(const Context &Ctx,
 
           Buffer.markInitialized(BitOffset, NumBits);
         } else {
-          BITCAST_TYPE_SWITCH(T, { P.deref<T>().bitcastToMemory(Buff.get()); 
});
+          BITCAST_TYPE_SWITCH(T, {
+            auto Val = P.deref<T>();
+            if (!Val.isNumber())
+              return false;
+            Val.bitcastToMemory(Buff.get());
+          });
 
           if (llvm::sys::IsBigEndianHost)
             swapBytes(Buff.get(), FullBitWidth.roundToBytes());
@@ -471,7 +477,7 @@ bool clang::interp::DoBitCastPtr(InterpState &S, CodePtr 
OpPC,
 
 using PrimTypeVariant =
     std::variant<Pointer, FunctionPointer, MemberPointer, FixedPoint,
-                 Integral<8, false>, Integral<8, true>, Integral<16, false>,
+                 Char<false>, Char<true>, Integral<16, false>,
                  Integral<16, true>, Integral<32, false>, Integral<32, true>,
                  Integral<64, false>, Integral<64, true>, IntegralAP<true>,
                  IntegralAP<false>, Boolean, Floating>;
diff --git a/clang/lib/AST/ByteCode/InterpFrame.cpp 
b/clang/lib/AST/ByteCode/InterpFrame.cpp
index 3c185a0ad661a..abd96ccc6fbfd 100644
--- a/clang/lib/AST/ByteCode/InterpFrame.cpp
+++ b/clang/lib/AST/ByteCode/InterpFrame.cpp
@@ -8,6 +8,7 @@
 
 #include "InterpFrame.h"
 #include "Boolean.h"
+#include "Char.h"
 #include "Function.h"
 #include "InterpStack.h"
 #include "InterpState.h"
diff --git a/clang/lib/AST/ByteCode/InterpStack.cpp 
b/clang/lib/AST/ByteCode/InterpStack.cpp
index 992546560eec4..461bc35979247 100644
--- a/clang/lib/AST/ByteCode/InterpStack.cpp
+++ b/clang/lib/AST/ByteCode/InterpStack.cpp
@@ -8,6 +8,7 @@
 
 #include "InterpStack.h"
 #include "Boolean.h"
+#include "Char.h"
 #include "FixedPoint.h"
 #include "Floating.h"
 #include "Integral.h"
diff --git a/clang/lib/AST/ByteCode/InterpStack.h 
b/clang/lib/AST/ByteCode/InterpStack.h
index c647dfa6d85ea..b93c4f982585d 100644
--- a/clang/lib/AST/ByteCode/InterpStack.h
+++ b/clang/lib/AST/ByteCode/InterpStack.h
@@ -174,10 +174,10 @@ class InterpStack final {
     else if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, Boolean>)
       return PT_Bool;
     else if constexpr (std::is_same_v<T, int8_t> ||
-                       std::is_same_v<T, Integral<8, true>>)
+                       std::is_same_v<T, Char<true>>)
       return PT_Sint8;
     else if constexpr (std::is_same_v<T, uint8_t> ||
-                       std::is_same_v<T, Integral<8, false>>)
+                       std::is_same_v<T, Char<false>>)
       return PT_Uint8;
     else if constexpr (std::is_same_v<T, int16_t> ||
                        std::is_same_v<T, Integral<16, true>>)
@@ -185,18 +185,15 @@ class InterpStack final {
     else if constexpr (std::is_same_v<T, uint16_t> ||
                        std::is_same_v<T, Integral<16, false>>)
       return PT_Uint16;
-    else if constexpr (std::is_same_v<T, int32_t> ||
-                       std::is_same_v<T, Integral<32, true>>)
+    else if constexpr (std::is_same_v<T, Integral<32, true>>)
       return PT_Sint32;
-    else if constexpr (std::is_same_v<T, uint32_t> ||
-                       std::is_same_v<T, Integral<32, false>>)
+    else if constexpr (std::is_same_v<T, Integral<32, false>>)
       return PT_Uint32;
-    else if constexpr (std::is_same_v<T, int64_t> ||
-                       std::is_same_v<T, Integral<64, true>>)
+    else if constexpr (std::is_same_v<T, Integral<64, true>>)
       return PT_Sint64;
-    else if constexpr (std::is_same_v<T, uint64_t> ||
-                       std::is_same_v<T, Integral<64, false>>)
+    else if constexpr (std::is_same_v<T, Integral<64, false>>)
       return PT_Uint64;
+
     else if constexpr (std::is_same_v<T, Floating>)
       return PT_Float;
     else if constexpr (std::is_same_v<T, IntegralAP<true>>)
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp 
b/clang/lib/AST/ByteCode/Pointer.cpp
index f4352e7edf5f8..913e7d9e8c73d 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -8,6 +8,7 @@
 
 #include "Pointer.h"
 #include "Boolean.h"
+#include "Char.h"
 #include "Context.h"
 #include "Floating.h"
 #include "Function.h"
@@ -745,6 +746,15 @@ bool Pointer::pointsToStringLiteral() const {
   return isa_and_nonnull<StringLiteral>(E);
 }
 
+bool Pointer::pointsToLabel() const {
+  if (isZero() || !isBlockPointer())
+    return false;
+
+  if (const Expr *E = BS.Pointee->getDescriptor()->asExpr())
+    return isa<AddrLabelExpr>(E);
+  return false;
+}
+
 std::optional<std::pair<Pointer, Pointer>>
 Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) {
   if (!A.isBlockPointer() || !B.isBlockPointer())
diff --git a/clang/lib/AST/ByteCode/Pointer.h b/clang/lib/AST/ByteCode/Pointer.h
index 010e917de81b2..65af81817f9d3 100644
--- a/clang/lib/AST/ByteCode/Pointer.h
+++ b/clang/lib/AST/ByteCode/Pointer.h
@@ -366,7 +366,7 @@ class Pointer {
     if (isIntegralPointer()) {
       if (!Int.Desc)
         return 1;
-      return Int.Desc->getElemSize();
+      return Int.Desc->getElemDataSize();
     }
 
     if (BS.Base == RootPtrMark)
@@ -809,6 +809,8 @@ class Pointer {
   /// i.e. a non-MaterializeTemporaryExpr Expr.
   bool pointsToLiteral() const;
   bool pointsToStringLiteral() const;
+  /// Whether this points to a block created for an AddrLabelExpr.
+  bool pointsToLabel() const;
 
   /// Prints the pointer.
   void print(llvm::raw_ostream &OS) const;
diff --git a/clang/lib/AST/ByteCode/PrimType.cpp 
b/clang/lib/AST/ByteCode/PrimType.cpp
index b4c1fd0305540..923233e5fb13a 100644
--- a/clang/lib/AST/ByteCode/PrimType.cpp
+++ b/clang/lib/AST/ByteCode/PrimType.cpp
@@ -8,6 +8,7 @@
 
 #include "PrimType.h"
 #include "Boolean.h"
+#include "Char.h"
 #include "FixedPoint.h"
 #include "Floating.h"
 #include "IntegralAP.h"
diff --git a/clang/lib/AST/ByteCode/PrimType.h 
b/clang/lib/AST/ByteCode/PrimType.h
index 2433eb33c47b1..15474f9b460ac 100644
--- a/clang/lib/AST/ByteCode/PrimType.h
+++ b/clang/lib/AST/ByteCode/PrimType.h
@@ -28,6 +28,7 @@ class FunctionPointer;
 class MemberPointer;
 class FixedPoint;
 template <bool Signed> class IntegralAP;
+template <bool Signed> class Char;
 template <unsigned Bits, bool Signed> class Integral;
 
 /// Enumeration of the primitive types of the VM.
@@ -135,13 +136,22 @@ constexpr bool needsAlloc(PrimType T) {
   return T == PT_IntAP || T == PT_IntAPS || T == PT_Float || T == PT_MemberPtr;
 }
 
+template <typename T> constexpr bool isIntegralOrPointer() {
+  return std::is_same_v<T, Integral<16, false>> ||
+         std::is_same_v<T, Integral<16, true>> ||
+         std::is_same_v<T, Integral<32, false>> ||
+         std::is_same_v<T, Integral<32, true>> ||
+         std::is_same_v<T, Integral<64, false>> ||
+         std::is_same_v<T, Integral<64, true>>;
+}
+
 /// Mapping from primitive types to their representation.
 template <PrimType T> struct PrimConv;
 template <> struct PrimConv<PT_Sint8> {
-  using T = Integral<8, true>;
+  using T = Char<true>;
 };
 template <> struct PrimConv<PT_Uint8> {
-  using T = Integral<8, false>;
+  using T = Char<false>;
 };
 template <> struct PrimConv<PT_Sint16> {
   using T = Integral<16, true>;
@@ -249,6 +259,22 @@ static inline bool aligned(const void *P) {
     }                                                                          
\
   } while (0)
 
+#define FIXED_SIZE_INT_TYPE_SWITCH(Expr, B)                                    
\
+  do {                                                                         
\
+    switch (Expr) {                                                            
\
+      TYPE_SWITCH_CASE(PT_Sint8, B)                                            
\
+      TYPE_SWITCH_CASE(PT_Uint8, B)                                            
\
+      TYPE_SWITCH_CASE(PT_Sint16, B)                                           
\
+      TYPE_SWITCH_CASE(PT_Uint16, B)                                           
\
+      TYPE_SWITCH_CASE(PT_Sint32, B)                                           
\
+      TYPE_SWITCH_CASE(PT_Uint32, B)                                           
\
+      TYPE_SWITCH_CASE(PT_Sint64, B)                                           
\
+      TYPE_SWITCH_CASE(PT_Uint64, B)                                           
\
+    default:                                                                   
\
+      llvm_unreachable("Not an integer value");                                
\
+    }                                                                          
\
+  } while (0)
+
 #define INT_TYPE_SWITCH_NO_BOOL(Expr, B)                                       
\
   do {                                                                         
\
     switch (Expr) {                                                            
\
diff --git a/clang/lib/AST/ByteCode/Primitives.h 
b/clang/lib/AST/ByteCode/Primitives.h
index e935dbfd3691c..0d5e45b43dae2 100644
--- a/clang/lib/AST/ByteCode/Primitives.h
+++ b/clang/lib/AST/ByteCode/Primitives.h
@@ -21,6 +21,14 @@
 namespace clang {
 namespace interp {
 
+enum class IntegralKind : uint8_t {
+  Number = 0,
+  Address,
+  BlockAddress,
+  LabelAddress,
+  AddrLabelDiff
+};
+
 /// Helper to compare two comparable types.
 template <typename T> ComparisonCategoryResult Compare(const T &X, const T &Y) 
{
   if (X < Y)
@@ -30,6 +38,38 @@ template <typename T> ComparisonCategoryResult Compare(const 
T &X, const T &Y) {
   return ComparisonCategoryResult::Equal;
 }
 
+template <typename T> inline bool CheckAddUB(T A, T B, T &R) {
+  if constexpr (std::is_signed_v<T>) {
+    return llvm::AddOverflow<T>(A, B, R);
+  } else {
+    R = A + B;
+    return false;
+  }
+}
+
+template <typename T> inline bool CheckSubUB(T A, T B, T &R) {
+  if constexpr (std::is_signed_v<T>) {
+    return llvm::SubOverflow<T>(A, B, R);
+  } else {
+    R = A - B;
+    return false;
+  }
+}
+
+template <typename T> inline bool CheckMulUB(T A, T B, T &R) {
+  if constexpr (std::is_signed_v<T>) {
+    return llvm::MulOverflow<T>(A, B, R);
+  } else if constexpr (sizeof(T) < sizeof(int)) {
+    // Silly integer promotion rules will convert both A and B to int,
+    // even it T is unsigned. Prevent that by manually casting to uint first.
+    R = static_cast<T>(static_cast<unsigned>(A) * static_cast<unsigned>(B));
+    return false;
+  } else {
+    R = A * B;
+    return false;
+  }
+}
+
 } // namespace interp
 } // namespace clang
 
diff --git a/clang/lib/AST/ByteCode/Program.cpp 
b/clang/lib/AST/ByteCode/Program.cpp
index efef5db177e56..ef6b231b57233 100644
--- a/clang/lib/AST/ByteCode/Program.cpp
+++ b/clang/lib/AST/ByteCode/Program.cpp
@@ -7,6 +7,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "Program.h"
+#include "Char.h"
 #include "Context.h"
 #include "Function.h"
 #include "Integral.h"
diff --git a/clang/test/AST/ByteCode/addr-label-diff.c 
b/clang/test/AST/ByteCode/addr-label-diff.c
new file mode 100644
index 0000000000000..b8f77ecf041cd
--- /dev/null
+++ b/clang/test/AST/ByteCode/addr-label-diff.c
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm %s -o -                           
              | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm %s -o - 
-fexperimental-new-constant-interpreter | FileCheck %s
+
+typedef __typeof((int*) 0 - (int*) 0) intptr_t;
+
+// CHECK: @f1.l0 = internal global i64 ptrtoint (ptr @f1 to i64)
+void f1(void) { static intptr_t l0 = (intptr_t) f1; }
+
+// CHECK: @FoldableAddrLabelDiff.x = internal global i64 sub (i64 ptrtoint 
(ptr blockaddress(@FoldableAddrLabelDiff, %a) to i64), i64 ptrtoint (ptr 
blockaddress(@FoldableAddrLabelDiff, %b) to i64)), align 8
+void FoldableAddrLabelDiff() { static long x = (long)&&a-(long)&&b; 
a:b:return;}
+
+// CHECK: @c.ar = internal global {{.*}} sub (i{{..}} ptrtoint (ptr 
blockaddress(@c, %l2) to i{{..}}), i{{..}} ptrtoint (ptr blockaddress(@c, %l1) 
to i{{..}}))
+int c(void) {
+  static int ar = &&l2 - &&l1;
+l1:
+  return 10;
+l2:
+  return 11;
+}
diff --git a/clang/test/AST/ByteCode/addr-label-diff.cpp 
b/clang/test/AST/ByteCode/addr-label-diff.cpp
new file mode 100644
index 0000000000000..336a0fbe3de3e
--- /dev/null
+++ b/clang/test/AST/ByteCode/addr-label-diff.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm %s -o -                           
              | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64 -emit-llvm %s -o - 
-fexperimental-new-constant-interpreter | FileCheck %s
+
+
+// Make sure we don't try to fold this either.
+// CHECK: @_ZZ23UnfoldableAddrLabelDiffvE1x = internal global i128 0
+void UnfoldableAddrLabelDiff() { static __int128_t x = (long)&&a-(long)&&b; 
a:b:return;}
+
+// CHECK: @_ZZ24UnfoldableAddrLabelDiff2vE1x = internal global i16 0
+void UnfoldableAddrLabelDiff2() { static short x = (long)&&a-(long)&&b; 
a:b:return;}
+
+
+// But make sure we do fold this.
+// CHECK: @_ZZ21FoldableAddrLabelDiffvE1x = internal global i64 sub (i64 
ptrtoint (ptr blockaddress(@_Z21FoldableAddrLabelDiffv
+void FoldableAddrLabelDiff() { static long x = (long)&&a-(long)&&b; 
a:b:return;}
+
diff --git a/clang/test/AST/ByteCode/builtin-bit-cast.cpp 
b/clang/test/AST/ByteCode/builtin-bit-cast.cpp
index 09a67e60fb3be..7731b6eed10c8 100644
--- a/clang/test/AST/ByteCode/builtin-bit-cast.cpp
+++ b/clang/test/AST/ByteCode/builtin-bit-cast.cpp
@@ -590,3 +590,12 @@ namespace ToPrimPtrs {
                                                                                
 // both-note {{bit_cast to a member pointer type is not allowed in a constant 
expression}}
 #endif
 }
+
+namespace NonNumbers {
+#define fold(x) (__builtin_constant_p(x) ? (x) : (x))
+  constexpr long fn(void) {
+    return __builtin_bit_cast(long, fold((long)&fn)); // ref-note {{constexpr 
bit cast involving type}}
+  }
+  static_assert(fn() == 1); // both-error {{not an integral constant 
expression}} \
+                            // ref-note {{in call to}}
+}
diff --git a/clang/test/AST/ByteCode/const-eval.c 
b/clang/test/AST/ByteCode/const-eval.c
index 70b2a4dbd86e4..e61050e5e3040 100644
--- a/clang/test/AST/ByteCode/const-eval.c
+++ b/clang/test/AST/ByteCode/const-eval.c
@@ -1,5 +1,5 @@
-// 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 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
 
@@ -127,7 +127,7 @@ EVAL_EXPR(43, varfloat && constfloat) // both-error {{not 
an integer constant ex
 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) // expected-error {{declared as an array 
with a negative size}}
+EVAL_EXPR(48, &x != &x - 1 ? 1 : -1)
 EVAL_EXPR(49, &x < &x - 100 ? 1 : -1) // ref-error {{not an integer constant 
expression}}
 
 extern struct Test50S Test50;
@@ -173,6 +173,9 @@ _Static_assert(A > B, "");
 int * GH149500_p = &(*(int *)0x400);
 static const void *GH149500_q = &(*(const struct sysrq_key_op *)0);
 
+
+void f0(void) { static intptr_t l0 = (unsigned)(intptr_t) f0;} // both-error 
{{initializer element is not a compile-time constant}}
+
 #else
 #error :(
 #endif
diff --git a/clang/test/AST/ByteCode/cxx11.cpp 
b/clang/test/AST/ByteCode/cxx11.cpp
index e1fb5948b7708..91c828abd87a9 100644
--- a/clang/test/AST/ByteCode/cxx11.cpp
+++ b/clang/test/AST/ByteCode/cxx11.cpp
@@ -423,3 +423,15 @@ namespace DummyToGlobalBlockMove {
   Baz Bar::_m[] = {{0}};
   const AP m = {&Bar ::m};
 }
+
+namespace AddSubMulNonNumber {
+#define fold(x) (__builtin_constant_p(x) ? (x) : (x))
+
+  typedef decltype(sizeof(int)) LabelDiffTy;
+  constexpr LabelDiffTy mulBy3(LabelDiffTy x) { return x * 3; } // both-note 
{{subexpression}}
+  void LabelDiffTest() {
+    static_assert(mulBy3(fold((LabelDiffTy)&&a-(LabelDiffTy)&&b)) == 3, ""); 
// both-error {{constant expression}} \
+                                                                             
// both-note {{call to 'mulBy3(&&a - &&b)'}}
+    a:b:return;
+  }
+}
diff --git a/clang/test/AST/ByteCode/int-as-ptr-arith.c 
b/clang/test/AST/ByteCode/int-as-ptr-arith.c
new file mode 100644
index 0000000000000..4a24463f978c2
--- /dev/null
+++ b/clang/test/AST/ByteCode/int-as-ptr-arith.c
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1       -triple x86_64 %s                                     
    -emit-llvm -o - | FileCheck --check-prefixes=X86,CHECK %s
+// RUN: %clang_cc1       -triple x86_64 %s 
-fexperimental-new-constant-interpreter -emit-llvm -o - | FileCheck 
--check-prefixes=X86,CHECK %s
+// RUN: %clang_cc1       -triple avr    %s                                     
    -emit-llvm -o - | FileCheck --check-prefixes=AVR,CHECK %s
+// RUN: %clang_cc1       -triple avr    %s 
-fexperimental-new-constant-interpreter -emit-llvm -o - | FileCheck 
--check-prefixes=AVR,CHECK %s
+
+// RUN: %clang_cc1 -xc++ -triple x86_64 %s                                     
    -emit-llvm -o - | FileCheck --check-prefixes=X86,CHECK %s
+// RUN: %clang_cc1 -xc++ -triple x86_64 %s 
-fexperimental-new-constant-interpreter -emit-llvm -o - | FileCheck 
--check-prefixes=X86,CHECK %s
+// RUN: %clang_cc1 -xc++ -triple avr    %s                                     
    -emit-llvm -o - | FileCheck --check-prefixes=AVR,CHECK %s
+// RUN: %clang_cc1 -xc++ -triple avr    %s 
-fexperimental-new-constant-interpreter -emit-llvm -o - | FileCheck 
--check-prefixes=AVR,CHECK %s
+
+int a;
+__UINTPTR_TYPE__ ptrasintadd1 = (__UINTPTR_TYPE__)&a - 4;
+__UINTPTR_TYPE__ ptrasintadd2 = (__UINTPTR_TYPE__)&a + 4;
+__UINTPTR_TYPE__ ptrasintadd3 = ((__UINTPTR_TYPE__)&a + 4) + 10;
+__UINTPTR_TYPE__ ptrasintadd4 = (__UINTPTR_TYPE__)&a + ((__UINTPTR_TYPE__)-1);
+__UINTPTR_TYPE__ ptrasintadd5 = 4 + (__UINTPTR_TYPE__)&a;
+__UINTPTR_TYPE__ ptrasintadd6 = 10 + ((__UINTPTR_TYPE__)&a + 4);
+
+// CHECK: @ptrasintadd1 = global {{.*}} ptrtoint (ptr getelementptr (i8, ptr 
@a, {{.*}} -4) to {{.*}})
+// CHECK: @ptrasintadd2 = global {{.*}} ptrtoint (ptr getelementptr (i8, ptr 
@a, {{.*}} 4) to {{.*}})
+// CHECK: @ptrasintadd3 = global {{.*}} ptrtoint (ptr getelementptr (i8, ptr 
@a, {{.*}} 14) to {{.*}})
+// AVR:   @ptrasintadd4 = global {{.*}} ptrtoint (ptr getelementptr (i8, ptr 
@a, {{.*}} 65535) to {{.*}})
+// X86:   @ptrasintadd4 = global {{.*}} ptrtoint (ptr getelementptr (i8, ptr 
@a, {{.*}} -1) to {{.*}})
+// CHECK: @ptrasintadd5 = global {{.*}} ptrtoint (ptr getelementptr (i8, ptr 
@a, {{.*}} 4) to {{.*}})
+// CHECK: @ptrasintadd6 = global {{.*}} ptrtoint (ptr getelementptr (i8, ptr 
@a, {{.*}} 14) to {{.*}})
diff --git a/clang/test/CodeGen/const-init.c b/clang/test/CodeGen/const-init.c
index 175d221ad410a..930cfab1e62b5 100644
--- a/clang/test/CodeGen/const-init.c
+++ b/clang/test/CodeGen/const-init.c
@@ -1,5 +1,6 @@
 // setting strict FP behaviour in the run line below tests that the compiler
 // does the right thing for global compound literals (compoundliteral test)
+// RUN: %clang_cc1 -triple i386-pc-linux-gnu -ffreestanding 
-Wno-pointer-to-int-cast -Wno-int-conversion -ffp-exception-behavior=strict 
-emit-llvm -o - %s -fexperimental-new-constant-interpreter | FileCheck %s
 // RUN: %clang_cc1 -triple i386-pc-linux-gnu -ffreestanding 
-Wno-pointer-to-int-cast -Wno-int-conversion -ffp-exception-behavior=strict 
-emit-llvm -o - %s | FileCheck %s
 
 #include <stdint.h>
diff --git a/clang/test/CodeGen/const-label-addr.c 
b/clang/test/CodeGen/const-label-addr.c
index 8030f96cb8aed..086971045d2ca 100644
--- a/clang/test/CodeGen/const-label-addr.c
+++ b/clang/test/CodeGen/const-label-addr.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 %s -emit-llvm -o - -fexperimental-new-constant-interpreter 
| FileCheck %s
 // REQUIRES: asserts
 
 // CHECK: @a.a = internal global ptr blockaddress(@a, %A)
diff --git a/clang/test/CodeGen/statements.c b/clang/test/CodeGen/statements.c
index 07ae075d6d807..bdfb5fc718755 100644
--- a/clang/test/CodeGen/statements.c
+++ b/clang/test/CodeGen/statements.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -Wno-error=return-type -Wno-error=int-conversion %s 
-emit-llvm-only
+// RUN: %clang_cc1 -Wno-error=return-type -Wno-error=int-conversion %s 
-emit-llvm-only -fexperimental-new-constant-interpreter
 // REQUIRES: LP64
 
 // Mismatched type between return and function result.
diff --git a/clang/test/CodeGen/staticinit.c b/clang/test/CodeGen/staticinit.c
index ec9b5b34d3ade..7ada59e220776 100644
--- a/clang/test/CodeGen/staticinit.c
+++ b/clang/test/CodeGen/staticinit.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -triple i386-pc-linux-gnu -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple i386-pc-linux-gnu -emit-llvm -o - %s 
-fexperimental-new-constant-interpreter | FileCheck %s
 
 struct AStruct { 
   int i;
diff --git a/clang/test/CodeGenCXX/2008-05-07-CrazyOffsetOf.cpp 
b/clang/test/CodeGenCXX/2008-05-07-CrazyOffsetOf.cpp
index cb31a04c69fea..3be58ea1b7cae 100644
--- a/clang/test/CodeGenCXX/2008-05-07-CrazyOffsetOf.cpp
+++ b/clang/test/CodeGenCXX/2008-05-07-CrazyOffsetOf.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -triple=x86_64-unknown-linux -emit-llvm %s -o - | FileCheck 
%s
+// RUN: %clang_cc1 -triple=x86_64-unknown-linux -emit-llvm %s -o - 
-fexperimental-new-constant-interpreter | FileCheck %s
 
 struct bork {
   struct bork *next_local;
diff --git a/clang/test/CodeGenCXX/const-init-cxx11.cpp 
b/clang/test/CodeGenCXX/const-init-cxx11.cpp
index 0795fb534af4b..125bf8c383a81 100644
--- a/clang/test/CodeGenCXX/const-init-cxx11.cpp
+++ b/clang/test/CodeGenCXX/const-init-cxx11.cpp
@@ -1,6 +1,9 @@
 // RUN: %clang_cc1 -w -fmerge-all-constants -triple x86_64-elf-gnu -emit-llvm 
-o - %s -std=c++11 | FileCheck %s
 // RUN: %clang_cc1 -w -fmerge-all-constants -triple x86_64-elf-gnu -emit-llvm 
-o - %s -std=c++20 | FileCheck -check-prefix=CHECK20 %s
 
+// RUN: %clang_cc1 -w -fmerge-all-constants -triple x86_64-elf-gnu -emit-llvm 
-o - %s -std=c++11 -fexperimental-new-constant-interpreter | FileCheck %s
+// RUN: %clang_cc1 -w -fmerge-all-constants -triple x86_64-elf-gnu -emit-llvm 
-o - %s -std=c++20 -fexperimental-new-constant-interpreter | FileCheck 
-check-prefix=CHECK20 %s
+
 // FIXME: The padding in all these objects should be zero-initialized.
 namespace StructUnion {
   struct A {
diff --git a/clang/test/CodeGenCXX/const-init.cpp 
b/clang/test/CodeGenCXX/const-init.cpp
index f5b715949f23a..18ed3df22f425 100644
--- a/clang/test/CodeGenCXX/const-init.cpp
+++ b/clang/test/CodeGenCXX/const-init.cpp
@@ -2,6 +2,12 @@
 // RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -std=c++98 -o - %s | 
FileCheck %s
 // RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -std=c++11 -o - %s | 
FileCheck %s
 
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s 
-fexperimental-new-constant-interpreter | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -std=c++98 -o - %s 
-fexperimental-new-constant-interpreter | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -std=c++11 -o - %s 
-fexperimental-new-constant-interpreter | FileCheck %s
+
+
+
 // CHECK: @a = global i32 10
 int a = 10;
 // CHECK: @ar = constant ptr @a
diff --git a/clang/test/Sema/array-init.c b/clang/test/Sema/array-init.c
index 54a7877e8f2e5..9482fa7722640 100644
--- a/clang/test/Sema/array-init.c
+++ b/clang/test/Sema/array-init.c
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -std=gnu99 -fsyntax-only -pedantic 
-verify=expected,pedantic %s
 // RUN: %clang_cc1 -std=gnu99 -fsyntax-only -Wgnu -Wc11-extensions -verify %s
+// RUN: %clang_cc1 -std=gnu99 -fsyntax-only -pedantic 
-verify=expected,pedantic %s -fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 -std=gnu99 -fsyntax-only -Wgnu -Wc11-extensions -verify %s 
-fexperimental-new-constant-interpreter
 // REQUIRES: LP64
 
 extern int foof(void) = 1; // expected-error{{illegal initializer (only 
variables can be initialized)}}
diff --git a/clang/test/Sema/compound-literal.c 
b/clang/test/Sema/compound-literal.c
index 3ed53d670d38f..1026e5ece11b4 100644
--- a/clang/test/Sema/compound-literal.c
+++ b/clang/test/Sema/compound-literal.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -fblocks -pedantic %s
+// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -pedantic %s 
-fexperimental-new-constant-interpreter
 // REQUIRES: LP64
 
 struct foo { int a, b; };
diff --git a/clang/test/Sema/const-ptr-int-ptr-cast.c 
b/clang/test/Sema/const-ptr-int-ptr-cast.c
index 9e3db6eb6dae4..b1c06a723afc1 100644
--- a/clang/test/Sema/const-ptr-int-ptr-cast.c
+++ b/clang/test/Sema/const-ptr-int-ptr-cast.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -ffreestanding %s
+// RUN: %clang_cc1 -fsyntax-only -verify -ffreestanding %s 
-fexperimental-new-constant-interpreter
 // expected-no-diagnostics
 
 typedef __UINTPTR_TYPE__ uintptr_t;
diff --git a/clang/test/Sema/init.c b/clang/test/Sema/init.c
index 249320f8445f5..cf3788bc21c93 100644
--- a/clang/test/Sema/init.c
+++ b/clang/test/Sema/init.c
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 %s -Wno-pointer-to-int-cast -verify -fsyntax-only 
-ffreestanding
+// RUN: %clang_cc1 %s -Wno-pointer-to-int-cast -verify -fsyntax-only 
-ffreestanding -fexperimental-new-constant-interpreter
 
 #include <stddef.h>
 #include <stdint.h>
diff --git a/clang/test/SemaCXX/constexpr-string.cpp 
b/clang/test/SemaCXX/constexpr-string.cpp
index 93e234685d284..b49979bbc8ca0 100644
--- a/clang/test/SemaCXX/constexpr-string.cpp
+++ b/clang/test/SemaCXX/constexpr-string.cpp
@@ -8,6 +8,7 @@
 // RUN: %clang_cc1 %s -triple armebv7-unknown-linux -std=c++2a -fsyntax-only 
-verify -pedantic -Wno-vla-extension -fno-wchar -DNO_PREDEFINED_WCHAR_T
 
 // RUN: %clang_cc1 %s -triple armebv7-unknown-linux -std=c++2a -fsyntax-only 
-verify -pedantic -Wno-vla-extension -fno-signed-char 
-fexperimental-new-constant-interpreter
+// RUN: %clang_cc1 %s -triple armebv7-unknown-linux -std=c++2a -fsyntax-only 
-verify -pedantic -Wno-vla-extension -fno-wchar -DNO_PREDEFINED_WCHAR_T 
-fexperimental-new-constant-interpreter
 
 # 9 "/usr/include/string.h" 1 3 4  // expected-warning {{this style of line 
directive is a GNU extension}}
 extern "C" {
diff --git a/clang/unittests/AST/ByteCode/Descriptor.cpp 
b/clang/unittests/AST/ByteCode/Descriptor.cpp
index 37e6f24f6b409..3ce97f339ed29 100644
--- a/clang/unittests/AST/ByteCode/Descriptor.cpp
+++ b/clang/unittests/AST/ByteCode/Descriptor.cpp
@@ -1,5 +1,6 @@
 #include "../../../lib/AST/ByteCode/Descriptor.h"
 #include "../../../lib/AST/ByteCode/Context.h"
+#include "../../../lib/AST/ByteCode/Integral.h"
 #include "../../../lib/AST/ByteCode/Program.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
@@ -356,7 +357,7 @@ TEST(Descriptor, Primitives) {
     // Last element of the first dimension.
     const Pointer &PE1 = PF4.atIndex(0).narrow().atIndex(2);
     ASSERT_TRUE(PE1.isLive());
-    ASSERT_EQ(PE1.deref<short>(), 3);
+    ASSERT_EQ(static_cast<int>(PE1.deref<Integral<16, true>>()), 3);
     ASSERT_EQ(PE1.getArray(), NE1);
     ASSERT_EQ(PE1.getIndex(), 2u);
 
@@ -389,7 +390,7 @@ TEST(Descriptor, Primitives) {
     // Last element of the last dimension
     const Pointer &PE3 = PF4.atIndex(2).narrow().atIndex(2);
     ASSERT_TRUE(PE3.isLive());
-    ASSERT_EQ(PE3.deref<short>(), 9);
+    ASSERT_EQ(static_cast<int>(PE3.deref<Integral<16, true>>()), 9);
     ASSERT_EQ(PE3.getArray(), NE3);
     ASSERT_EQ(PE3.getIndex(), 2u);
   }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to