From 7f48154ca11fee6579fe29a51ab2a329424d31c4 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Timm=20B=C3=A4der?= Date: Thu, 26 Jan 2023 07:50:14 +0100 Subject: [PATCH] Re-apply "[clang][Interp] Fix left-/right-shifting more than sizeof(unsigned)" --- clang/lib/AST/Interp/Integral.h | 12 ++++++++++++ clang/lib/AST/Interp/Interp.h | 11 ++++++----- clang/test/AST/Interp/shifts.cpp | 9 +++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/clang/lib/AST/Interp/Integral.h b/clang/lib/AST/Interp/Integral.h index d536816..0eb0990 100644 --- a/clang/lib/AST/Interp/Integral.h +++ b/clang/lib/AST/Interp/Integral.h @@ -234,6 +234,18 @@ public: return false; } + template + static void shiftLeft(const Integral A, const Integral B, + unsigned OpBits, Integral *R) { + *R = Integral::from(A.V << B.V, OpBits); + } + + template + static void shiftRight(const Integral A, const Integral B, + unsigned OpBits, Integral *R) { + *R = Integral::from(A.V >> B.V, OpBits); + } + private: template static bool CheckAddUB(T A, T B, T &R) { if constexpr (std::is_signed_v) { diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 466df04..b36b513 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(S, OpPC, RHS, Bits)) return false; - unsigned URHS = static_cast(RHS); - S.Stk.push(LT::from(static_cast(LHS) >> URHS, LHS.bitWidth())); + Integral R; + Integral::shiftRight(LHS.toUnsigned(), RHS, Bits, &R); + S.Stk.push(R); return true; } @@ -1350,9 +1351,9 @@ inline bool Shl(InterpState &S, CodePtr OpPC) { if (!CheckShift(S, OpPC, RHS, Bits)) return false; - unsigned URHS = static_cast(RHS); - S.Stk.push(LT::from(static_cast(LHS) << URHS, LHS.bitWidth())); - + Integral R; + Integral::shiftLeft(LHS.toUnsigned(), RHS, Bits, &R); + S.Stk.push(R); return true; } diff --git a/clang/test/AST/Interp/shifts.cpp b/clang/test/AST/Interp/shifts.cpp index 8fa78be..3b2fc26 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; }; -- 2.7.4