Skip to content

Commit 5bbfa26

Browse files
tbaederraokblast
authored andcommitted
[clang][bytecode] Fix unsigned wraparound behavior with bitfields (llvm#164445)
and inc/dec operations. We need to truncate the new value to the bitfield bitwidth.
1 parent 14e04a0 commit 5bbfa26

File tree

4 files changed

+206
-6
lines changed

4 files changed

+206
-6
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

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

6435+
static uint32_t getBitWidth(const Expr *E) {
6436+
assert(E->refersToBitField());
6437+
const auto *ME = cast<MemberExpr>(E);
6438+
const auto *FD = cast<FieldDecl>(ME->getMemberDecl());
6439+
return FD->getBitWidthValue();
6440+
}
6441+
64356442
template <class Emitter>
64366443
bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
64376444
const Expr *SubExpr = E->getSubExpr();
@@ -6460,10 +6467,15 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
64606467
return DiscardResult ? this->emitPopPtr(E) : true;
64616468
}
64626469

6463-
if (T == PT_Float) {
6470+
if (T == PT_Float)
64646471
return DiscardResult ? this->emitIncfPop(getFPOptions(E), E)
64656472
: this->emitIncf(getFPOptions(E), E);
6466-
}
6473+
6474+
if (SubExpr->refersToBitField())
6475+
return DiscardResult ? this->emitIncPopBitfield(*T, E->canOverflow(),
6476+
getBitWidth(SubExpr), E)
6477+
: this->emitIncBitfield(*T, E->canOverflow(),
6478+
getBitWidth(SubExpr), E);
64676479

64686480
return DiscardResult ? this->emitIncPop(*T, E->canOverflow(), E)
64696481
: this->emitInc(*T, E->canOverflow(), E);
@@ -6484,9 +6496,15 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
64846496
return DiscardResult ? this->emitPopPtr(E) : true;
64856497
}
64866498

6487-
if (T == PT_Float) {
6499+
if (T == PT_Float)
64886500
return DiscardResult ? this->emitDecfPop(getFPOptions(E), E)
64896501
: this->emitDecf(getFPOptions(E), E);
6502+
6503+
if (SubExpr->refersToBitField()) {
6504+
return DiscardResult ? this->emitDecPopBitfield(*T, E->canOverflow(),
6505+
getBitWidth(SubExpr), E)
6506+
: this->emitDecBitfield(*T, E->canOverflow(),
6507+
getBitWidth(SubExpr), E);
64906508
}
64916509

64926510
return DiscardResult ? this->emitDecPop(*T, E->canOverflow(), E)
@@ -6515,6 +6533,11 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
65156533
if (DiscardResult) {
65166534
if (T == PT_Float)
65176535
return this->emitIncfPop(getFPOptions(E), E);
6536+
if (SubExpr->refersToBitField())
6537+
return DiscardResult ? this->emitIncPopBitfield(*T, E->canOverflow(),
6538+
getBitWidth(SubExpr), E)
6539+
: this->emitIncBitfield(*T, E->canOverflow(),
6540+
getBitWidth(SubExpr), E);
65186541
return this->emitIncPop(*T, E->canOverflow(), E);
65196542
}
65206543

@@ -6530,6 +6553,11 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
65306553
return false;
65316554
if (!this->emitStoreFloat(E))
65326555
return false;
6556+
} else if (SubExpr->refersToBitField()) {
6557+
assert(isIntegralType(*T));
6558+
if (!this->emitPreIncBitfield(*T, E->canOverflow(), getBitWidth(SubExpr),
6559+
E))
6560+
return false;
65336561
} else {
65346562
assert(isIntegralType(*T));
65356563
if (!this->emitPreInc(*T, E->canOverflow(), E))
@@ -6560,6 +6588,11 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
65606588
if (DiscardResult) {
65616589
if (T == PT_Float)
65626590
return this->emitDecfPop(getFPOptions(E), E);
6591+
if (SubExpr->refersToBitField())
6592+
return DiscardResult ? this->emitDecPopBitfield(*T, E->canOverflow(),
6593+
getBitWidth(SubExpr), E)
6594+
: this->emitDecBitfield(*T, E->canOverflow(),
6595+
getBitWidth(SubExpr), E);
65636596
return this->emitDecPop(*T, E->canOverflow(), E);
65646597
}
65656598

@@ -6575,6 +6608,11 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
65756608
return false;
65766609
if (!this->emitStoreFloat(E))
65776610
return false;
6611+
} else if (SubExpr->refersToBitField()) {
6612+
assert(isIntegralType(*T));
6613+
if (!this->emitPreDecBitfield(*T, E->canOverflow(), getBitWidth(SubExpr),
6614+
E))
6615+
return false;
65786616
} else {
65796617
assert(isIntegralType(*T));
65806618
if (!this->emitPreDec(*T, E->canOverflow(), E))

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)