Skip to content

Conversation

@N1ark
Copy link
Contributor

@N1ark N1ark commented Dec 21, 2025

Based on #207
Fixes #173

Long PR description because changes are a bit obscure maybe...

  • Add a "disjoint" analysis, that tracks inequalities. Anytime it learns either !(a == b) or distinct(a, b, ...), it stores the inequalities. It can then reduce equalities to true/false when possible!
    To avoid its state being too large, it only stores inequalities for: values of type location, or simple variables, or concrete bitvectors; nothing else.

    Furthermore (and this should be discussed), it does a trick where if it absorbs information regarding a variable of type location, it does nos dirty those variables in the PC. In other words, it will learn e.g. |1| != |2|, and |1| and |2| won't be marked as dirty (meaning they become relevant for the next SAT check). This avoids sending a sat check everytime we create a new location to verify that all locations can in fact be distinct (since we "know" they can be).

  • Add a filter : t -> Var.t -> Svalue.ty -> Svalue.t Iter.t -> Svalue.t Iter.t function to Analysis.S. It allows an analysis to filter the possible values for a variable of a given type, using its knowledge.

    This is used in the trivial model check in Bv_solver. We generate an infinite iterator of values for each type, and then filter that with the analyses. We then pick the first N possible values (currently 3), and do model checks with these values. This works a lot better than the previous method (using the variable's ID) and even just doing 2 attempts is usually a much better improvement.

    The filter of each analysis does:

    • Disjoint: if we know the variable is distinct to the iterated value, we filter it out.
    • Equality: if we know some equality for this variable, we substitute the entire iterator by a singleton iterator with that value. E.g. if we know V|1| == V|2| (and decide V|1| is cheaper than V|2|), for a filter on variable |2| we return singleton |1|. Bv_solver is then clever enough to resolve that to the same value as V|1| in the model check!
    • Interval: if we know some interval for the variable, we again substitute the entire iterator by a (non-shuffled) iterator over all valid values. The reason we override the iterator rather than filtering is because in cases where the interval is very narrow (e.g. [0; 10]), filtering means we will randomly generate numbers until we get N values that randomly fall in that range, which is insanely slow. I need to see if shuffling this iterator (requires some work) is a better heuristic!
  • Re-add the AddOvf binary operator to check for overflows. We used to translate these into some comparison of the operand signs and it would just create an unnecessarily large expression, since analyses could usually not learn anything from it.

  • Remove Declared_vars from Bv_solver; instead we learn the types of variables by using iter_var. Much nicer, in my opinion.

  • The minimum value of a pointer in Rust is now its alignment; this helps the trivial model check a lot, since instead of starting at 1 for e.g. alignment 8, it starts at 8 and instantly finds a model :)

  • Added some reductions

@N1ark N1ark requested a review from giltho as a code owner December 21, 2025 17:16
@N1ark N1ark added soteria-core Issues related to the Soteria core library performance Issues relating to performance improvements solver Solver related issues and PRs: new solvers, changes to encoding, etc. labels Dec 21, 2025
@N1ark N1ark changed the title Patricia trees of values Disjoint Analysis and Analysis Filters Dec 21, 2025
@N1ark N1ark changed the title Disjoint Analysis and Analysis Filters Disjoint analysis and analysis filters Dec 21, 2025
@giltho giltho mentioned this pull request Dec 26, 2025
@giltho
Copy link
Contributor

giltho commented Dec 27, 2025

I think that one I can't just review in the car like the previous ones. I need to go a bit deeper and understand the implications of not dirtying variables.

I'm not sure that's valid because it is possible for two locations to be equal.

@N1ark
Copy link
Contributor Author

N1ark commented Dec 29, 2025

I'm not sure that's valid because it is possible for two locations to be equal.

That's fine though? It will query whether L1 and L2 are equal, and the disjoint analysis will just add to the constraints all inequalities relating to L1 and L2.

Feel free to add a test if you want to check but I think it's sound (modulo when you reach 2^N locations and Z3 would tell u they can't all be distinct)

Base automatically changed from patricia-optims to main December 29, 2025 18:13
@N1ark N1ark force-pushed the disjoint-analysis branch from 726bb59 to 2d4b883 Compare December 29, 2025 18:23
@N1ark N1ark force-pushed the disjoint-analysis branch from 2d4b883 to 565ae94 Compare December 29, 2025 18:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Issues relating to performance improvements solver Solver related issues and PRs: new solvers, changes to encoding, etc. soteria-core Issues related to the Soteria core library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants