https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/185028
>From 4badbdb496025a5508efad3c7fc01db897b7757c 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 | 225 +++++++++++++++++- 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 | 8 + 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 + 39 files changed, 850 insertions(+), 128 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..fb56f9415ed15 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 !S.getLangOpts().CPlusPlus || !Ptr.pointsToLabel(); + + 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..4feb2457db4cd 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,49 @@ 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(); + } + llvm::errs() << __PRETTY_FUNCTION__ << '\n'; + + S.Stk.push<T>(Kind, Ptr, Op<int32_t>()(Offset, Number)); + S.Stk.dump(); + 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 +387,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 +436,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); } @@ -1049,6 +1126,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 +1459,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; } @@ -2405,7 +2505,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 +2551,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 +2640,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 +2694,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 +2713,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 +2822,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 +3435,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 +3598,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..4e3a5cd295458 100644 --- a/clang/test/AST/ByteCode/builtin-bit-cast.cpp +++ b/clang/test/AST/ByteCode/builtin-bit-cast.cpp @@ -590,3 +590,11 @@ 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)); + } + static_assert(fn() == 1); // both-error {{not an integral constant expression}} +} 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" { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
