Skip to content

Commit 52491f1

Browse files
committed
Signed integer optimisations; Also warn about 'x+y<x'
1 parent 6375fb4 commit 52491f1

2 files changed

Lines changed: 61 additions & 12 deletions

File tree

lib/checktype.cpp

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -228,37 +228,58 @@ void CheckType::integerOverflowError(const Token *tok, const ValueFlow::Value &v
228228

229229
void CheckType::checkIntegerOverflowOptimisations()
230230
{
231-
// Various patterns are defined here:
231+
// Interesting blogs:
232232
// https://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html
233+
// https://research.checkpoint.com/2020/optout-compiler-undefined-behavior-optimizations/
233234

234-
// These optimisations seem to generate "unwanted" output
235235
// x + c < x -> false
236236
// x + c <= x -> false
237237
// x + c > x -> true
238238
// x + c >= x -> true
239239

240+
// x + y < x -> y < 0
241+
242+
240243
if (!mSettings->isEnabled(Settings::PORTABILITY))
241244
return;
242245

243246
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
244247
if (!Token::Match(tok, "<|<=|>=|>") || !tok->isBinaryOp())
245248
continue;
246249

247-
if (Token::Match(tok->astOperand1(), "[+-]") &&
248-
tok->astOperand1()->valueType() &&
249-
tok->astOperand1()->valueType()->isIntegral() &&
250-
tok->astOperand1()->valueType()->sign == ValueType::Sign::SIGNED &&
251-
tok->astOperand1()->isBinaryOp() &&
252-
tok->astOperand1()->astOperand2()->isNumber() &&
253-
Token::Match(tok->astOperand1()->astOperand1(), "%var%") &&
254-
tok->astOperand1()->astOperand1()->str() == tok->astOperand2()->str()) {
250+
const std::string cmp = tok->str();
251+
const Token * const lhs = tok->astOperand1();
252+
if (!Token::Match(lhs, "[+-]") || !lhs->isBinaryOp() || !lhs->valueType() || !lhs->valueType()->isIntegral() || lhs->valueType()->sign != ValueType::Sign::SIGNED)
253+
continue;
254+
255+
const Token *expr = lhs->astOperand1();
256+
const Token *other = lhs->astOperand2();
257+
if (expr->varId() != lhs->astSibling()->varId())
258+
continue;
259+
260+
// x [+-] c cmp x
261+
if (other->isNumber() && other->getKnownIntValue() > 0) {
255262
bool result;
256263
if (tok->astOperand1()->str() == "+")
257-
result = Token::Match(tok, ">|>=");
264+
result = (cmp == ">" || cmp == ">=");
258265
else
259-
result = Token::Match(tok, "<|<=");
266+
result = (cmp == "<" || cmp == "<=");
260267
integerOverflowOptimisationError(tok, result ? "true" : "false");
261268
}
269+
270+
// x + y cmp x
271+
if (lhs->str() == "+" && other->varId() > 0) {
272+
const std::string result = other->str() + cmp + "0";
273+
integerOverflowOptimisationError(tok, result);
274+
}
275+
276+
// x - y cmp x
277+
if (lhs->str() == "-" && other->varId() > 0) {
278+
std::string cmp2 = cmp;
279+
cmp2[0] = (cmp[0] == '<') ? '>' : '<';
280+
const std::string result = other->str() + cmp2 + "0";
281+
integerOverflowOptimisationError(tok, result);
282+
}
262283
}
263284
}
264285

test/testtype.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ class TestType : public TestFixture {
253253
Settings settings;
254254
settings.addEnabled("warning");
255255

256+
// x + c < x
257+
256258
check("int f(int x) { return x + 10 > x; }", &settings);
257259
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x+10>x' will be optimised into 'true'. Signed integer overflow is undefined behavior.\n", errout.str());
258260

@@ -276,6 +278,32 @@ class TestType : public TestFixture {
276278

277279
check("int f(int x) { return x - 10 <= x; }", &settings);
278280
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x-10<=x' will be optimised into 'true'. Signed integer overflow is undefined behavior.\n", errout.str());
281+
282+
// x + y < x
283+
check("int f(int x, int y) { return x + y < x; }", &settings);
284+
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x+y<x' will be optimised into 'y<0'. Signed integer overflow is undefined behavior.\n", errout.str());
285+
286+
check("int f(int x, int y) { return x + y <= x; }", &settings);
287+
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x+y<=x' will be optimised into 'y<=0'. Signed integer overflow is undefined behavior.\n", errout.str());
288+
289+
check("int f(int x, int y) { return x + y > x; }", &settings);
290+
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x+y>x' will be optimised into 'y>0'. Signed integer overflow is undefined behavior.\n", errout.str());
291+
292+
check("int f(int x, int y) { return x + y >= x; }", &settings);
293+
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x+y>=x' will be optimised into 'y>=0'. Signed integer overflow is undefined behavior.\n", errout.str());
294+
295+
// x - y < x
296+
check("int f(int x, int y) { return x - y < x; }", &settings);
297+
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x-y<x' will be optimised into 'y>0'. Signed integer overflow is undefined behavior.\n", errout.str());
298+
299+
check("int f(int x, int y) { return x - y <= x; }", &settings);
300+
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x-y<=x' will be optimised into 'y>=0'. Signed integer overflow is undefined behavior.\n", errout.str());
301+
302+
check("int f(int x, int y) { return x - y > x; }", &settings);
303+
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x-y>x' will be optimised into 'y<0'. Signed integer overflow is undefined behavior.\n", errout.str());
304+
305+
check("int f(int x, int y) { return x - y >= x; }", &settings);
306+
ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x-y>=x' will be optimised into 'y<=0'. Signed integer overflow is undefined behavior.\n", errout.str());
279307
}
280308

281309
void signConversion() {

0 commit comments

Comments
 (0)