Author: Timm Bäder Date: 2023-01-26T08:02:27+01:00 New Revision: 7f48154ca11fee6579fe29a51ab2a329424d31c4
URL: https://github.com/llvm/llvm-project/commit/7f48154ca11fee6579fe29a51ab2a329424d31c4 DIFF: https://github.com/llvm/llvm-project/commit/7f48154ca11fee6579fe29a51ab2a329424d31c4.diff LOG: Re-apply "[clang][Interp] Fix left-/right-shifting more than sizeof(unsigned)" Added: Modified: clang/lib/AST/Interp/Integral.h clang/lib/AST/Interp/Interp.h clang/test/AST/Interp/shifts.cpp Removed: ################################################################################ diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h index d5368167d967..0eb0990a0334 100644 --- a/clang/lib/AST/Interp/Integral.h +++ b/clang/lib/AST/Interp/Integral.h @@ -234,6 +234,18 @@ template <unsigned Bits, bool Signed> class Integral final { return false; } + template <unsigned RHSBits, bool RHSSign> + static void shiftLeft(const Integral A, const Integral<RHSBits, RHSSign> B, + unsigned OpBits, Integral *R) { + *R = Integral::from(A.V << B.V, OpBits); + } + + template <unsigned RHSBits, bool RHSSign> + static void shiftRight(const Integral A, const Integral<RHSBits, RHSSign> B, + 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>) { diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 466df04ac08d..b36b513c067f 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -1334,8 +1334,9 @@ inline bool Shr(InterpState &S, CodePtr OpPC) { if (!CheckShift<RT>(S, OpPC, RHS, Bits)) return false; - unsigned URHS = static_cast<unsigned>(RHS); - S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) >> URHS, LHS.bitWidth())); + Integral<LT::bitWidth(), false> R; + Integral<LT::bitWidth(), false>::shiftRight(LHS.toUnsigned(), RHS, Bits, &R); + S.Stk.push<LT>(R); return true; } @@ -1350,9 +1351,9 @@ inline bool Shl(InterpState &S, CodePtr OpPC) { if (!CheckShift<RT>(S, OpPC, RHS, Bits)) return false; - unsigned URHS = static_cast<unsigned>(RHS); - S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) << URHS, LHS.bitWidth())); - + Integral<LT::bitWidth(), false> R; + Integral<LT::bitWidth(), false>::shiftLeft(LHS.toUnsigned(), RHS, Bits, &R); + S.Stk.push<LT>(R); return true; } diff --git a/clang/test/AST/Interp/shifts.cpp b/clang/test/AST/Interp/shifts.cpp index 8fa78beb9302..3b2fc2619fe3 100644 --- a/clang/test/AST/Interp/shifts.cpp +++ b/clang/test/AST/Interp/shifts.cpp @@ -143,4 +143,13 @@ namespace shifts { constexpr char c2 = 1; constexpr int i3 = c2 << (__CHAR_BIT__ + 1); // Not ill-formed + + /// The purpose of these few lines is to test that we can shift more bits + /// than an unsigned *of the host* has. There was a bug where we casted + /// to host-unsigned. However, we cannot query what a host-unsigned even is + /// here, so only test this on platforms where `sizeof(long long) > sizeof(unsigned)`. + constexpr long long int L = 1; + constexpr signed int R = (sizeof(unsigned) * 8) + 1; + constexpr decltype(L) M = (R > 32 && R < 64) ? L << R : 0; + constexpr decltype(L) M2 = (R > 32 && R < 64) ? L >> R : 0; }; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits