Skip to content

Commit 6d965c9

Browse files
committed
[clang][Interp] Implement left and right shifts
Differential Revision: https://reviews.llvm.org/D136532
1 parent 62efe45 commit 6d965c9

File tree

4 files changed

+203
-61
lines changed

4 files changed

+203
-61
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
237237
return Discard(this->emitBitAnd(*T, BO));
238238
case BO_Or:
239239
return Discard(this->emitBitOr(*T, BO));
240+
case BO_Shl:
241+
return Discard(this->emitShl(*LT, *RT, BO));
242+
case BO_Shr:
243+
return Discard(this->emitShr(*LT, *RT, BO));
240244
case BO_LAnd:
241245
case BO_LOr:
242246
default:
@@ -451,7 +455,13 @@ bool ByteCodeExprGen<Emitter>::VisitCompoundAssignOperator(
451455
case BO_DivAssign:
452456
case BO_RemAssign:
453457
case BO_ShlAssign:
458+
if (!this->emitShl(*LT, *RT, E))
459+
return false;
460+
break;
454461
case BO_ShrAssign:
462+
if (!this->emitShr(*LT, *RT, E))
463+
return false;
464+
break;
455465
case BO_AndAssign:
456466
case BO_XorAssign:
457467
case BO_OrAssign:

clang/lib/AST/Interp/Interp.h

Lines changed: 40 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,84 +1103,63 @@ inline bool This(InterpState &S, CodePtr OpPC) {
11031103
// Shr, Shl
11041104
//===----------------------------------------------------------------------===//
11051105

1106-
template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::T>
1107-
unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) {
1106+
template <PrimType NameL, PrimType NameR>
1107+
inline bool Shr(InterpState &S, CodePtr OpPC) {
1108+
using LT = typename PrimConv<NameL>::T;
1109+
using RT = typename PrimConv<NameR>::T;
1110+
const auto &RHS = S.Stk.pop<RT>();
1111+
const auto &LHS = S.Stk.pop<LT>();
1112+
const unsigned Bits = LHS.bitWidth();
1113+
1114+
if (RHS.isNegative()) {
1115+
const SourceInfo &Loc = S.Current->getSource(OpPC);
1116+
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
1117+
return false;
1118+
}
1119+
11081120
// C++11 [expr.shift]p1: Shift width must be less than the bit width of
11091121
// the shifted type.
1110-
if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) {
1122+
if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) {
11111123
const Expr *E = S.Current->getExpr(OpPC);
1112-
const APSInt Val = V.toAPSInt();
1124+
const APSInt Val = RHS.toAPSInt();
11131125
QualType Ty = E->getType();
11141126
S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
1115-
return Bits;
1116-
} else {
1117-
return static_cast<unsigned>(V);
1118-
}
1119-
}
1120-
1121-
template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
1122-
inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
1123-
if (RHS >= V.bitWidth()) {
1124-
S.Stk.push<T>(T::from(0, V.bitWidth()));
1125-
} else {
1126-
S.Stk.push<T>(T::from(V >> RHS, V.bitWidth()));
1127-
}
1128-
return true;
1129-
}
1130-
1131-
template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
1132-
inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
1133-
if (V.isSigned() && !S.getLangOpts().CPlusPlus20) {
1134-
// C++11 [expr.shift]p2: A signed left shift must have a non-negative
1135-
// operand, and must not overflow the corresponding unsigned type.
1136-
// C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to
1137-
// E1 x 2^E2 module 2^N.
1138-
if (V.isNegative()) {
1139-
const Expr *E = S.Current->getExpr(OpPC);
1140-
S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt();
1141-
} else if (V.countLeadingZeros() < RHS) {
1142-
S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards);
1143-
}
1127+
return false;
11441128
}
11451129

1146-
if (V.bitWidth() == 1) {
1147-
S.Stk.push<T>(V);
1148-
} else if (RHS >= V.bitWidth()) {
1149-
S.Stk.push<T>(T::from(0, V.bitWidth()));
1150-
} else {
1151-
S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth()));
1152-
}
1130+
unsigned URHS = static_cast<unsigned>(RHS);
1131+
S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) >> URHS, LHS.bitWidth()));
11531132
return true;
11541133
}
11551134

1156-
template <PrimType TL, PrimType TR>
1157-
inline bool Shr(InterpState &S, CodePtr OpPC) {
1158-
const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
1159-
const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
1135+
template <PrimType NameL, PrimType NameR>
1136+
inline bool Shl(InterpState &S, CodePtr OpPC) {
1137+
using LT = typename PrimConv<NameL>::T;
1138+
using RT = typename PrimConv<NameR>::T;
1139+
const auto &RHS = S.Stk.pop<RT>();
1140+
const auto &LHS = S.Stk.pop<LT>();
11601141
const unsigned Bits = LHS.bitWidth();
11611142

1162-
if (RHS.isSigned() && RHS.isNegative()) {
1143+
if (RHS.isNegative()) {
11631144
const SourceInfo &Loc = S.Current->getSource(OpPC);
11641145
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
1165-
return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
1166-
} else {
1167-
return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
1146+
return false;
11681147
}
1169-
}
11701148

1171-
template <PrimType TL, PrimType TR>
1172-
inline bool Shl(InterpState &S, CodePtr OpPC) {
1173-
const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
1174-
const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
1175-
const unsigned Bits = LHS.bitWidth();
1176-
1177-
if (RHS.isSigned() && RHS.isNegative()) {
1178-
const SourceInfo &Loc = S.Current->getSource(OpPC);
1179-
S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
1180-
return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
1181-
} else {
1182-
return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
1149+
// C++11 [expr.shift]p1: Shift width must be less than the bit width of
1150+
// the shifted type.
1151+
if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) {
1152+
const Expr *E = S.Current->getExpr(OpPC);
1153+
const APSInt Val = RHS.toAPSInt();
1154+
QualType Ty = E->getType();
1155+
S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
1156+
return false;
11831157
}
1158+
1159+
unsigned URHS = static_cast<unsigned>(RHS);
1160+
S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) << URHS, LHS.bitWidth()));
1161+
1162+
return true;
11841163
}
11851164

11861165
//===----------------------------------------------------------------------===//

clang/lib/AST/Interp/Opcodes.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,17 @@ def Rem : Opcode {
402402
let Types = [NumberTypeClass];
403403
let HasGroup = 1;
404404
}
405+
406+
def Shl : Opcode {
407+
let Types = [IntegerTypeClass, IntegerTypeClass];
408+
let HasGroup = 1;
409+
}
410+
411+
def Shr : Opcode {
412+
let Types = [IntegerTypeClass, IntegerTypeClass];
413+
let HasGroup = 1;
414+
}
415+
405416
def BitAnd : IntegerOpcode;
406417
def BitOr : IntegerOpcode;
407418
def Div : Opcode {

clang/test/AST/Interp/shifts.cpp

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++20 -verify %s
2+
// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -std=c++17 -verify=cxx17 %s
3+
// RUN: %clang_cc1 -std=c++20 -verify=ref %s
4+
// RUN: %clang_cc1 -std=c++17 -verify=ref-cxx17 %s
5+
6+
#define INT_MIN (~__INT_MAX__)
7+
8+
9+
namespace shifts {
10+
constexpr void test() { // ref-error {{constexpr function never produces a constant expression}} \
11+
// ref-cxx17-error {{constexpr function never produces a constant expression}}
12+
13+
char c; // cxx17-warning {{uninitialized variable}} \
14+
// ref-cxx17-warning {{uninitialized variable}}
15+
16+
c = 0 << 0;
17+
c = 0 << 1;
18+
c = 1 << 0;
19+
c = 1 << -0;
20+
c = 1 >> -0;
21+
c = 1 << -1; // expected-warning {{shift count is negative}} \
22+
// cxx17-warning {{shift count is negative}} \
23+
// ref-warning {{shift count is negative}} \
24+
// ref-note {{negative shift count -1}} \
25+
// ref-cxx17-warning {{shift count is negative}} \
26+
// ref-cxx17-note {{negative shift count -1}}
27+
28+
c = 1 >> -1; // expected-warning {{shift count is negative}} \
29+
// cxx17-warning {{shift count is negative}} \
30+
// ref-warning {{shift count is negative}} \
31+
// ref-cxx17-warning {{shift count is negative}}
32+
c = 1 << (unsigned)-1; // expected-warning {{shift count >= width of type}} \
33+
// FIXME: 'implicit conversion' warning missing in the new interpreter. \
34+
// cxx17-warning {{shift count >= width of type}} \
35+
// ref-warning {{shift count >= width of type}} \
36+
// ref-warning {{implicit conversion}} \
37+
// ref-cxx17-warning {{shift count >= width of type}} \
38+
// ref-cxx17-warning {{implicit conversion}}
39+
c = 1 >> (unsigned)-1; // expected-warning {{shift count >= width of type}} \
40+
// cxx17-warning {{shift count >= width of type}} \
41+
// ref-warning {{shift count >= width of type}} \
42+
// ref-cxx17-warning {{shift count >= width of type}}
43+
c = 1 << c;
44+
c <<= 0;
45+
c >>= 0;
46+
c <<= 1;
47+
c >>= 1;
48+
c <<= -1; // expected-warning {{shift count is negative}} \
49+
// cxx17-warning {{shift count is negative}} \
50+
// ref-warning {{shift count is negative}} \
51+
// ref-cxx17-warning {{shift count is negative}}
52+
c >>= -1; // expected-warning {{shift count is negative}} \
53+
// cxx17-warning {{shift count is negative}} \
54+
// ref-warning {{shift count is negative}} \
55+
// ref-cxx17-warning {{shift count is negative}}
56+
c <<= 999999; // expected-warning {{shift count >= width of type}} \
57+
// cxx17-warning {{shift count >= width of type}} \
58+
// ref-warning {{shift count >= width of type}} \
59+
// ref-cxx17-warning {{shift count >= width of type}}
60+
c >>= 999999; // expected-warning {{shift count >= width of type}} \
61+
// cxx17-warning {{shift count >= width of type}} \
62+
// ref-warning {{shift count >= width of type}} \
63+
// ref-cxx17-warning {{shift count >= width of type}}
64+
c <<= __CHAR_BIT__; // expected-warning {{shift count >= width of type}} \
65+
// cxx17-warning {{shift count >= width of type}} \
66+
// ref-warning {{shift count >= width of type}} \
67+
// ref-cxx17-warning {{shift count >= width of type}}
68+
c >>= __CHAR_BIT__; // expected-warning {{shift count >= width of type}} \
69+
// cxx17-warning {{shift count >= width of type}} \
70+
// ref-warning {{shift count >= width of type}} \
71+
// ref-cxx17-warning {{shift count >= width of type}}
72+
c <<= __CHAR_BIT__+1; // expected-warning {{shift count >= width of type}} \
73+
// cxx17-warning {{shift count >= width of type}} \
74+
// ref-warning {{shift count >= width of type}} \
75+
// ref-cxx17-warning {{shift count >= width of type}}
76+
c >>= __CHAR_BIT__+1; // expected-warning {{shift count >= width of type}} \
77+
// cxx17-warning {{shift count >= width of type}} \
78+
// ref-warning {{shift count >= width of type}} \
79+
// ref-cxx17-warning {{shift count >= width of type}}
80+
(void)((long)c << __CHAR_BIT__);
81+
82+
int i; // cxx17-warning {{uninitialized variable}} \
83+
// ref-cxx17-warning {{uninitialized variable}}
84+
i = 1 << (__INT_WIDTH__ - 2);
85+
i = 2 << (__INT_WIDTH__ - 1); // cxx17-warning {{bits to represent, but 'int' only has}} \
86+
// ref-cxx17-warning {{bits to represent, but 'int' only has}}
87+
i = 1 << (__INT_WIDTH__ - 1); // cxx17-warning-not {{sets the sign bit of the shift expression}}
88+
i = -1 << (__INT_WIDTH__ - 1); // cxx17-warning {{shifting a negative signed value is undefined}} \
89+
// ref-cxx17-warning {{shifting a negative signed value is undefined}}
90+
i = -1 << 0; // cxx17-warning {{shifting a negative signed value is undefined}} \
91+
// ref-cxx17-warning {{shifting a negative signed value is undefined}}
92+
i = 0 << (__INT_WIDTH__ - 1);
93+
i = (char)1 << (__INT_WIDTH__ - 2);
94+
95+
unsigned u; // cxx17-warning {{uninitialized variable}} \
96+
// ref-cxx17-warning {{uninitialized variable}}
97+
u = 1U << (__INT_WIDTH__ - 1);
98+
u = 5U << (__INT_WIDTH__ - 1);
99+
100+
long long int lli; // cxx17-warning {{uninitialized variable}} \
101+
// ref-cxx17-warning {{uninitialized variable}}
102+
lli = INT_MIN << 2; // cxx17-warning {{shifting a negative signed value is undefined}} \
103+
// ref-cxx17-warning {{shifting a negative signed value is undefined}}
104+
lli = 1LL << (sizeof(long long) * __CHAR_BIT__ - 2);
105+
}
106+
107+
static_assert(1 << 4 == 16, "");
108+
constexpr unsigned m = 2 >> 1;
109+
static_assert(m == 1, "");
110+
constexpr unsigned char c = 0 << 8;
111+
static_assert(c == 0, "");
112+
static_assert(true << 1, "");
113+
static_assert(1 << (__INT_WIDTH__ +1) == 0, ""); // expected-error {{not an integral constant expression}} \
114+
// expected-note {{>= width of type 'int'}} \
115+
// cxx17-error {{not an integral constant expression}} \
116+
// cxx17-note {{>= width of type 'int'}} \
117+
// ref-error {{not an integral constant expression}} \
118+
// ref-note {{>= width of type 'int'}} \
119+
// ref-cxx17-error {{not an integral constant expression}} \
120+
// ref-cxx17-note {{>= width of type 'int'}}
121+
122+
constexpr int i1 = 1 << -1; // expected-error {{must be initialized by a constant expression}} \
123+
// expected-note {{negative shift count -1}} \
124+
// cxx17-error {{must be initialized by a constant expression}} \
125+
// cxx17-note {{negative shift count -1}} \
126+
// ref-error {{must be initialized by a constant expression}} \
127+
// ref-note {{negative shift count -1}} \
128+
// ref-cxx17-error {{must be initialized by a constant expression}} \
129+
// ref-cxx17-note {{negative shift count -1}}
130+
131+
constexpr int i2 = 1 << (__INT_WIDTH__ + 1); // expected-error {{must be initialized by a constant expression}} \
132+
// expected-note {{>= width of type}} \
133+
// cxx17-error {{must be initialized by a constant expression}} \
134+
// cxx17-note {{>= width of type}} \
135+
// ref-error {{must be initialized by a constant expression}} \
136+
// ref-note {{>= width of type}} \
137+
// ref-cxx17-error {{must be initialized by a constant expression}} \
138+
// ref-cxx17-note {{>= width of type}}
139+
140+
constexpr char c2 = 1;
141+
constexpr int i3 = c2 << (__CHAR_BIT__ + 1); // Not ill-formed
142+
};

0 commit comments

Comments
 (0)