https://github.com/steakhal updated https://github.com/llvm/llvm-project/pull/112583
>From 4bf74c7f9caf89b67e6001b601f70741c1a672cc Mon Sep 17 00:00:00 2001 From: Balazs Benics <benicsbal...@gmail.com> Date: Wed, 16 Oct 2024 15:52:31 +0200 Subject: [PATCH 1/7] [analyzer][Solver] Improve getSymVal and friends Instead of just doing a lookup in the existing constraints, let's use `getRange()` for dissambling SymSymExprs and inferring from the constraint system what end range set we can simplify. This means that `getSymVal` gets more expensive while getting smarter. I don't expect it to be an issue as this API is only rarely used, and `getRange` is used a lot more often - yet without any observable hit on performance. This patch also removes dead declarations of EQClass overloads. --- .../Core/RangeConstraintManager.cpp | 11 +++++---- clang/test/Analysis/infeasible-sink.c | 23 +++++-------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 70d5a609681790..d1999c86f8ecd8 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1939,11 +1939,8 @@ class RangeConstraintManager : public RangedConstraintManager { RangeSet::Factory F; RangeSet getRange(ProgramStateRef State, SymbolRef Sym); - RangeSet getRange(ProgramStateRef State, EquivalenceClass Class); ProgramStateRef setRange(ProgramStateRef State, SymbolRef Sym, RangeSet Range); - ProgramStateRef setRange(ProgramStateRef State, EquivalenceClass Class, - RangeSet Range); RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, @@ -2866,12 +2863,14 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, SymbolRef Sym) const { - const RangeSet *T = getConstraint(St, Sym); - return T ? T->getConcreteValue() : nullptr; + auto &MutableSelf = const_cast<RangeConstraintManager &>(*this); + return MutableSelf.getRange(St, Sym).getConcreteValue(); } const llvm::APSInt *RangeConstraintManager::getSymMinVal(ProgramStateRef St, SymbolRef Sym) const { + // TODO: Use `getRange()` like in `getSymVal()`, but that would make some + // of the reports of `BitwiseShiftChecker` look awkward. const RangeSet *T = getConstraint(St, Sym); if (!T || T->isEmpty()) return nullptr; @@ -2880,6 +2879,8 @@ const llvm::APSInt *RangeConstraintManager::getSymMinVal(ProgramStateRef St, const llvm::APSInt *RangeConstraintManager::getSymMaxVal(ProgramStateRef St, SymbolRef Sym) const { + // TODO: Use `getRange()` like in `getSymVal()`, but that would make some + // of the reports of `BitwiseShiftChecker` look awkward. const RangeSet *T = getConstraint(St, Sym); if (!T || T->isEmpty()) return nullptr; diff --git a/clang/test/Analysis/infeasible-sink.c b/clang/test/Analysis/infeasible-sink.c index 9cb66fcac0b6be..d5b4e82268f5b6 100644 --- a/clang/test/Analysis/infeasible-sink.c +++ b/clang/test/Analysis/infeasible-sink.c @@ -38,7 +38,7 @@ void test1(int x) { } int a, b, c, d, e; -void test2() { +void test2(void) { if (a == 0) return; @@ -50,28 +50,17 @@ void test2() { b = d; a -= d; + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + if (a != 0) return; - clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}} - - /* The BASELINE passes these checks ('wrning' is used to avoid lit to match) - // The parent state is already infeasible, look at this contradiction: - clang_analyzer_eval(b > 0); // expected-wrning{{FALSE}} - clang_analyzer_eval(b <= 0); // expected-wrning{{FALSE}} - // Crashes with expensive checks. - if (b > 0) { - clang_analyzer_warnIfReached(); // no-warning, OK - return; - } - // Should not be reachable. - clang_analyzer_warnIfReached(); // expected-wrning{{REACHABLE}} - */ + clang_analyzer_warnIfReached(); // no-warning: Even the parent state is unreachable. // The parent state is already infeasible, but we realize that only if b is // constrained. - clang_analyzer_eval(b > 0); // expected-warning{{UNKNOWN}} - clang_analyzer_eval(b <= 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(b > 0); // no-warning + clang_analyzer_eval(b <= 0); // no-warning if (b > 0) { clang_analyzer_warnIfReached(); // no-warning return; >From be65bb0694be3353fda260ab73777aea28de9113 Mon Sep 17 00:00:00 2001 From: Balazs Benics <benicsbal...@gmail.com> Date: Wed, 16 Oct 2024 15:53:20 +0200 Subject: [PATCH 2/7] [analyzer][Solver] Teach SymbolicRangeInferrer about commutativity This patch should not introduce much overhead as it only does one more constraint map lookup, which is really quick. --- .../StaticAnalyzer/Core/RangeConstraintManager.cpp | 14 ++++++++++++++ clang/test/Analysis/unary-sym-expr.c | 3 +-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index d1999c86f8ecd8..2ee86a90a1f6d2 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1249,6 +1249,8 @@ class SymbolicRangeInferrer // calculate the effective range set by intersecting the range set // for A - B and the negated range set of B - A. getRangeForNegatedSymSym(SSE), + // If commutative, we may have constaints for the commuted variant. + getRangeCommutativeSymSym(SSE), // If Sym is a comparison expression (except <=>), // find any other comparisons with the same operands. // See function description. @@ -1485,6 +1487,18 @@ class SymbolicRangeInferrer Sym->getType()); } + std::optional<RangeSet> getRangeCommutativeSymSym(const SymSymExpr *SSE) { + bool IsCommutative = llvm::is_contained({BO_Add, BO_Mul}, SSE->getOpcode()); + if (!IsCommutative) + return std::nullopt; + + SymbolRef Commuted = State->getSymbolManager().getSymSymExpr( + SSE->getRHS(), BO_Add, SSE->getLHS(), SSE->getType()); + if (const RangeSet *Range = getConstraint(State, Commuted)) + return *Range; + return std::nullopt; + } + // Returns ranges only for binary comparison operators (except <=>) // when left and right operands are symbolic values. // Finds any other comparisons with the same operands. diff --git a/clang/test/Analysis/unary-sym-expr.c b/clang/test/Analysis/unary-sym-expr.c index 7c4774f3cca82f..7ce7a3c5c24e35 100644 --- a/clang/test/Analysis/unary-sym-expr.c +++ b/clang/test/Analysis/unary-sym-expr.c @@ -33,8 +33,7 @@ void test_svalbuilder_simplification(int x, int y) { if (x + y != 3) return; clang_analyzer_eval(-(x + y) == -3); // expected-warning{{TRUE}} - // FIXME Commutativity is not supported yet. - clang_analyzer_eval(-(y + x) == -3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(-(y + x) == -3); // expected-warning{{TRUE}} } int test_fp(int flag) { >From ac6d7ac054feda50e6db8f90e1ec66a43dcae5b1 Mon Sep 17 00:00:00 2001 From: Balazs Benics <benicsbal...@gmail.com> Date: Thu, 17 Oct 2024 13:05:05 +0200 Subject: [PATCH 3/7] Fix BO_Add typo! --- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 2ee86a90a1f6d2..3dd4b269be4e35 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1493,7 +1493,7 @@ class SymbolicRangeInferrer return std::nullopt; SymbolRef Commuted = State->getSymbolManager().getSymSymExpr( - SSE->getRHS(), BO_Add, SSE->getLHS(), SSE->getType()); + SSE->getRHS(), SSE->getOpcode(), SSE->getLHS(), SSE->getType()); if (const RangeSet *Range = getConstraint(State, Commuted)) return *Range; return std::nullopt; >From d194e958c6adbf474621757278c6b440e93cae5d Mon Sep 17 00:00:00 2001 From: Balazs Benics <benicsbal...@gmail.com> Date: Thu, 17 Oct 2024 13:05:19 +0200 Subject: [PATCH 4/7] Drop part of the test that become dead --- clang/test/Analysis/infeasible-sink.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/clang/test/Analysis/infeasible-sink.c b/clang/test/Analysis/infeasible-sink.c index d5b4e82268f5b6..4e2c008965ece5 100644 --- a/clang/test/Analysis/infeasible-sink.c +++ b/clang/test/Analysis/infeasible-sink.c @@ -56,14 +56,4 @@ void test2(void) { return; clang_analyzer_warnIfReached(); // no-warning: Even the parent state is unreachable. - - // The parent state is already infeasible, but we realize that only if b is - // constrained. - clang_analyzer_eval(b > 0); // no-warning - clang_analyzer_eval(b <= 0); // no-warning - if (b > 0) { - clang_analyzer_warnIfReached(); // no-warning - return; - } - clang_analyzer_warnIfReached(); // no-warning } >From 0ca686a3e1d3afab3abb0f58117926e59bd615c4 Mon Sep 17 00:00:00 2001 From: Balazs Benics <benicsbal...@gmail.com> Date: Thu, 17 Oct 2024 13:09:49 +0200 Subject: [PATCH 5/7] Use getRange() in getSym{Min,Max}Val too --- .../Core/RangeConstraintManager.cpp | 18 ++++++------------ clang/test/Analysis/bitwise-shift-common.c | 8 ++------ 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 3dd4b269be4e35..c3bb027e90c34c 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -2883,22 +2883,16 @@ const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, const llvm::APSInt *RangeConstraintManager::getSymMinVal(ProgramStateRef St, SymbolRef Sym) const { - // TODO: Use `getRange()` like in `getSymVal()`, but that would make some - // of the reports of `BitwiseShiftChecker` look awkward. - const RangeSet *T = getConstraint(St, Sym); - if (!T || T->isEmpty()) - return nullptr; - return &T->getMinValue(); + auto &MutableSelf = const_cast<RangeConstraintManager &>(*this); + RangeSet Range = MutableSelf.getRange(St, Sym); + return Range.isEmpty() ? nullptr : &Range.getMinValue(); } const llvm::APSInt *RangeConstraintManager::getSymMaxVal(ProgramStateRef St, SymbolRef Sym) const { - // TODO: Use `getRange()` like in `getSymVal()`, but that would make some - // of the reports of `BitwiseShiftChecker` look awkward. - const RangeSet *T = getConstraint(St, Sym); - if (!T || T->isEmpty()) - return nullptr; - return &T->getMaxValue(); + auto &MutableSelf = const_cast<RangeConstraintManager &>(*this); + RangeSet Range = MutableSelf.getRange(St, Sym); + return Range.isEmpty() ? nullptr : &Range.getMaxValue(); } //===----------------------------------------------------------------------===// diff --git a/clang/test/Analysis/bitwise-shift-common.c b/clang/test/Analysis/bitwise-shift-common.c index 5f37d9976263ae..735f125f317bf5 100644 --- a/clang/test/Analysis/bitwise-shift-common.c +++ b/clang/test/Analysis/bitwise-shift-common.c @@ -99,11 +99,7 @@ int too_large_right_operand_compound(unsigned short arg) { // expected-note@-2 {{32s:{ [-2147483648, 2147483647] }} return 1 << (32 + arg); // expected-warning@-1 {{Left shift overflows the capacity of 'int'}} - // expected-note@-2 {{The result of left shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}} - // FIXME: this message should be - // {{The result of left shift is undefined because the right operand is >= 32, not smaller than 32, the capacity of 'int'}} - // but for some reason neither the new logic, nor debug.ExprInspection and - // clang_analyzer_value reports this range information. + // expected-note@-2 {{The result of left shift is undefined because the right operand is >= -2147483648, not smaller than 32, the capacity of 'int'}} } // TEST STATE UPDATES @@ -116,7 +112,7 @@ void state_update(char a, int *p) { // expected-note@-1 {{Assuming right operand of bit shift is non-negative but less than 32}} *p += 1 << (a + 32); // expected-warning@-1 {{Left shift overflows the capacity of 'int'}} - // expected-note@-2 {{The result of left shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}} + // expected-note@-2 {{The result of left shift is undefined because the right operand is >= -2147483648, not smaller than 32, the capacity of 'int'}} } void state_update_2(char a, int *p) { >From 3a0a2a23ad1fe4dc27fd8bf14d148a4cd9abef0a Mon Sep 17 00:00:00 2001 From: Balazs Benics <benicsbal...@gmail.com> Date: Thu, 17 Oct 2024 14:33:08 +0200 Subject: [PATCH 6/7] Support more commutative operators and add some tests --- .../Core/RangeConstraintManager.cpp | 9 +++- clang/test/Analysis/unary-sym-expr.c | 45 ++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index c3bb027e90c34c..0d1e206572e92a 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1488,12 +1488,17 @@ class SymbolicRangeInferrer } std::optional<RangeSet> getRangeCommutativeSymSym(const SymSymExpr *SSE) { - bool IsCommutative = llvm::is_contained({BO_Add, BO_Mul}, SSE->getOpcode()); + auto Op = SSE->getOpcode(); + assert(Op != BO_LOr); + assert(Op != BO_LAnd); + bool IsCommutative = llvm::is_contained( + // ==, !=, |, &, +, *, ^ + {BO_EQ, BO_NE, BO_Or, BO_And, BO_Add, BO_Mul, BO_Xor}, Op); if (!IsCommutative) return std::nullopt; SymbolRef Commuted = State->getSymbolManager().getSymSymExpr( - SSE->getRHS(), SSE->getOpcode(), SSE->getLHS(), SSE->getType()); + SSE->getRHS(), Op, SSE->getLHS(), SSE->getType()); if (const RangeSet *Range = getConstraint(State, Commuted)) return *Range; return std::nullopt; diff --git a/clang/test/Analysis/unary-sym-expr.c b/clang/test/Analysis/unary-sym-expr.c index 7ce7a3c5c24e35..04b7c22862c50c 100644 --- a/clang/test/Analysis/unary-sym-expr.c +++ b/clang/test/Analysis/unary-sym-expr.c @@ -29,13 +29,56 @@ int test(int x, int y) { return 42; } -void test_svalbuilder_simplification(int x, int y) { +void test_svalbuilder_simplification_add(int x, int y) { if (x + y != 3) return; clang_analyzer_eval(-(x + y) == -3); // expected-warning{{TRUE}} clang_analyzer_eval(-(y + x) == -3); // expected-warning{{TRUE}} } +void test_svalbuilder_simplification_mul(int x, int y) { + if (x * y != 3) + return; + clang_analyzer_eval(-(x * y) == -3); // expected-warning{{TRUE}} + clang_analyzer_eval(-(y * x) == -3); // expected-warning{{TRUE}} +} + +void test_svalbuilder_simplification_and(int x, int y) { + if ((x & y) != 3) + return; + clang_analyzer_eval(-(x & y) == -3); // expected-warning{{TRUE}} + clang_analyzer_eval(-(y & x) == -3); // expected-warning{{TRUE}} +} + +void test_svalbuilder_simplification_or(int x, int y) { + if ((x | y) != 3) + return; + clang_analyzer_eval(-(x | y) == -3); // expected-warning{{TRUE}} + clang_analyzer_eval(-(y | x) == -3); // expected-warning{{TRUE}} +} + +void test_svalbuilder_simplification_xor(int x, int y) { + if ((x ^ y) != 3) + return; + clang_analyzer_eval(-(x ^ y) == -3); // expected-warning{{TRUE}} + clang_analyzer_eval(-(y ^ x) == -3); // expected-warning{{TRUE}} +} + +void test_svalbuilder_simplification_land(int x, int y) { + if (x && y) { + clang_analyzer_eval(-(x && y) == -1); // expected-warning{{UNKNOWN}} FIXME: Should be TRUE + clang_analyzer_eval(-(y && x) == -1); // expected-warning{{UNKNOWN}} FIXME: Should be TRUE + } +} + +void test_svalbuilder_simplification_lor(int x, int y) { + if (x || y) { + // FIXME: Why do we have both TRUE and UNKNOWN? + clang_analyzer_eval(-(x || y) == -1); // expected-warning{{TRUE}} expected-warning{{UNKNOWN}} + clang_analyzer_eval(-(y || x) == -1); // expected-warning{{TRUE}} expected-warning{{UNKNOWN}} + } +} + int test_fp(int flag) { int value; if (flag == 0) >From 1bdfff0c204ead130b51aa3814e6f8076140f4ed Mon Sep 17 00:00:00 2001 From: Balazs Benics <benicsbal...@gmail.com> Date: Thu, 17 Oct 2024 14:41:47 +0200 Subject: [PATCH 7/7] Adjust the BitwiseShiftChecker to keep existing behavior --- clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp | 3 ++- clang/test/Analysis/bitwise-shift-common.c | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp index 339927c165fe00..17f1214195b3ee 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/BitwiseShiftChecker.cpp @@ -177,7 +177,8 @@ BugReportPtr BitwiseShiftValidator::checkOvershift() { RightOpStr = formatv(" '{0}'", ConcreteRight->getValue()); else { SValBuilder &SVB = Ctx.getSValBuilder(); - if (const llvm::APSInt *MinRight = SVB.getMinValue(FoldedState, Right)) { + if (const llvm::APSInt *MinRight = SVB.getMinValue(FoldedState, Right); + MinRight && *MinRight >= LHSBitWidth) { LowerBoundStr = formatv(" >= {0},", MinRight->getExtValue()); } } diff --git a/clang/test/Analysis/bitwise-shift-common.c b/clang/test/Analysis/bitwise-shift-common.c index 735f125f317bf5..5f37d9976263ae 100644 --- a/clang/test/Analysis/bitwise-shift-common.c +++ b/clang/test/Analysis/bitwise-shift-common.c @@ -99,7 +99,11 @@ int too_large_right_operand_compound(unsigned short arg) { // expected-note@-2 {{32s:{ [-2147483648, 2147483647] }} return 1 << (32 + arg); // expected-warning@-1 {{Left shift overflows the capacity of 'int'}} - // expected-note@-2 {{The result of left shift is undefined because the right operand is >= -2147483648, not smaller than 32, the capacity of 'int'}} + // expected-note@-2 {{The result of left shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}} + // FIXME: this message should be + // {{The result of left shift is undefined because the right operand is >= 32, not smaller than 32, the capacity of 'int'}} + // but for some reason neither the new logic, nor debug.ExprInspection and + // clang_analyzer_value reports this range information. } // TEST STATE UPDATES @@ -112,7 +116,7 @@ void state_update(char a, int *p) { // expected-note@-1 {{Assuming right operand of bit shift is non-negative but less than 32}} *p += 1 << (a + 32); // expected-warning@-1 {{Left shift overflows the capacity of 'int'}} - // expected-note@-2 {{The result of left shift is undefined because the right operand is >= -2147483648, not smaller than 32, the capacity of 'int'}} + // expected-note@-2 {{The result of left shift is undefined because the right operand is not smaller than 32, the capacity of 'int'}} } void state_update_2(char a, int *p) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits