[clang] 6d965c9 - [clang][Interp] Implement left and right shifts

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 30 01:00:44 PDT 2022


Author: Timm Bäder
Date: 2022-10-30T08:59:55+01:00
New Revision: 6d965c94ba5806eb18c34cec9a8e9c4cb65c6885

URL: https://github.com/llvm/llvm-project/commit/6d965c94ba5806eb18c34cec9a8e9c4cb65c6885
DIFF: https://github.com/llvm/llvm-project/commit/6d965c94ba5806eb18c34cec9a8e9c4cb65c6885.diff

LOG: [clang][Interp] Implement left and right shifts

Differential Revision: https://reviews.llvm.org/D136532

Added: 
    clang/test/AST/Interp/shifts.cpp

Modified: 
    clang/lib/AST/Interp/ByteCodeExprGen.cpp
    clang/lib/AST/Interp/Interp.h
    clang/lib/AST/Interp/Opcodes.td

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 2edd16bc7e6c..f13cd7791a6c 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -237,6 +237,10 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
       return Discard(this->emitBitAnd(*T, BO));
     case BO_Or:
       return Discard(this->emitBitOr(*T, BO));
+    case BO_Shl:
+      return Discard(this->emitShl(*LT, *RT, BO));
+    case BO_Shr:
+      return Discard(this->emitShr(*LT, *RT, BO));
     case BO_LAnd:
     case BO_LOr:
     default:
@@ -451,7 +455,13 @@ bool ByteCodeExprGen<Emitter>::VisitCompoundAssignOperator(
   case BO_DivAssign:
   case BO_RemAssign:
   case BO_ShlAssign:
+    if (!this->emitShl(*LT, *RT, E))
+      return false;
+    break;
   case BO_ShrAssign:
+    if (!this->emitShr(*LT, *RT, E))
+      return false;
+    break;
   case BO_AndAssign:
   case BO_XorAssign:
   case BO_OrAssign:

diff  --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 81f833735258..aacd5209fcfd 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1103,84 +1103,63 @@ inline bool This(InterpState &S, CodePtr OpPC) {
 // Shr, Shl
 //===----------------------------------------------------------------------===//
 
-template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::T>
-unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) {
+template <PrimType NameL, PrimType NameR>
+inline bool Shr(InterpState &S, CodePtr OpPC) {
+  using LT = typename PrimConv<NameL>::T;
+  using RT = typename PrimConv<NameR>::T;
+  const auto &RHS = S.Stk.pop<RT>();
+  const auto &LHS = S.Stk.pop<LT>();
+  const unsigned Bits = LHS.bitWidth();
+
+  if (RHS.isNegative()) {
+    const SourceInfo &Loc = S.Current->getSource(OpPC);
+    S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
+    return false;
+  }
+
   // C++11 [expr.shift]p1: Shift width must be less than the bit width of
   // the shifted type.
-  if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) {
+  if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) {
     const Expr *E = S.Current->getExpr(OpPC);
-    const APSInt Val = V.toAPSInt();
+    const APSInt Val = RHS.toAPSInt();
     QualType Ty = E->getType();
     S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
-    return Bits;
-  } else {
-    return static_cast<unsigned>(V);
-  }
-}
-
-template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
-inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
-  if (RHS >= V.bitWidth()) {
-    S.Stk.push<T>(T::from(0, V.bitWidth()));
-  } else {
-    S.Stk.push<T>(T::from(V >> RHS, V.bitWidth()));
-  }
-  return true;
-}
-
-template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
-inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
-  if (V.isSigned() && !S.getLangOpts().CPlusPlus20) {
-    // C++11 [expr.shift]p2: A signed left shift must have a non-negative
-    // operand, and must not overflow the corresponding unsigned type.
-    // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to
-    // E1 x 2^E2 module 2^N.
-    if (V.isNegative()) {
-      const Expr *E = S.Current->getExpr(OpPC);
-      S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt();
-    } else if (V.countLeadingZeros() < RHS) {
-      S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards);
-    }
+    return false;
   }
 
-  if (V.bitWidth() == 1) {
-    S.Stk.push<T>(V);
-  } else if (RHS >= V.bitWidth()) {
-    S.Stk.push<T>(T::from(0, V.bitWidth()));
-  } else {
-    S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth()));
-  }
+  unsigned URHS = static_cast<unsigned>(RHS);
+  S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) >> URHS, LHS.bitWidth()));
   return true;
 }
 
-template <PrimType TL, PrimType TR>
-inline bool Shr(InterpState &S, CodePtr OpPC) {
-  const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
-  const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
+template <PrimType NameL, PrimType NameR>
+inline bool Shl(InterpState &S, CodePtr OpPC) {
+  using LT = typename PrimConv<NameL>::T;
+  using RT = typename PrimConv<NameR>::T;
+  const auto &RHS = S.Stk.pop<RT>();
+  const auto &LHS = S.Stk.pop<LT>();
   const unsigned Bits = LHS.bitWidth();
 
-  if (RHS.isSigned() && RHS.isNegative()) {
+  if (RHS.isNegative()) {
     const SourceInfo &Loc = S.Current->getSource(OpPC);
     S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
-    return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
-  } else {
-    return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
+    return false;
   }
-}
 
-template <PrimType TL, PrimType TR>
-inline bool Shl(InterpState &S, CodePtr OpPC) {
-  const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
-  const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
-  const unsigned Bits = LHS.bitWidth();
-
-  if (RHS.isSigned() && RHS.isNegative()) {
-    const SourceInfo &Loc = S.Current->getSource(OpPC);
-    S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
-    return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
-  } else {
-    return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
+  // C++11 [expr.shift]p1: Shift width must be less than the bit width of
+  // the shifted type.
+  if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) {
+    const Expr *E = S.Current->getExpr(OpPC);
+    const APSInt Val = RHS.toAPSInt();
+    QualType Ty = E->getType();
+    S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
+    return false;
   }
+
+  unsigned URHS = static_cast<unsigned>(RHS);
+  S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) << URHS, LHS.bitWidth()));
+
+  return true;
 }
 
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 6523f129bc45..73f0f6140f6b 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -402,6 +402,17 @@ def Rem : Opcode {
   let Types = [NumberTypeClass];
   let HasGroup = 1;
 }
+
+def Shl : Opcode {
+  let Types = [IntegerTypeClass, IntegerTypeClass];
+  let HasGroup = 1;
+}
+
+def Shr : Opcode {
+  let Types = [IntegerTypeClass, IntegerTypeClass];
+  let HasGroup = 1;
+}
+
 def BitAnd : IntegerOpcode;
 def BitOr : IntegerOpcode;
 def Div : Opcode {

diff  --git a/clang/test/AST/Interp/shifts.cpp b/clang/test/AST/Interp/shifts.cpp
new file mode 100644
index 000000000000..b1df7b85cc9f
--- /dev/null
+++ b/clang/test/AST/Interp/shifts.cpp
@@ -0,0 +1,142 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify %s
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify=cxx17 %s
+// RUN: %clang_cc1 -std=c++20 -verify=ref %s
+// RUN: %clang_cc1 -std=c++17 -verify=ref-cxx17 %s
+
+#define INT_MIN (~__INT_MAX__)
+
+
+namespace shifts {
+  constexpr void test() { // ref-error {{constexpr function never produces a constant expression}} \
+                          // ref-cxx17-error {{constexpr function never produces a constant expression}}
+
+    char c; // cxx17-warning {{uninitialized variable}} \
+            // ref-cxx17-warning {{uninitialized variable}}
+
+    c = 0 << 0;
+    c = 0 << 1;
+    c = 1 << 0;
+    c = 1 << -0;
+    c = 1 >> -0;
+    c = 1 << -1; // expected-warning {{shift count is negative}} \
+                 // cxx17-warning {{shift count is negative}} \
+                 // ref-warning {{shift count is negative}} \
+                 // ref-note {{negative shift count -1}} \
+                 // ref-cxx17-warning {{shift count is negative}} \
+                 // ref-cxx17-note {{negative shift count -1}}
+
+    c = 1 >> -1; // expected-warning {{shift count is negative}} \
+                 // cxx17-warning {{shift count is negative}} \
+                 // ref-warning {{shift count is negative}} \
+                 // ref-cxx17-warning {{shift count is negative}}
+    c = 1 << (unsigned)-1; // expected-warning {{shift count >= width of type}} \
+                           // FIXME: 'implicit conversion' warning missing in the new interpreter. \
+                           // cxx17-warning {{shift count >= width of type}} \
+                           // ref-warning {{shift count >= width of type}} \
+                           // ref-warning {{implicit conversion}} \
+                           // ref-cxx17-warning {{shift count >= width of type}} \
+                           // ref-cxx17-warning {{implicit conversion}}
+    c = 1 >> (unsigned)-1; // expected-warning {{shift count >= width of type}} \
+                           // cxx17-warning {{shift count >= width of type}} \
+                           // ref-warning {{shift count >= width of type}} \
+                           // ref-cxx17-warning {{shift count >= width of type}}
+    c = 1 << c;
+    c <<= 0;
+    c >>= 0;
+    c <<= 1;
+    c >>= 1;
+    c <<= -1; // expected-warning {{shift count is negative}} \
+              // cxx17-warning {{shift count is negative}} \
+              // ref-warning {{shift count is negative}} \
+              // ref-cxx17-warning {{shift count is negative}}
+    c >>= -1; // expected-warning {{shift count is negative}} \
+              // cxx17-warning {{shift count is negative}} \
+              // ref-warning {{shift count is negative}} \
+              // ref-cxx17-warning {{shift count is negative}}
+    c <<= 999999; // expected-warning {{shift count >= width of type}} \
+                  // cxx17-warning {{shift count >= width of type}} \
+                  // ref-warning {{shift count >= width of type}} \
+                  // ref-cxx17-warning {{shift count >= width of type}}
+    c >>= 999999; // expected-warning {{shift count >= width of type}} \
+                  // cxx17-warning {{shift count >= width of type}} \
+                  // ref-warning {{shift count >= width of type}} \
+                  // ref-cxx17-warning {{shift count >= width of type}}
+    c <<= __CHAR_BIT__; // expected-warning {{shift count >= width of type}} \
+                        // cxx17-warning {{shift count >= width of type}} \
+                        // ref-warning {{shift count >= width of type}} \
+                        // ref-cxx17-warning {{shift count >= width of type}}
+    c >>= __CHAR_BIT__; // expected-warning {{shift count >= width of type}} \
+                        // cxx17-warning {{shift count >= width of type}} \
+                        // ref-warning {{shift count >= width of type}} \
+                        // ref-cxx17-warning {{shift count >= width of type}}
+    c <<= __CHAR_BIT__+1; // expected-warning {{shift count >= width of type}} \
+                          // cxx17-warning {{shift count >= width of type}} \
+                          // ref-warning {{shift count >= width of type}} \
+                          // ref-cxx17-warning {{shift count >= width of type}}
+    c >>= __CHAR_BIT__+1; // expected-warning {{shift count >= width of type}} \
+                          // cxx17-warning {{shift count >= width of type}} \
+                          // ref-warning {{shift count >= width of type}} \
+                          // ref-cxx17-warning {{shift count >= width of type}}
+    (void)((long)c << __CHAR_BIT__);
+
+    int i; // cxx17-warning {{uninitialized variable}} \
+           // ref-cxx17-warning {{uninitialized variable}}
+    i = 1 << (__INT_WIDTH__ - 2);
+    i = 2 << (__INT_WIDTH__ - 1); // cxx17-warning {{bits to represent, but 'int' only has}} \
+                                  // ref-cxx17-warning {{bits to represent, but 'int' only has}}
+    i = 1 << (__INT_WIDTH__ - 1); // cxx17-warning-not {{sets the sign bit of the shift expression}}
+    i = -1 << (__INT_WIDTH__ - 1); // cxx17-warning {{shifting a negative signed value is undefined}} \
+                                   // ref-cxx17-warning {{shifting a negative signed value is undefined}}
+    i = -1 << 0; // cxx17-warning {{shifting a negative signed value is undefined}} \
+                 // ref-cxx17-warning {{shifting a negative signed value is undefined}}
+    i = 0 << (__INT_WIDTH__ - 1);
+    i = (char)1 << (__INT_WIDTH__ - 2);
+
+    unsigned u; // cxx17-warning {{uninitialized variable}} \
+                // ref-cxx17-warning {{uninitialized variable}}
+    u = 1U << (__INT_WIDTH__ - 1);
+    u = 5U << (__INT_WIDTH__ - 1);
+
+    long long int lli; // cxx17-warning {{uninitialized variable}} \
+                       // ref-cxx17-warning {{uninitialized variable}}
+    lli = INT_MIN << 2; // cxx17-warning {{shifting a negative signed value is undefined}} \
+                        // ref-cxx17-warning {{shifting a negative signed value is undefined}}
+    lli = 1LL << (sizeof(long long) * __CHAR_BIT__ - 2);
+  }
+
+  static_assert(1 << 4 == 16, "");
+  constexpr unsigned m = 2 >> 1;
+  static_assert(m == 1, "");
+  constexpr unsigned char c = 0 << 8;
+  static_assert(c == 0, "");
+  static_assert(true << 1, "");
+  static_assert(1 << (__INT_WIDTH__ +1) == 0, "");  // expected-error {{not an integral constant expression}} \
+                                                    // expected-note {{>= width of type 'int'}} \
+                                                    // cxx17-error {{not an integral constant expression}} \
+                                                    // cxx17-note {{>= width of type 'int'}} \
+                                                    // ref-error {{not an integral constant expression}} \
+                                                    // ref-note {{>= width of type 'int'}} \
+                                                    // ref-cxx17-error {{not an integral constant expression}} \
+                                                    // ref-cxx17-note {{>= width of type 'int'}}
+
+  constexpr int i1 = 1 << -1; // expected-error {{must be initialized by a constant expression}} \
+                              // expected-note {{negative shift count -1}} \
+                              // cxx17-error {{must be initialized by a constant expression}} \
+                              // cxx17-note {{negative shift count -1}} \
+                              // ref-error {{must be initialized by a constant expression}} \
+                              // ref-note {{negative shift count -1}} \
+                              // ref-cxx17-error {{must be initialized by a constant expression}} \
+                              // ref-cxx17-note {{negative shift count -1}}
+
+  constexpr int i2 = 1 << (__INT_WIDTH__ + 1); // expected-error {{must be initialized by a constant expression}} \
+                                               // expected-note {{>= width of type}} \
+                                               // cxx17-error {{must be initialized by a constant expression}} \
+                                               // cxx17-note {{>= width of type}} \
+                                               // ref-error {{must be initialized by a constant expression}} \
+                                               // ref-note {{>= width of type}} \
+                                               // ref-cxx17-error {{must be initialized by a constant expression}} \
+                                               // ref-cxx17-note {{>= width of type}}
+
+  constexpr char c2 = 1;
+  constexpr int i3 = c2 << (__CHAR_BIT__ + 1); // Not ill-formed
+};


        


More information about the cfe-commits mailing list