diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 9bd040a208b3f..cf82ec9c15eca 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -138,6 +138,13 @@ bool ByteCodeExprGen::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::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 958b50b1615ad..6df61e93ad83a 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 classify(QualType T) const; diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h index a8df431bef117..f9a33bbcd7bd7 100644 --- a/clang/lib/AST/Interp/IntegralAP.h +++ b/clang/lib/AST/Interp/IntegralAP.h @@ -37,8 +37,12 @@ template class IntegralAP final { APSInt V; template static T truncateCast(const APSInt &V) { - return std::is_signed_v ? 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 ? V.getSExtValue() : V.getZExtValue(); + + return std::is_signed_v ? V.trunc(BitSize).getSExtValue() + : V.trunc(BitSize).getZExtValue(); } public: @@ -89,10 +93,9 @@ template class IntegralAP final { } template - static IntegralAP from(Integral I) { - // FIXME: Take bits parameter. + static IntegralAP from(Integral I, unsigned BitWidth) { APSInt Copy = - APSInt(APInt(128, static_cast(I), InputSigned), !Signed); + APSInt(APInt(BitWidth, static_cast(I), InputSigned), !Signed); Copy.setIsSigned(Signed); assert(Copy.isSigned() == Signed); @@ -108,8 +111,7 @@ template 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 47dc1d08c9c4d..438cfb5a7eb90 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1568,6 +1568,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 ::T> +bool CastAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + S.Stk.push>( + IntegralAP::from(S.Stk.pop(), BitWidth)); + return true; +} + +template ::T> +bool CastAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) { + S.Stk.push>( + IntegralAP::from(S.Stk.pop(), BitWidth)); + return true; +} + template ::T> bool CastIntegralFloating(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem, @@ -1608,6 +1624,46 @@ bool CastFloatingIntegral(InterpState &S, CodePtr OpPC) { } } +static inline bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC, + uint32_t BitWidth) { + const Floating &F = S.Stk.pop(); + + 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(Result)); + return CheckFloatResult(S, OpPC, F, Status); +} + +static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC, + uint32_t BitWidth) { + const Floating &F = S.Stk.pop(); + + 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(Result)); + return CheckFloatResult(S, OpPC, F, Status); +} + template ::T> bool CastPointerIntegral(InterpState &S, CodePtr OpPC) { const Pointer &Ptr = S.Stk.pop(); @@ -1697,7 +1753,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::from(R)); return true; } diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp index bba0255219bc0..7552c1b88cff6 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(Offset); - R = APSInt(APInt(T::bitWidth(), static_cast(Val), T::isSigned())); + R = APSInt( + APInt(Val.bitWidth(), static_cast(Val), T::isSigned())); }); return R; diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td index 50b6c0ac154de..9d390fed15241 100644 --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -564,7 +564,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 { @@ -577,6 +577,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]; @@ -586,11 +602,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 index b3f02d2b76953..547e0a90d0da5 100644 --- a/clang/test/AST/Interp/intap.cpp +++ b/clang/test/AST/Interp/intap.cpp @@ -3,6 +3,21 @@ // RUN: %clang_cc1 -std=c++11 -fms-extensions -verify=ref %s // RUN: %clang_cc1 -std=c++20 -fms-extensions -verify=ref %s + +using MaxBitInt = _BitInt(8388608); + +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, ""); + + +constexpr MaxBitInt A_ = 0; +constexpr MaxBitInt B_ = A_ + 1; +static_assert(B_ == 1, ""); + + #ifdef __SIZEOF_INT128__ namespace i128 { typedef __int128 int128_t;