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 +}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits