Skip to content

Commit 90e3bc9

Browse files
committed
[clang][bytecode] Fix unsigned wraparound behavior with bitfields
and inc/dec operations. We need to truncate the new value to the bitfield bitwidth.
1 parent bffdf0b commit 90e3bc9

File tree

5 files changed

+208
-6
lines changed

5 files changed

+208
-6
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6438,6 +6438,13 @@ bool Compiler<Emitter>::visitFunc(const FunctionDecl *F) {
64386438
return this->emitNoRet(SourceInfo{});
64396439
}
64406440

6441+
static uint32_t getBitWidth(const Expr *E) {
6442+
assert(E->refersToBitField());
6443+
const auto *ME = cast<MemberExpr>(E);
6444+
const auto *FD = cast<FieldDecl>(ME->getMemberDecl());
6445+
return FD->getBitWidthValue();
6446+
}
6447+
64416448
template <class Emitter>
64426449
bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
64436450
const Expr *SubExpr = E->getSubExpr();
@@ -6466,10 +6473,15 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
64666473
return DiscardResult ? this->emitPopPtr(E) : true;
64676474
}
64686475

6469-
if (T == PT_Float) {
6476+
if (T == PT_Float)
64706477
return DiscardResult ? this->emitIncfPop(getFPOptions(E), E)
64716478
: this->emitIncf(getFPOptions(E), E);
6472-
}
6479+
6480+
if (SubExpr->refersToBitField())
6481+
return DiscardResult ? this->emitIncPopBitfield(*T, E->canOverflow(),
6482+
getBitWidth(SubExpr), E)
6483+
: this->emitIncBitfield(*T, E->canOverflow(),
6484+
getBitWidth(SubExpr), E);
64736485

64746486
return DiscardResult ? this->emitIncPop(*T, E->canOverflow(), E)
64756487
: this->emitInc(*T, E->canOverflow(), E);
@@ -6490,9 +6502,15 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
64906502
return DiscardResult ? this->emitPopPtr(E) : true;
64916503
}
64926504

6493-
if (T == PT_Float) {
6505+
if (T == PT_Float)
64946506
return DiscardResult ? this->emitDecfPop(getFPOptions(E), E)
64956507
: this->emitDecf(getFPOptions(E), E);
6508+
6509+
if (SubExpr->refersToBitField()) {
6510+
return DiscardResult ? this->emitDecPopBitfield(*T, E->canOverflow(),
6511+
getBitWidth(SubExpr), E)
6512+
: this->emitDecBitfield(*T, E->canOverflow(),
6513+
getBitWidth(SubExpr), E);
64966514
}
64976515

64986516
return DiscardResult ? this->emitDecPop(*T, E->canOverflow(), E)
@@ -6521,6 +6539,11 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
65216539
if (DiscardResult) {
65226540
if (T == PT_Float)
65236541
return this->emitIncfPop(getFPOptions(E), E);
6542+
if (SubExpr->refersToBitField())
6543+
return DiscardResult ? this->emitIncPopBitfield(*T, E->canOverflow(),
6544+
getBitWidth(SubExpr), E)
6545+
: this->emitIncBitfield(*T, E->canOverflow(),
6546+
getBitWidth(SubExpr), E);
65246547
return this->emitIncPop(*T, E->canOverflow(), E);
65256548
}
65266549

@@ -6536,6 +6559,11 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
65366559
return false;
65376560
if (!this->emitStoreFloat(E))
65386561
return false;
6562+
} else if (SubExpr->refersToBitField()) {
6563+
assert(isIntegralType(*T));
6564+
if (!this->emitPreIncBitfield(*T, E->canOverflow(), getBitWidth(SubExpr),
6565+
E))
6566+
return false;
65396567
} else {
65406568
assert(isIntegralType(*T));
65416569
if (!this->emitPreInc(*T, E->canOverflow(), E))
@@ -6566,6 +6594,11 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
65666594
if (DiscardResult) {
65676595
if (T == PT_Float)
65686596
return this->emitDecfPop(getFPOptions(E), E);
6597+
if (SubExpr->refersToBitField())
6598+
return DiscardResult ? this->emitDecPopBitfield(*T, E->canOverflow(),
6599+
getBitWidth(SubExpr), E)
6600+
: this->emitDecBitfield(*T, E->canOverflow(),
6601+
getBitWidth(SubExpr), E);
65696602
return this->emitDecPop(*T, E->canOverflow(), E);
65706603
}
65716604

@@ -6581,6 +6614,11 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
65816614
return false;
65826615
if (!this->emitStoreFloat(E))
65836616
return false;
6617+
} else if (SubExpr->refersToBitField()) {
6618+
assert(isIntegralType(*T));
6619+
if (!this->emitPreDecBitfield(*T, E->canOverflow(), getBitWidth(SubExpr),
6620+
E))
6621+
return false;
65846622
} else {
65856623
assert(isIntegralType(*T));
65866624
if (!this->emitPreDec(*T, E->canOverflow(), E))

clang/lib/AST/ByteCode/Interp.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,6 +1610,8 @@ bool Call(InterpState &S, CodePtr OpPC, const Function *Func,
16101610
if (!CheckCallDepth(S, OpPC))
16111611
return cleanup();
16121612

1613+
// Func->dump();
1614+
16131615
auto NewFrame = std::make_unique<InterpFrame>(S, Func, OpPC, VarArgSize);
16141616
InterpFrame *FrameBefore = S.Current;
16151617
S.Current = NewFrame.get();

clang/lib/AST/ByteCode/Interp.h

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ enum class IncDecOp {
702702

703703
template <typename T, IncDecOp Op, PushVal DoPush>
704704
bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
705-
bool CanOverflow) {
705+
bool CanOverflow, UnsignedOrNone BitWidth = std::nullopt) {
706706
assert(!Ptr.isDummy());
707707

708708
if (!S.inConstantContext()) {
@@ -725,12 +725,18 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
725725

726726
if constexpr (Op == IncDecOp::Inc) {
727727
if (!T::increment(Value, &Result) || !CanOverflow) {
728-
Ptr.deref<T>() = Result;
728+
if (BitWidth)
729+
Ptr.deref<T>() = Result.truncate(*BitWidth);
730+
else
731+
Ptr.deref<T>() = Result;
729732
return true;
730733
}
731734
} else {
732735
if (!T::decrement(Value, &Result) || !CanOverflow) {
733-
Ptr.deref<T>() = Result;
736+
if (BitWidth)
737+
Ptr.deref<T>() = Result.truncate(*BitWidth);
738+
else
739+
Ptr.deref<T>() = Result;
734740
return true;
735741
}
736742
}
@@ -774,6 +780,17 @@ bool Inc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
774780
CanOverflow);
775781
}
776782

783+
template <PrimType Name, class T = typename PrimConv<Name>::T>
784+
bool IncBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
785+
unsigned BitWidth) {
786+
const Pointer &Ptr = S.Stk.pop<Pointer>();
787+
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
788+
return false;
789+
790+
return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr, CanOverflow,
791+
BitWidth);
792+
}
793+
777794
/// 1) Pops a pointer from the stack
778795
/// 2) Load the value from the pointer
779796
/// 3) Writes the value increased by one back to the pointer
@@ -786,6 +803,17 @@ bool IncPop(InterpState &S, CodePtr OpPC, bool CanOverflow) {
786803
return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow);
787804
}
788805

806+
template <PrimType Name, class T = typename PrimConv<Name>::T>
807+
bool IncPopBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
808+
uint32_t BitWidth) {
809+
const Pointer &Ptr = S.Stk.pop<Pointer>();
810+
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
811+
return false;
812+
813+
return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow,
814+
BitWidth);
815+
}
816+
789817
template <PrimType Name, class T = typename PrimConv<Name>::T>
790818
bool PreInc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
791819
const Pointer &Ptr = S.Stk.peek<Pointer>();
@@ -795,6 +823,17 @@ bool PreInc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
795823
return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow);
796824
}
797825

826+
template <PrimType Name, class T = typename PrimConv<Name>::T>
827+
bool PreIncBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
828+
uint32_t BitWidth) {
829+
const Pointer &Ptr = S.Stk.peek<Pointer>();
830+
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
831+
return false;
832+
833+
return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow,
834+
BitWidth);
835+
}
836+
798837
/// 1) Pops a pointer from the stack
799838
/// 2) Load the value from the pointer
800839
/// 3) Writes the value decreased by one back to the pointer
@@ -808,6 +847,16 @@ bool Dec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
808847
return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr,
809848
CanOverflow);
810849
}
850+
template <PrimType Name, class T = typename PrimConv<Name>::T>
851+
bool DecBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
852+
uint32_t BitWidth) {
853+
const Pointer &Ptr = S.Stk.pop<Pointer>();
854+
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
855+
return false;
856+
857+
return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr, CanOverflow,
858+
BitWidth);
859+
}
811860

812861
/// 1) Pops a pointer from the stack
813862
/// 2) Load the value from the pointer
@@ -821,6 +870,17 @@ bool DecPop(InterpState &S, CodePtr OpPC, bool CanOverflow) {
821870
return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow);
822871
}
823872

873+
template <PrimType Name, class T = typename PrimConv<Name>::T>
874+
bool DecPopBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
875+
uint32_t BitWidth) {
876+
const Pointer &Ptr = S.Stk.pop<Pointer>();
877+
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
878+
return false;
879+
880+
return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow,
881+
BitWidth);
882+
}
883+
824884
template <PrimType Name, class T = typename PrimConv<Name>::T>
825885
bool PreDec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
826886
const Pointer &Ptr = S.Stk.peek<Pointer>();
@@ -829,6 +889,16 @@ bool PreDec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
829889
return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow);
830890
}
831891

892+
template <PrimType Name, class T = typename PrimConv<Name>::T>
893+
bool PreDecBitfield(InterpState &S, CodePtr OpPC, bool CanOverflow,
894+
uint32_t BitWidth) {
895+
const Pointer &Ptr = S.Stk.peek<Pointer>();
896+
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
897+
return false;
898+
return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow,
899+
BitWidth);
900+
}
901+
832902
template <IncDecOp Op, PushVal DoPush>
833903
bool IncDecFloatHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
834904
uint32_t FPOI) {

clang/lib/AST/ByteCode/Opcodes.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,12 +612,25 @@ class OverflowOpcode : Opcode {
612612
let HasGroup = 1;
613613
}
614614

615+
class OverflowBitfieldOpcode : Opcode {
616+
let Types = [AluTypeClass];
617+
let Args = [ArgBool, ArgUint32];
618+
let HasGroup = 1;
619+
}
620+
615621
def Inc : OverflowOpcode;
622+
def IncBitfield : OverflowBitfieldOpcode;
616623
def IncPop : OverflowOpcode;
624+
def IncPopBitfield : OverflowBitfieldOpcode;
617625
def PreInc : OverflowOpcode;
626+
def PreIncBitfield : OverflowBitfieldOpcode;
627+
618628
def Dec : OverflowOpcode;
629+
def DecBitfield : OverflowBitfieldOpcode;
619630
def DecPop : OverflowOpcode;
631+
def DecPopBitfield : OverflowBitfieldOpcode;
620632
def PreDec : OverflowOpcode;
633+
def PreDecBitfield : OverflowBitfieldOpcode;
621634

622635
// Float increment and decrement.
623636
def Incf: FloatOpcode;

clang/test/AST/ByteCode/bitfields.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,82 @@ namespace NonConstBitWidth {
128128
// both-note {{read of non-const variable}}
129129
};
130130
}
131+
132+
namespace IncDecOverflow {
133+
constexpr bool test1() {
134+
struct {unsigned u: 5; } a {};
135+
a.u--;
136+
return a.u == 31;
137+
}
138+
static_assert(test1(), "");
139+
140+
constexpr bool test2() {
141+
struct {unsigned u: 5; } a {};
142+
--a.u;
143+
return a.u == 31;
144+
}
145+
static_assert(test2(), "");
146+
147+
constexpr bool test3() {
148+
int x = 0;
149+
struct {unsigned u: 5; } a {};
150+
x = a.u--;
151+
return a.u == 31;
152+
}
153+
static_assert(test3(), "");
154+
155+
constexpr bool test4() {
156+
int x = 0;
157+
struct {unsigned u: 5; } a {};
158+
x = --a.u;
159+
return a.u == 31;
160+
}
161+
static_assert(test4(), "");
162+
163+
constexpr bool test5() {
164+
struct {unsigned u: 5; } a {};
165+
a.u = 31;
166+
++a.u;
167+
168+
return a.u == 0;
169+
}
170+
static_assert(test5(), "");
171+
172+
constexpr bool test6() {
173+
struct {unsigned u: 5; } a {};
174+
a.u = 31;
175+
++a.u;
176+
177+
return a.u == 0;
178+
}
179+
static_assert(test6(), "");
180+
181+
constexpr bool test7() {
182+
struct {unsigned u: 5; } a {};
183+
a.u = 31;
184+
a.u++;
185+
186+
return a.u == 0;
187+
}
188+
static_assert(test7(), "");
189+
190+
constexpr bool test8() {
191+
int x = 0;
192+
struct {unsigned u: 5; } a {};
193+
a.u = 31;
194+
x = a.u++;
195+
196+
return a.u == 0;
197+
}
198+
static_assert(test8(), "");
199+
200+
constexpr bool test9() {
201+
int x = 0;
202+
struct {unsigned u: 5; } a {};
203+
a.u = 31;
204+
x = ++a.u;
205+
206+
return a.u == 0;
207+
}
208+
static_assert(test9(), "");
209+
}

0 commit comments

Comments
 (0)