Re-apply "[clang][Interp] Fix left-/right-shifting more than sizeof(unsigned)"
authorTimm Bäder <tbaeder@redhat.com>
Thu, 26 Jan 2023 06:50:14 +0000 (07:50 +0100)
committerTimm Bäder <tbaeder@redhat.com>
Thu, 26 Jan 2023 07:02:27 +0000 (08:02 +0100)
clang/lib/AST/Interp/Integral.h
clang/lib/AST/Interp/Interp.h
clang/test/AST/Interp/shifts.cpp

index d536816..0eb0990 100644 (file)
@@ -234,6 +234,18 @@ public:
     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>) {
index 466df04..b36b513 100644 (file)
@@ -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;
 }
 
index 8fa78be..3b2fc26 100644 (file)
@@ -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;
 };