[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