# Abstract Interpretation with Conjunctions of Predicates

Parameters: a finite set of formulas in program variables (often called predicates in this context). Suppose is the formula 'false'.

**Example:** with

A simple description of this analysis is as the instance of the following idea: assume optimistically that all predicates hold at every program point (the conjunction of all of them holds). Then check whether the result is a sufficiently annotated program. If it is not, then remove the predicates that do not hold, and repeat this process. In the worst case we end up with no predicates holding (the top element of the lattice). There are, however, different ways of removing predicates, and this abstract interpretation formulation gives us one particular way.

Suppose for simplicity we consider as states tuples of values of integer variables and .

For a formula containing variables and and given values we write to denote the truth value of in interpretation that maps to and maps to . (We can formally define this if we have a truth-value function that gives semantics to formulas under a given interpretation function, as in classical semantics of first-order logic.)

## Lattice

Let , so for we have .

We design analysis using Abstract Interpretation Recipe.

Define a syntactic order on :

Clearly, this forms a finite lattice with height .

Note that, because the order is inverted, the full set is the bottom and is the top of the lattice.

**Example:**

Note that implies that the formula

is a valid formula, because every conjunct on right-hand side appears also on the left-hand side.

- Does the converse hold?

We could also define ordering on elements of using the above implication. This is a pre-order. We could work with equivalence classes if we use a theorem prover to check the equivalence of formulas.

## Concretization Function Gamma

Define

i.e. set of states in which all predicates in evaluate to true.

**Example:**
In our example, the definition reduces to

In particular, whenever , then

Empty conjunction is by definition true, so

## Monotonicity of Gamma

We observe that concretization is monotonic. Indeed, suppose . Then , so

which means .

**Example:**

i.e. .

## Abstraction Function Alpha

For , define

**Example:**

## Monotonicity of Alpha

Let .

We wish to prove that .

Let . Then for all we have .

Then also for all we have , because .

Therefore . We showed , so .

## Alpha and Gamma are a Galois Connection

We show are a Galois Connection. We need to show that

This is easy because both conditions reduce to

## Not a Galois Insertion

Is it the case that ?

We show this is not the case. This is because is not injective.

Indeed, take and . Then

Note , but , but it is not the case that . In this particular case,

and so

However, the approach works and is sound even without the condition .

## Least Upper Bound and Greatest Lower Bound

As expected, according to the syntactic ordering, is and is .

That means that, at control-flow graph join points, we intersect sets of predicates that are true on each of the incoming edges.

## Computing sp sharp

**Example:**
Consider computing . We can test for each predicate whether

We obtain that the condition holds for , , and for , but not for . Thus,

On the other hand,

Fix some command given by relation .

Denote . We try to find how to compute . As usual in Abstract Interpretation Recipe, for correctness we need to have

which, thanks to Galois connection is equivalent to

i.e.

We wish to find the smallest lattice element, which is the largest set (this gives the tightest approximation). So we let

By observation above,

Let denote the meaning of relation (according to e.g. Compositional VCG). Then means

i.e.

We then plug this expression back into definition. Because the existentials are left of implication, the result is:

This is the rule to compute .

In summary, to compute , all we need to know is to check whether the following formula is valid

for given and .

**Example of analysis run:**

x = 0; y = 1; while (x < 1000) { x = x + 1; y = 2*x; y = y + 1; print(y); }

Take as the predicates .

x = 0; y = 1; // 0<y, x<y,x=0,y=1, x<1000 // 0<y, 0<=x, x<y while (x < 1000) { // 0<y, 0<=x, x<y, x<1000 x = x + 1; // 0<y, 0<=x, 0<x y = 2*x; // 0<y, 0<=x, 0<x, x<y y = y + 1; // 0<y, 0<=x, 0<x, x<y print(y); } // 0<y, 0<=x, x<y, 1000 <= x

## Pre-Computation using Weakest Preconditions

There is an alternative definition, which can be used to convert the above problem into a finite-state exploration problem. (It will be particularly useful if we consider an extension from conjunctions to disjunction of conjunctions.)

The condition

is equivalent to the validity of the formula

Fix a predicate . Consider all for which the above condition holds, define them as

Clearly, if and , then also . So, we can represent using its minimal elements . These are minimal sufficient sets of predicates that ensure that holds.

Then, we can define equivalently

The advantage of this definition is that we compute for a given predicate and command given by , and then we can efficiently compute for all abstract values by checking inclusion of finite sets.

**Approximation:** The set can have up to elements. To avoid this, we can restrict it to only those subsets that have at most e.g. 3 predicates.

Note that using smaller means that some will be missing, the condition will be false in some cases, and in those cases will not be included in the postcondition, so the resulting abstract element will be larger in the lattice. So the approach is still correct, just not as precise.

## Further Techniques

**Predicates per program point:** Normally each program point has its own set of predicates. Indeed, these predicates may talk about local variables that only exist at those program points. The entire approach remains the same, just whenever computing the set of predicates at a program point , use a local set instead of one global set .

Further challanges:

- compute the sets automatically, i.e. discover predicates automatically (usually through
*counterexample-guided abstraction refinement*) - be more precise when handling paths through programs (path sensitivity, dependent attribute analysis)
- analyze procedures (context-free reachability)

See also predicate abstraction references.