Skip to content

[analyzer][Solver] Improve getSymVal and friends (1/2) #112583

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 18, 2024
Merged

[analyzer][Solver] Improve getSymVal and friends (1/2) #112583

merged 1 commit into from
Oct 18, 2024

Conversation

steakhal
Copy link
Contributor

This PR should be review commit by commit, and ought to be rebase-merged.
See the individual commits for their summary.

Once these two commits are reviewed, I'll force push to this PR the first commit, and open a new PR for the second commit because rebase-merge is not an option for upstream llvm, but I still wanted to let you know the whole picture, and that these two commits are related.

@llvmbot llvmbot added the clang Clang issues not falling into any other category label Oct 16, 2024
@llvmbot
Copy link
Member

llvmbot commented Oct 16, 2024

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Balazs Benics (steakhal)

Changes

This PR should be review commit by commit, and ought to be rebase-merged.
See the individual commits for their summary.

Once these two commits are reviewed, I'll force push to this PR the first commit, and open a new PR for the second commit because rebase-merge is not an option for upstream llvm, but I still wanted to let you know the whole picture, and that these two commits are related.


Full diff: https://github.com/llvm/llvm-project/pull/112583.diff

3 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp (+20-5)
  • (modified) clang/test/Analysis/infeasible-sink.c (+6-17)
  • (modified) clang/test/Analysis/unary-sym-expr.c (+1-2)
diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 70d5a609681790..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.
@@ -1939,11 +1953,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 +2877,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 +2893,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;
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) {

Copy link
Contributor

@NagyDonat NagyDonat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, see inline remarks for details.

Copy link
Contributor

@NagyDonat NagyDonat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(The "approve mark" was accidental, this should not be merged without fixing the trivial mistake that hardcodes addition.)

@steakhal steakhal requested a review from NagyDonat October 17, 2024 12:57
@steakhal
Copy link
Contributor Author

@NagyDonat Let me know if I resolved all your comments, and close those if you agree.

Copy link
Contributor

@NagyDonat NagyDonat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the updates!

I have an optional suggestion/question in the only inline discussion that's left unresolved, but feel free to merge this without handling that.

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.
@steakhal steakhal changed the title [analyzer] Improve solver [analyzer][Solver] Improve getSymVal and friends (1/2) Oct 18, 2024
@steakhal steakhal merged commit 4995d09 into llvm:main Oct 18, 2024
5 of 6 checks passed
@steakhal steakhal deleted the improve-solver branch October 18, 2024 11:51
steakhal added a commit that referenced this pull request Oct 18, 2024
…/2) (#112887)

This patch should not introduce much overhead as it only does one more
constraint map lookup, which is really quick.

Depends on #112583
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants