llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang <details> <summary>Changes</summary> Make sure we pass the expected bitwidth around when casting to IntAP/IntAPS and move the tests to their own file. This makes it easier to test the `IntegralAP` code for different bit widths than 128. --- Full diff: https://github.com/llvm/llvm-project/pull/68069.diff 8 Files Affected: - (modified) clang/lib/AST/Interp/ByteCodeExprGen.cpp (+12) - (modified) clang/lib/AST/Interp/Context.h (+2) - (modified) clang/lib/AST/Interp/IntegralAP.h (+9-7) - (modified) clang/lib/AST/Interp/Interp.h (+57-1) - (modified) clang/lib/AST/Interp/InterpBuiltin.cpp (+2-1) - (modified) clang/lib/AST/Interp/Opcodes.td (+28-2) - (added) clang/test/AST/Interp/intap.cpp (+83) - (modified) clang/test/AST/Interp/literals.cpp (-75) ``````````diff diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index e266804a4e75dea..f899c2fe22ecd8f 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -138,6 +138,13 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { if (!this->visit(SubExpr)) return false; + if (ToT == PT_IntAP) + return this->emitCastFloatingIntegralAP(Ctx.getBitWidth(CE->getType()), + CE); + if (ToT == PT_IntAPS) + return this->emitCastFloatingIntegralAPS(Ctx.getBitWidth(CE->getType()), + CE); + return this->emitCastFloatingIntegral(*ToT, CE); } @@ -183,6 +190,11 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { return true; } + if (ToT == PT_IntAP) + return this->emitCastAP(*FromT, Ctx.getBitWidth(CE->getType()), CE); + if (ToT == PT_IntAPS) + return this->emitCastAPS(*FromT, Ctx.getBitWidth(CE->getType()), CE); + return this->emitCast(*FromT, *ToT, CE); } diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h index 958b50b1615ad18..6df61e93ad83abc 100644 --- a/clang/lib/AST/Interp/Context.h +++ b/clang/lib/AST/Interp/Context.h @@ -64,6 +64,8 @@ class Context final { unsigned getCharBit() const; /// Return the floating-point semantics for T. const llvm::fltSemantics &getFloatSemantics(QualType T) const; + /// Return the size of T in bits. + uint32_t getBitWidth(QualType T) const { return Ctx.getIntWidth(T); } /// Classifies an expression. std::optional<PrimType> classify(QualType T) const; diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h index a8df431bef11784..f9a33bbcd7bd7fa 100644 --- a/clang/lib/AST/Interp/IntegralAP.h +++ b/clang/lib/AST/Interp/IntegralAP.h @@ -37,8 +37,12 @@ template <bool Signed> class IntegralAP final { APSInt V; template <typename T> static T truncateCast(const APSInt &V) { - return std::is_signed_v<T> ? V.trunc(sizeof(T) * 8).getSExtValue() - : V.trunc(sizeof(T) * 8).getZExtValue(); + constexpr unsigned BitSize = sizeof(T) * 8; + if (BitSize >= V.getBitWidth()) + return std::is_signed_v<T> ? V.getSExtValue() : V.getZExtValue(); + + return std::is_signed_v<T> ? V.trunc(BitSize).getSExtValue() + : V.trunc(BitSize).getZExtValue(); } public: @@ -89,10 +93,9 @@ template <bool Signed> class IntegralAP final { } template <unsigned Bits, bool InputSigned> - static IntegralAP from(Integral<Bits, InputSigned> I) { - // FIXME: Take bits parameter. + static IntegralAP from(Integral<Bits, InputSigned> I, unsigned BitWidth) { APSInt Copy = - APSInt(APInt(128, static_cast<int64_t>(I), InputSigned), !Signed); + APSInt(APInt(BitWidth, static_cast<int64_t>(I), InputSigned), !Signed); Copy.setIsSigned(Signed); assert(Copy.isSigned() == Signed); @@ -108,8 +111,7 @@ template <bool Signed> class IntegralAP final { return IntegralAP(0); } - // FIXME: This can't be static if the bitwidth depends on V. - static constexpr unsigned bitWidth() { return 128; } + constexpr unsigned bitWidth() const { return V.getBitWidth(); } APSInt toAPSInt(unsigned Bits = 0) const { return V; } APValue toAPValue() const { return APValue(V); } diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 9d5ec3315415cf7..d3ee28c0315cda8 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1561,6 +1561,22 @@ inline bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem, return true; } +/// Like Cast(), but we cast to an arbitrary-bitwidth integral, so we need +/// 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) { + S.Stk.push<IntegralAP<false>>( + IntegralAP<false>::from(S.Stk.pop<T>(), BitWidth)); + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +bool CastAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + S.Stk.push<IntegralAP<true>>( + IntegralAP<true>::from(S.Stk.pop<T>(), BitWidth)); + return true; +} + template <PrimType Name, class T = typename PrimConv<Name>::T> bool CastIntegralFloating(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem, @@ -1601,6 +1617,46 @@ bool CastFloatingIntegral(InterpState &S, CodePtr OpPC) { } } +static inline bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC, + uint32_t BitWidth) { + const Floating &F = S.Stk.pop<Floating>(); + + APSInt Result(BitWidth, /*IsUnsigned=*/true); + auto Status = F.convertToInteger(Result); + + // Float-to-Integral overflow check. + if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) { + const Expr *E = S.Current->getExpr(OpPC); + QualType Type = E->getType(); + + S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type; + return S.noteUndefinedBehavior(); + } + + S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result)); + return CheckFloatResult(S, OpPC, F, Status); +} + +static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, + uint32_t BitWidth) { + const Floating &F = S.Stk.pop<Floating>(); + + APSInt Result(BitWidth, /*IsUnsigned=*/false); + auto Status = F.convertToInteger(Result); + + // Float-to-Integral overflow check. + if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) { + const Expr *E = S.Current->getExpr(OpPC); + QualType Type = E->getType(); + + S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type; + return S.noteUndefinedBehavior(); + } + + S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result)); + return CheckFloatResult(S, OpPC, F, Status); +} + template <PrimType Name, class T = typename PrimConv<Name>::T> bool CastPointerIntegral(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop<Pointer>(); @@ -1690,7 +1746,7 @@ inline bool Shl(InterpState &S, CodePtr OpPC) { typename LT::AsUnsigned R; LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS), - LT::AsUnsigned::from(RHS), Bits, &R); + LT::AsUnsigned::from(RHS, Bits), Bits, &R); S.Stk.push<LT>(LT::from(R)); return true; } diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index bba0255219bc0d7..7552c1b88cff60c 100644 --- a/clang/lib/AST/Interp/InterpBuiltin.cpp +++ b/clang/lib/AST/Interp/InterpBuiltin.cpp @@ -41,7 +41,8 @@ static APSInt peekToAPSInt(InterpStack &Stk, PrimType T, size_t Offset = 0) { APSInt R; INT_TYPE_SWITCH(T, { T Val = Stk.peek<T>(Offset); - R = APSInt(APInt(T::bitWidth(), static_cast<uint64_t>(Val), T::isSigned())); + R = APSInt( + APInt(Val.bitWidth(), static_cast<uint64_t>(Val), T::isSigned())); }); return R; diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 9fc4938bb37bde8..ef6f730a21f027f 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -560,7 +560,7 @@ def FromCastTypeClass : TypeClass { } def ToCastTypeClass : TypeClass { - let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool, IntAP, IntAPS]; + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; } def Cast: Opcode { @@ -573,6 +573,22 @@ def CastFP : Opcode { let Args = [ArgFltSemantics, ArgRoundingMode]; } +def FixedSizeIntegralTypes : TypeClass { + let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool]; +} + +def CastAP : Opcode { + let Types = [AluTypeClass]; + let Args = [ArgUint32]; + let HasGroup = 1; +} + +def CastAPS : Opcode { + let Types = [AluTypeClass]; + let Args = [ArgUint32]; + let HasGroup = 1; +} + // Cast an integer to a floating type def CastIntegralFloating : Opcode { let Types = [AluTypeClass]; @@ -582,11 +598,21 @@ def CastIntegralFloating : Opcode { // Cast a floating to an integer type def CastFloatingIntegral : Opcode { - let Types = [AluTypeClass]; + let Types = [FixedSizeIntegralTypes]; let Args = []; let HasGroup = 1; } +def CastFloatingIntegralAP : Opcode { + let Types = []; + let Args = [ArgUint32]; +} + +def CastFloatingIntegralAPS : Opcode { + let Types = []; + let Args = [ArgUint32]; +} + def CastPointerIntegral : Opcode { let Types = [AluTypeClass]; let Args = []; diff --git a/clang/test/AST/Interp/intap.cpp b/clang/test/AST/Interp/intap.cpp new file mode 100644 index 000000000000000..8d8f3f27fc01672 --- /dev/null +++ b/clang/test/AST/Interp/intap.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -verify=ref %s +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s + +constexpr _BitInt(2) A = 0; +constexpr _BitInt(2) B = A + 1; +constexpr _BitInt(2) C = B + 1; // expected-warning {{from 2 to -2}} \ + // ref-warning {{from 2 to -2}} +static_assert(C == -2, ""); + + +#ifdef __SIZEOF_INT128__ +namespace i128 { + typedef __int128 int128_t; + typedef unsigned __int128 uint128_t; + constexpr int128_t I128_1 = 12; + static_assert(I128_1 == 12, ""); + static_assert(I128_1 != 10, ""); + static_assert(I128_1 != 12, ""); // expected-error{{failed}} \ + // ref-error{{failed}} \ + // expected-note{{evaluates to}} \ + // ref-note{{evaluates to}} + + static const __uint128_t UINT128_MAX =__uint128_t(__int128_t(-1L)); + static_assert(UINT128_MAX == -1, ""); + + static const __int128_t INT128_MAX = UINT128_MAX >> (__int128_t)1; + static_assert(INT128_MAX != 0, ""); + static const __int128_t INT128_MIN = -INT128_MAX - 1; + constexpr __int128 A = INT128_MAX + 1; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{outside the range}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{outside the range}} + constexpr int128_t Two = (int128_t)1 << 1ul; + static_assert(Two == 2, ""); + + constexpr uint128_t AllOnes = ~static_cast<uint128_t>(0); + static_assert(AllOnes == UINT128_MAX, ""); + +#if __cplusplus >= 201402L + template <typename T> + constexpr T CastFrom(__int128_t A) { + T B = (T)A; + return B; + } + static_assert(CastFrom<char>(12) == 12, ""); + static_assert(CastFrom<unsigned char>(12) == 12, ""); + static_assert(CastFrom<long>(12) == 12, ""); + static_assert(CastFrom<unsigned short>(12) == 12, ""); + static_assert(CastFrom<int128_t>(12) == 12, ""); + static_assert(CastFrom<float>(12) == 12, ""); + static_assert(CastFrom<double>(12) == 12, ""); + static_assert(CastFrom<long double>(12) == 12, ""); + + static_assert(CastFrom<char>(AllOnes) == -1, ""); + static_assert(CastFrom<unsigned char>(AllOnes) == 0xFF, ""); + static_assert(CastFrom<long>(AllOnes) == -1, ""); + static_assert(CastFrom<unsigned short>(AllOnes) == 0xFFFF, ""); + static_assert(CastFrom<int>(AllOnes) == -1, ""); + static_assert(CastFrom<int128_t>(AllOnes) == -1, ""); + static_assert(CastFrom<uint128_t>(AllOnes) == AllOnes, ""); + + template <typename T> + constexpr __int128 CastTo(T A) { + int128_t B = (int128_t)A; + return B; + } + static_assert(CastTo<char>(12) == 12, ""); + static_assert(CastTo<unsigned char>(12) == 12, ""); + static_assert(CastTo<long>(12) == 12, ""); + static_assert(CastTo<unsigned long long>(12) == 12, ""); + static_assert(CastTo<float>(12) == 12, ""); + static_assert(CastTo<double>(12) == 12, ""); + static_assert(CastTo<long double>(12) == 12, ""); +#endif + + constexpr int128_t Error = __LDBL_MAX__; // ref-warning {{implicit conversion of out of range value}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{is outside the range of representable values of type}} \ + // expected-warning {{implicit conversion of out of range value}} \ + // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{is outside the range of representable values of type}} +} +#endif diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp index 00875bcf44dc8e6..ceda59405ea9105 100644 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -26,81 +26,6 @@ static_assert(number != 10, ""); // expected-error{{failed}} \ // expected-note{{evaluates to}} \ // ref-note{{evaluates to}} - -#ifdef __SIZEOF_INT128__ -namespace i128 { - typedef __int128 int128_t; - typedef unsigned __int128 uint128_t; - constexpr int128_t I128_1 = 12; - static_assert(I128_1 == 12, ""); - static_assert(I128_1 != 10, ""); - static_assert(I128_1 != 12, ""); // expected-error{{failed}} \ - // ref-error{{failed}} \ - // expected-note{{evaluates to}} \ - // ref-note{{evaluates to}} - - static const __uint128_t UINT128_MAX =__uint128_t(__int128_t(-1L)); - static_assert(UINT128_MAX == -1, ""); - - static const __int128_t INT128_MAX = UINT128_MAX >> (__int128_t)1; - static_assert(INT128_MAX != 0, ""); - static const __int128_t INT128_MIN = -INT128_MAX - 1; - constexpr __int128 A = INT128_MAX + 1; // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{outside the range}} \ - // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{outside the range}} - constexpr int128_t Two = (int128_t)1 << 1ul; - static_assert(Two == 2, ""); - - constexpr uint128_t AllOnes = ~static_cast<uint128_t>(0); - static_assert(AllOnes == UINT128_MAX, ""); - -#if __cplusplus >= 201402L - template <typename T> - constexpr T CastFrom(__int128_t A) { - T B = (T)A; - return B; - } - static_assert(CastFrom<char>(12) == 12, ""); - static_assert(CastFrom<unsigned char>(12) == 12, ""); - static_assert(CastFrom<long>(12) == 12, ""); - static_assert(CastFrom<unsigned short>(12) == 12, ""); - static_assert(CastFrom<int128_t>(12) == 12, ""); - static_assert(CastFrom<float>(12) == 12, ""); - static_assert(CastFrom<double>(12) == 12, ""); - static_assert(CastFrom<long double>(12) == 12, ""); - - static_assert(CastFrom<char>(AllOnes) == -1, ""); - static_assert(CastFrom<unsigned char>(AllOnes) == 0xFF, ""); - static_assert(CastFrom<long>(AllOnes) == -1, ""); - static_assert(CastFrom<unsigned short>(AllOnes) == 0xFFFF, ""); - static_assert(CastFrom<int>(AllOnes) == -1, ""); - static_assert(CastFrom<int128_t>(AllOnes) == -1, ""); - static_assert(CastFrom<uint128_t>(AllOnes) == AllOnes, ""); - - template <typename T> - constexpr __int128 CastTo(T A) { - int128_t B = (int128_t)A; - return B; - } - static_assert(CastTo<char>(12) == 12, ""); - static_assert(CastTo<unsigned char>(12) == 12, ""); - static_assert(CastTo<long>(12) == 12, ""); - static_assert(CastTo<unsigned long long>(12) == 12, ""); - static_assert(CastTo<float>(12) == 12, ""); - static_assert(CastTo<double>(12) == 12, ""); - static_assert(CastTo<long double>(12) == 12, ""); -#endif - -constexpr int128_t Error = __LDBL_MAX__; // ref-warning {{implicit conversion of out of range value}} \ - // ref-error {{must be initialized by a constant expression}} \ - // ref-note {{is outside the range of representable values of type}} \ - // expected-warning {{implicit conversion of out of range value}} \ - // expected-error {{must be initialized by a constant expression}} \ - // expected-note {{is outside the range of representable values of type}} -} -#endif - constexpr bool b = number; static_assert(b, ""); constexpr int one = true; `````````` </details> https://github.com/llvm/llvm-project/pull/68069 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits