[clang] [clang][bytecode] Support different integral types (e.g. addresses) (PR #185028)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 7 23:21:33 PST 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/185028
>From 3461922a082ad3d4fe488f1c23aee0e1b854b213 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
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 | 189 +++++++++----
clang/lib/AST/ByteCode/IntegralAP.h | 1 +
clang/lib/AST/ByteCode/Interp.cpp | 34 ++-
clang/lib/AST/ByteCode/Interp.h | 261 +++++++++++++++++-
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 | 23 +-
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, 892 insertions(+), 135 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..109aff980b924 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"
@@ -21,6 +22,8 @@
#include <cstddef>
#include <cstdint>
+#include "Descriptor.h"
+#include "InterpBlock.h"
#include "Primitives.h"
namespace clang {
@@ -64,13 +67,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 +95,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 +157,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 +166,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 +183,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 +233,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 +253,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 +272,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 +406,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..c31260d563cc1 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,26 @@ 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 +3632,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..6d58f5a24bd77 100644
--- a/clang/lib/AST/ByteCode/InterpStack.h
+++ b/clang/lib/AST/ByteCode/InterpStack.h
@@ -174,29 +174,24 @@ 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>>)
+ else if constexpr (std::is_same_v<T, Integral<16, true>>)
return PT_Sint16;
- else if constexpr (std::is_same_v<T, uint16_t> ||
- std::is_same_v<T, Integral<16, false>>)
+ else if constexpr (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);
}
More information about the cfe-commits
mailing list