-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[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
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-static-analyzer-1 Author: Balazs Benics (steakhal) ChangesThis PR should be review commit by commit, and ought to be rebase-merged. 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:
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) {
|
There was a problem hiding this 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.
There was a problem hiding this 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.)
@NagyDonat Let me know if I resolved all your comments, and close those if you agree. |
There was a problem hiding this 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.
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.