Author: Timm Baeder Date: 2023-10-11T08:53:21+02:00 New Revision: 26d9f851cfddf2d6b57239a7ba054d027756bfb7
URL: https://github.com/llvm/llvm-project/commit/26d9f851cfddf2d6b57239a7ba054d027756bfb7 DIFF: https://github.com/llvm/llvm-project/commit/26d9f851cfddf2d6b57239a7ba054d027756bfb7.diff LOG: [clang][Interp] Add basic support for _BitInt (#68069) Make sure we pass the expected bitwidth around when casting to IntAP/IntAPS. This makes it easier to test the `IntegralAP` code for different bit widths than 128. Added: Modified: clang/lib/AST/Interp/ByteCodeExprGen.cpp clang/lib/AST/Interp/Context.h clang/lib/AST/Interp/IntegralAP.h clang/lib/AST/Interp/Interp.h clang/lib/AST/Interp/InterpBuiltin.cpp clang/lib/AST/Interp/Opcodes.td clang/test/AST/Interp/intap.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index e7a431ddee6f002..2b745d6a1509868 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 25ce93ac03d3733..1ad3b8bfc7711d3 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 <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, @@ -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<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>(); @@ -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>(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 50b6c0ac154de30..9d390fed152417f 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 b3f02d2b769531d..547e0a90d0da5de 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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits