[clang] [clang][bytecode] Ignore overflow in unary operators if requested (PR #132557)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 22 09:23:50 PDT 2025
https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/132557
Add PreInc and PreDec ops for this purpose and ignore the overflow if UnaryOperator::canOverflow() returns false.
>From 06a6ab84ce28373731bd3750b876f951f849b129 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Sat, 22 Mar 2025 17:19:12 +0100
Subject: [PATCH] [clang][bytecode] Ignore overflow in unary operators if
requested
Add PreInc and PreDec ops for this purpose and ignore the overflow
if UnaryOperator::canOverflow() returns false.
---
clang/lib/AST/ByteCode/Compiler.cpp | 28 ++++++-----------
clang/lib/AST/ByteCode/Interp.h | 45 ++++++++++++++++++++--------
clang/lib/AST/ByteCode/Opcodes.td | 16 +++++++---
clang/test/AST/ByteCode/literals.cpp | 26 ++++++++++++++++
4 files changed, 80 insertions(+), 35 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index b30c669df6825..943e4302a0398 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -3518,7 +3518,7 @@ bool Compiler<Emitter>::VisitCXXNewExpr(const CXXNewExpr *E) {
// ++Iter;
if (!this->emitGetPtrLocal(Iter, E))
return false;
- if (!this->emitIncPop(SizeT, E))
+ if (!this->emitIncPop(SizeT, false, E))
return false;
if (!this->jump(StartLabel))
@@ -5957,7 +5957,8 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
: this->emitIncf(getFPOptions(E), E);
}
- return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E);
+ return DiscardResult ? this->emitIncPop(*T, E->canOverflow(), E)
+ : this->emitInc(*T, E->canOverflow(), E);
}
case UO_PostDec: { // x--
if (!Ctx.getLangOpts().CPlusPlus14)
@@ -5980,7 +5981,8 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
: this->emitDecf(getFPOptions(E), E);
}
- return DiscardResult ? this->emitDecPop(*T, E) : this->emitDec(*T, E);
+ return DiscardResult ? this->emitDecPop(*T, E->canOverflow(), E)
+ : this->emitDec(*T, E->canOverflow(), E);
}
case UO_PreInc: { // ++x
if (!Ctx.getLangOpts().CPlusPlus14)
@@ -6005,7 +6007,7 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
if (DiscardResult) {
if (T == PT_Float)
return this->emitIncfPop(getFPOptions(E), E);
- return this->emitIncPop(*T, E);
+ return this->emitIncPop(*T, E->canOverflow(), E);
}
if (T == PT_Float) {
@@ -6020,13 +6022,7 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
return false;
} else {
assert(isIntegralType(*T));
- if (!this->emitLoad(*T, E))
- return false;
- if (!this->emitConst(1, E))
- return false;
- if (!this->emitAdd(*T, E))
- return false;
- if (!this->emitStore(*T, E))
+ if (!this->emitPreInc(*T, E->canOverflow(), E))
return false;
}
return E->isGLValue() || this->emitLoadPop(*T, E);
@@ -6054,7 +6050,7 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
if (DiscardResult) {
if (T == PT_Float)
return this->emitDecfPop(getFPOptions(E), E);
- return this->emitDecPop(*T, E);
+ return this->emitDecPop(*T, E->canOverflow(), E);
}
if (T == PT_Float) {
@@ -6069,13 +6065,7 @@ bool Compiler<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
return false;
} else {
assert(isIntegralType(*T));
- if (!this->emitLoad(*T, E))
- return false;
- if (!this->emitConst(1, E))
- return false;
- if (!this->emitSub(*T, E))
- return false;
- if (!this->emitStore(*T, E))
+ if (!this->emitPreDec(*T, E->canOverflow(), E))
return false;
}
return E->isGLValue() || this->emitLoadPop(*T, E);
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 8ec4d95871702..ee4139fbc9530 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -765,7 +765,8 @@ enum class IncDecOp {
};
template <typename T, IncDecOp Op, PushVal DoPush>
-bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
+ bool CanOverflow) {
assert(!Ptr.isDummy());
if constexpr (std::is_same_v<T, Boolean>) {
@@ -780,16 +781,17 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
S.Stk.push<T>(Value);
if constexpr (Op == IncDecOp::Inc) {
- if (!T::increment(Value, &Result)) {
+ if (!T::increment(Value, &Result) || !CanOverflow) {
Ptr.deref<T>() = Result;
return true;
}
} else {
- if (!T::decrement(Value, &Result)) {
+ if (!T::decrement(Value, &Result) || !CanOverflow) {
Ptr.deref<T>() = Result;
return true;
}
}
+ assert(CanOverflow);
// Something went wrong with the previous operation. Compute the
// result with another bit of precision.
@@ -812,7 +814,6 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
<< Trunc << Type << E->getSourceRange();
return true;
}
-
return handleOverflow(S, OpPC, APResult);
}
@@ -821,24 +822,34 @@ bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
/// 3) Writes the value increased by one back to the pointer
/// 4) Pushes the original (pre-inc) value on the stack.
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool Inc(InterpState &S, CodePtr OpPC) {
+bool Inc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
return false;
- return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr);
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr,
+ CanOverflow);
}
/// 1) Pops a pointer from the stack
/// 2) Load the value from the pointer
/// 3) Writes the value increased by one back to the pointer
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool IncPop(InterpState &S, CodePtr OpPC) {
+bool IncPop(InterpState &S, CodePtr OpPC, bool CanOverflow) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
return false;
- return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr);
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool PreInc(InterpState &S, CodePtr OpPC, bool CanOverflow) {
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckLoad(S, OpPC, Ptr, AK_Increment))
+ return false;
+
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr, CanOverflow);
}
/// 1) Pops a pointer from the stack
@@ -846,24 +857,34 @@ bool IncPop(InterpState &S, CodePtr OpPC) {
/// 3) Writes the value decreased by one back to the pointer
/// 4) Pushes the original (pre-dec) value on the stack.
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool Dec(InterpState &S, CodePtr OpPC) {
+bool Dec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
return false;
- return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr);
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr,
+ CanOverflow);
}
/// 1) Pops a pointer from the stack
/// 2) Load the value from the pointer
/// 3) Writes the value decreased by one back to the pointer
template <PrimType Name, class T = typename PrimConv<Name>::T>
-bool DecPop(InterpState &S, CodePtr OpPC) {
+bool DecPop(InterpState &S, CodePtr OpPC, bool CanOverflow) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
return false;
- return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr);
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow);
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool PreDec(InterpState &S, CodePtr OpPC, bool CanOverflow) {
+ const Pointer &Ptr = S.Stk.peek<Pointer>();
+ if (!CheckLoad(S, OpPC, Ptr, AK_Decrement))
+ return false;
+
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr, CanOverflow);
}
template <IncDecOp Op, PushVal DoPush>
diff --git a/clang/lib/AST/ByteCode/Opcodes.td b/clang/lib/AST/ByteCode/Opcodes.td
index c29793ec886e5..798771bf91f05 100644
--- a/clang/lib/AST/ByteCode/Opcodes.td
+++ b/clang/lib/AST/ByteCode/Opcodes.td
@@ -593,10 +593,18 @@ def Shr : Opcode {
def Inv: Opcode;
// Increment and decrement.
-def Inc: AluOpcode;
-def IncPop : AluOpcode;
-def Dec: AluOpcode;
-def DecPop: AluOpcode;
+class OverflowOpcode : Opcode {
+ let Types = [AluTypeClass];
+ let Args = [ArgBool];
+ let HasGroup = 1;
+}
+
+def Inc : OverflowOpcode;
+def IncPop : OverflowOpcode;
+def PreInc : OverflowOpcode;
+def Dec : OverflowOpcode;
+def DecPop : OverflowOpcode;
+def PreDec : OverflowOpcode;
// Float increment and decrement.
def Incf: FloatOpcode;
diff --git a/clang/test/AST/ByteCode/literals.cpp b/clang/test/AST/ByteCode/literals.cpp
index 73fcb0f1f2dc3..68d400bc31dd7 100644
--- a/clang/test/AST/ByteCode/literals.cpp
+++ b/clang/test/AST/ByteCode/literals.cpp
@@ -598,6 +598,32 @@ namespace IncDec {
static_assert(UnderFlow() == -1, ""); // both-error {{not an integral constant expression}} \
// both-note {{in call to 'UnderFlow()'}}
+ /// This UnaryOperator can't overflow, so we shouldn't diagnose any overflow.
+ constexpr int CanOverflow() {
+ char c = 127;
+ char p;
+ ++c;
+ c++;
+ p = ++c;
+ p = c++;
+
+ c = -128;
+ --c;
+ c--;
+ p = --c;
+ p = ++c;
+
+ return 0;
+ }
+ static_assert(CanOverflow() == 0, "");
+
+ constexpr char OverflownChar() {
+ char c = 127;
+ c++;
+ return c;
+ }
+ static_assert(OverflownChar() == -128, "");
+
constexpr int getTwo() {
int i = 1;
return (i += 1);
More information about the cfe-commits
mailing list