{'fraction_non_alphanumeric': 0.046016659939084804, 'fraction_numerical': 0.007963010531723606, 'mean_word_length': 4.27424037158893, 'pattern_counts': {'":': 0, '<': 2, '<?xml version=': 0, '>': 0, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
Stratified Static Analysis Based on Variable Dependencies

In static analysis by abstract interpretation, one often uses widening operators in order to enforce convergence within finite time to an inductive invariant. Certain widening operators, including the classical one over finite polyhedra, exhibit an unintuitive behavior: analyzing the program over a subset of its variables may lead a more precise result than analyzing the original program! In this article, we present simple workarounds for such behavior.


Introduction
During experiments, we found examples over which classical polyhedral analysis [8], even with alternative widenings [2], would fail to discover some simple program invariants, which could sometimes even be discovered by interval analysis. This would even happen on simple loops, e.g. for(int i=0; i<N; i++), if the loop contained a nested loop not touching i: the analysis would not discover i ≥ 0! It is counterintuitive that difficulties in analyzing the behavior of the program on other variables should lead to imprecise results for i.
In some of these examples, such as this simple loop, the lost invariants could be easily recovered by syntactic pattern-matching, but such techniques are brittle. We therefore searched for techniques inspired by our intuition that poor results on certain variables should not impact variables not depending on them.

Generalities and Notations
We consider the strongest invariant of a loop (or, more generally, of a program), defined as the least fixed point lfp Ψ of a monotone operator Ψ over sets of program states [6]. For instance, in program 2, the strongest invariant of the loop is the least fixed point in (P(Z × Z), ⊆) of the operator Explicit-state model-checking computes such invariants as explicitly represented sets of states (that is, for each state there exists some little data structure). Implicit-state model checking uses compact representations of such sets, such as binary decision diagrams, and computes the least solution of Ψ(X) = X by finding the limit of the ascending sequence X 0 = ∅, X n+1 = Ψ(X n ); for systems with at most n states, this limit is reached within at most n iterations. For infinite state systems such as software programs 1 such an approach is infeasible, because (a) the sets of states X i may be large (or even infinite, if infinite nondeterminism is used) (b) the sequence may not converge within a finite number of iterations.
Abstract interpretation [6,5] solves point (a) by replacing arbitrary sets of states by over-approximations; for instance, a set of points in Z n or Q n may be replaced by an enclosing convex polyhedron [11,8,12]. A given analysis thus restricts itself to a given abstract domain of sets of states; in this article, we focus, as an example, on the domain of polyhedra, but there exist many other abstract domains, for numerical [16] or non-numerical states. The operator Ψ on concrete states is replaced by an abstract operator Ψ ♯ , satisfying a soundness condition Ψ(X ♯ ) ⊆ Ψ ♯ (X ♯ ) for all X ♯ . 2 Problem (b), that is, failure for the sequence X ♯ n+1 = Ψ ♯ (X ♯ n ) to become stationary, remains if the abstract domains contains infinite strictly ascending sequences; 3 this is for instance the case of the domain of convex polyhedra. Some form of convergence acceleration is thus needed. Starting with u ♯ 0 = ∅, upwards iterations with widening [5,6] x ⊔ y is such that x, y ⊆ x ⊔ y (in the case of polyhedra, ⊔ is generally taken to be the convex hull), and ▽ is a widening operator, such that for all x ⊆ y, y ⊆ x▽y (soundness property), and any sequence of the form u ♯ n+1 = u ♯ n ▽v ♯ n , where v ♯ n is any other sequence, is stationary: after a certain N , it is constant (termination property). Then, Once an inductive invariant u ♯ N is obtained, it may be refined by narrowing iterations, which in practice generally consist in computing Ψ ♯ k (u ♯ N ) until the sequence becomes stationary or k exceeds a preset limit.
Widening operators have various unpleasant properties. The best known is that they bring imprecision: the result of widening/narrowing iterations may be strictly larger than the least element of the abstract domain that is an inductive invariant, let alone an invariant (in Sec. 5 we shall list some alternative approaches that do not suffer from this inconvenience, at the expense of generality). The contribution of this article is a generic method to reduce some of the imprecision induced by widening.

Motivating Example
Classical polyhedral analysis [8], 5 when applied to Listing 1, discovers that i ≥ 1 ∧ i ≤ 5 is an invariant at the head of the loop. Yet, running the same analysis on Listing 2 yields i ≤ 5 but not i ≥ 1.
This example is not fortuitous: it models how to address consecutive lines of a matrix in lower triangular packed storage mode. In that memory-effective approach, the matrix is stored in memory as a unidimensional array, each line next to the preceding one, and line number i only uses i positions in the array: j is the index of the start of the line in the array.
Program 1 is an abstraction of Program 2: each execution of the latter maps to an execution of the former. Yet, the analysis of the former produces a more precise loop invariant than the analysis of the latter. This is an example of the non-monotonicity of analyzes using widenings, a long-known phenomenon [7, ex. 11]: a more precise abstraction may ultimately lead to less precision in the final analysis result.
So far, so good: such polyhedra still imply i ≥ 1. At the next iteration, however, this constraint is lost and one gets the polyhedron −i + j ≥ −1, and finally ⊤, the whole plane. The constraint i ≤ 5 is recovered by one step of downwards iteration. Analysis with the improved widening proposed by Bagnara et al. [2], as implemented in the Parma Polyhedra Library, yields a different iteration sequence, but still reaches ⊤ at the end. If one runs a polyhedral analysis on Program 1, one gets the inductive invariant 1 ≤ i ≤ 5, which is also valid for Program 2. Intersecting this invariant with the output of the widening in the analysis of Program 2 yields a reasonably precise polyhedron (Table 1).
Thus, the basic idea of our method: run preliminary analyzes over abstractions of the program obtained by removing some of the variables, in order to refine the analysis of the complete program. In order to further convey our intuition, let us remark that Prog. 2 is the result of loop fusion over the following program : Normal forward polyhedral analysis on this program will find good invariants for both loops. In particular, the second loop may not perturb analysis of the first loop. It seems reasonable that the same applies to the code after loop fusion.  Table 1: Comparison of classic static analysis (upward iterations with widening ▽ followed by descending iterations) and stratified static analysis on Program 2. Classic analysis loses the constraint i ≥ 1 and finds ⊤ in 5 iterations. The upper bound i ≤ 5 is found with one narrowing iteration. Stratified analysis on the stratum consisting of variable i first finds 1 ≤ i ≤ 5. Then, it analyzes stratum i, j and intersect with result of stratum i. A fixed point is found after 4 iterations (u ♯ 3 , last line). The table also shows the polyhedra found after two narrowing iterations. The resulting polyhedron, even without narrowing iterations, is much more precise than the one found by classic analysis.
The same code could have been the result of the compilation into C of a data-flow program (e.g. Simulink or Lustre) consisting in a ramp generator and an integrator: Again, it seems natural that the analysis of the integrator should not hamper the analysis of the ramp.

Stratified Analysis
We have investigated two approaches. In stratified analysis, we successively perform several static analyzes by abstract interpretation, the results from each analysis being used to refine the following ones. In stratified widening, a single analysis pass is performed, but with a widening improving on and derived from the traditional widening on polyhedra.

Dependency Strata
We consider a set S of subsets of the set of variables V of the program, such that V ∈ S; we order it by inclusion. An immediate predecessor of S ∈ S, denoted by S ′ ≺ S, is S ′ such that S ′ S and there is no S ′′ such that S ′ S ′′ S. In practice, if we have a relationship v 1 → v 2 meaning "v 1 flows into v 2 through some computation" or "v 2 depends on v 1 ", then the elements of S are, in addition to V itself, subsets S of V closed by: if v ∈ S and v ′ → v, then v ′ ∈ S. One way to construct such subsets is to compute for each variable v the set S(v) = {v ′ | v ′ → v}, and add this set to S unless it is already present. For better efficiency, one computes the strongly connected components of →, and takes S(v) for one v in each component.
Note that → needs not be the semantics dependency relation, which takes into account both data and control dependencies. In intuitive (and imprecise) terms, a variable x is said to be data-dependent on a variable y if x is assigned to by an expression where y appears; a variable x is said to be controldependent on a variable y if x is assigned in a program branch executed or not executed according to the value of y. Collecting all program elements on which a variable depends, through data or control dependencies, is known as slicing [27]. If → takes into account all dependencies, then S(v) is the slice of variables on which v depends.
A helpful intuition of our method is that it performs analyzes on program slices of increasing size; but this is somewhat misleading, because we do not make any assumption on → and thus it does not necessarily reflect all dependencies. In particular, ignoring control dependencies, compared conventional slicing, may produce simpler slices, of a more manageable size -X. Rival, when developing the Astrée static analyzer, observed that, for many variables, the slice corresponded to approximately 80% of the code, thus slicing did not significantly simplify the program [19].

Informal Definition
Let S be a subset of the variables in program P . We note P |S the program P where all references to variables outside S have been replaced by nondet() nondeterministic choices.
For any program P , let C(P ) be its collecting semantics: the set of reachable states of P . In order to simplify notations, for S ⊆ S ′ , we identify sets of states referring to the variables in S with their completion by all values for variables in S ′ \ S. For any S, P |S is a safe abstraction of P : For any program P , let A(P ) be the result of static analysis of P . Correctness of the analysis means C(P ) ⊆ A(P ). Let A(P, K) be the result of the static analysis of P where the semantics of P is restricted to states in K: in other words, all states outside of K are removed from the transition relation. For any K ⊇ C(P ), C(P ) ⊆ A(P, K).
For each S ∈ S, we compute the intermediate analysis result R(S) after all R(S ′ ), S ′ ≺ S, have been computed, as follows: Remark that in this formula, we could have made S ′ to range over all predecessors without changing the result; however, this would have been less efficient. By induction on the length of the ≺-chains, for all S, R(S) ⊇ C(P |S ). At the end, R(V) ⊇ C(P ) is a correct analysis result for the whole program; in fact, any R(S) ⊇ C(P ), so one can stop the analysis at any step, for instance because of a time limit. This is the analysis performed in §1.2, with S = {{i}, {i, j }}.

Formal Definitions and Variants
Let S ∈ S. We assume that the result R(S ′ ) of the analysis for all S ′ ≺ S has already been computed. Let K ♯ = S ′ ≺S R(S ′ ); we assume that lfp Ψ ⊆ R(S ′ ) for all S ′ ≺ S and thus that lfp Ψ ⊆ K ♯ . The analysis described at Eqn. 3 is defined by the sequence: We compute the limit R(S) = u ♯ N of that stationary sequence, and output u ♯ N ∩ K ♯ . Let us note Ψ |A (X) = Ψ(X ∩ A) ∩ A. In other words, Ψ |A is Ψ with everything outside of A being discarded. The following lemma means that we do not change the strongest invariant by throwing out unreachable states in the definition of the semantics, which is intuitive.
Proof. lfp Ψ |A is the limit of the ascending sequence defined by X 0 = ∅, X n+1 = Ψ |A (X n ), lfp Ψ that of Y 0 = ∅, Y n+1 = Ψ(Y n ). By induction, for all n, X n = Y n .
Corollary 2. u ♯ N , and thus u ♯ N ∩ K ♯ , includes lfp Ψ, that is, the reachable states.
Proof. Proof Because y ⊆ x▽y and y ⊆ x ⊔ y for all x, y, The result follows from the lemma. We conclude that, by induction over ≺, for all S, lfp Ψ ⊆ R(S).
We shall now describe a subtly different iteration scheme, which supposes some additional properties of ▽: Definition 3. We say that ▽ satisfies the "up to" termination condition if for any fixed K ♯ , any This property ensures the correctness of widening "up to" [12], a well-known improvement to widening, and is true of the standard widening on polyhedra as well as Bagnara et al.'s improved widening [2, p. 53]. Using the same notations and hypotheses as above, we use this iteration: Again, once we get a stationary value u ♯ N in this sequence, then it is such that lfp Ψ ⊆ u ♯ N : The result follows from Lem. 1.

Stratified Widenings
An alternative to the method described in the preceding section, which runs successive analyzes of increasing precision, is to run a single analysis over a reduced product [5] of polyhedral domains, but with a special widening operator. We shall provide two options for that operator.

Widening with or without Reduction
We distinguish the internal state (P S ) S∈S of the iteration sequence from the set of states represented, as in [17]. The various abstract operations will therefore continue operating on polyhedra as usual: only the widening operator is replaced.
Our widening operators will take a tuple (P S ) S∈S as a first argument and single polyhedron Q as a second argument. A tuple (P S ) S∈S represents the polyhedron We note π S (P ) the projection of polyhedron P onto the variables in S. If S ⊆ S ′ , a polyhedron on the variables in S shall be also considered as a polyhedron on the variables in S ′ by keeping the same constraints. This means, in particular, that P ⊆ π S (P ) for any P and S.
The first widening operator is very simple: where ▽ is any widening on polyhedra. This widening converges because each coordinate converges, since ▽ is a widening. It is obvious that, if (P S ) S∈S is the resulting limit, then γ ((P S ) S∈S ) is an inductive invariant. The second widening applies internal reductions. (R S ) S∈S denotes (P S ) S∈S ▽ 2 (Q S ) S∈S . We compute the R S in ascending order with respect to ≺, with the convention that the intersection of zero polyhedra is the full polyhedron: Theorem 5. Assuming that ▽ is a widening satisfying the "up to" termination condition (Def. 3), ▽ 2 is a widening.
Proof. Proof Let u (n+1) = u (n) ▽ 2 v (n) be a sequence, with u (n) ⊑ v (n) for all n; each element u (n) consists in u (n) S for S ∈ S. We prove that for all S ∈ S the sequence u (n) S is stationary, by induction over ≺.
For S with no predecessor, (u S ′ ) is stationary; thus there is a N such that for n ≥ N , all (u (n) The results follows from ▽ being a widening satisfying our additional property.
Instead of polyhedra, one may use other abstract domains fitted with an operation ⊓ such that a ∩ b ⊆ a ⊓ b for all a, b. Let us however note that ▽ 1 and ▽ 2 yield the same results as the ordinary widening ▽ if applied to domains, such as difference bound matrices or octagons [16] where ▽ and projection commute: π S (P )▽π S (Q) = π S (P ▽Q), and therefore that they bring no improvement for such domains: the P S are just projections of P V . More precisely: Lemma 6. Assume that π S (P )▽π S (Q) = π S (P ▽Q) for all P and Q. Any iteration sequence of the form P (n+1) = P (n) ▽Q (n) then satisfies, for all n and S ∈ S, P (n) S = π S (P (n) V ), assuming this equality holds for n = 0.
Proof. Proof Regarding ▽ 1 : by induction over n, for any S, P ). Regarding ▽ 2 : by induction over n, then by induction over S with respect to ≻: (P , since for any X and S ′ ≻ S, π S ′ (X) ∩ π S (X) = π S (X).

Generalized Reduction Leads to Nontermination
Communicating information between several abstract domains used at the same time is sometimes referred to as a closure or reduction operation. Our ▽ 2 operation includes a partial closure, with information flowing from a to b if a ≺ b, but not the reverse. One could wonder about applying reductions in all directions. Unfortunately, we would lose the termination property of widening, as demonstrated by the following example. 7 Listing 3: Alternating increments i n t i =0 , j = 0 ; while ( t r u e ) { i f ( i <= j ) i ++; e l s e j ++; } i j This loop has different behaviors on odd and even iterations: at iteration 2n, i = n and j = n; at iteration 2n + 1, i = n + 1 and j = n. The results of a static analysis with polyhedra on (i, j), and unions instead of widenings, are, in constraint form: P ♯ 2n : P ♯ ∧ i ≤ n and P ♯ 2n+1 : P ♯ ∧ j ≤ n, P ♯ denoting i ≥ j ∧ i ≤ j + 1 ∧ j ≥ 0 (we identify P ♯ with the conjunctions of the constraints that define it). If for the iteration n = 4 we use widening, 8 we instead obtain P ♯ 4 = P ♯ , which is an inductive invariant.
We have established that this program poses no challenge to "classical" polyhedral analysis. The same is true if we apply one of the analyzes of Sec. 2 or one of the widenings of Sec. 3.1. Let us now see what happens if we modify the ▽ 2 operator of Sec. 3.1 by allowing reductions not following ≺.
Instead of the definition given at Eq. 8, we instead initialize all R S to P S ▽π S (Q), then apply some replacements, or reductions, of the form: If we reach a fixed point for this replacement system, using the terminology from octagons [16], we say that we have applied the closure operation. Let us first remark that γ ((R S ) S∈S ) is left unchanged any number of such reductions: . The iterations with widening followed by closure behave, on I ♯ and J ♯ , like those with unionsand they do not converge within finite time. Observe that this happens because we alternatively reduce I ♯ → P ♯ → J ♯ and J ♯ → P ♯ → I ♯ , whereas the definitions of Sec. 3.1 only allow I ♯ → P ♯ and J ♯ → P ♯ . 7 The fact that widenings followed by reductions with cycles (reduce a using b, then reduce b using a) may not ensure termination is already known. For instance, closure in difference-bound matrices and octagons breaks termination. [16, example 3.7.3, p. 85] 8 Applying unions at n first iterations and then applying widening is a standard technique known as delayed widening.

Experimental Results
The stratified analysis presented in section 2, in both variants (Eqn. 4 and Eqn. 5), was evaluated against the classical analysis described by Eqn. 2 on a set of benchmarks used by STMicroelectronics in the development cycle of its compilers, in addition to a few specific examples such as the one from Sec. 1.2. LAO Kernels is a set of benchmarks internally used for the evaluation of compilers code generators and optimizations. It is mainly composed of small computational kernels representative of the target applications of STMicroelectronics (audio and video stream processing, embedded device control), associated with a testing harness to be able to run them on the target processor. It contains 63 functions, of which 49 contain at least one loop. Loops have to exhibit some properties, like a nonlinear relation between variables in the loop scope, in order to benefit from this method. Stratified analysis finds a more precise invariant for 5 of these functions.
Among these 5 functions, discrete cosine transform has three nested loops. The intuition of why stratified analysis performs better is it obtains an invariant for the indices affected by the outer loop before attempting to analyze the inner loop, thus preventing imprecisions during the inner loop analysis to affect the invariant on the outer loop indices.
The dependency relation used to create the strata is based on a modified dataflow graph; strongly connected components (SCC) are reduced to super-nodes, while keeping the existing dependency relations. Initial strata stem from the root nodes of this SCC dependency graph, additional ones are created by following the dependency relations until one stratum encompasses all variables in the dependency graph. In the while loop of the listing 2, the variable j depends from i; the SCC nodes simply consist of {i} and {j}, and the analysis creates two strata {i} and {i, j}.
The two variants of stratified analysis described by Eqn. 4 and Eqn. 5 find the same results, and in all cases find invariants equal to or stronger than those obtained by the classical analysis. Bagnara et al.'s alternate widening [2] yields iteration sequences different from those obtained by the classical widening, but ultimately finds the same invariant; thus, our approach improves on theirs on this benchmark set. Table 2 shows the number of variables in the outermost stratum, along with the number of strata considered by the analysis and its overhead with respect to the standard analysis using only the classic widening. Some programs exhibit a large number of strata, impacting the cost of the analysis. It is possible to run the expensive stratified analysis after a first cheaper standard analysis, while focusing on certain loop nests (those reaching ⊤ for instance).  Table 2: Number of variable in the last stratum, number of strata and overhead of stratified analysis for programs that benefit from this method. The baseline for overhead measures is the classic analysis using bare widenings, without delay or widening-up-to).
We rely on the APRON numerical abstract domain library 9 [14] for all abstract domain computations. APRON implements, among other domains, convex polyhedra with the classical widening, with linearization of nonlinear expressions following Miné's approach [15]. In addition, in order to compare with Bagnara et al.'s alternate widening, we used the Parma Polyhedra Library 10 [1] (with the classical widening, the PPL produces exactly the same results as APRON up to equivalence of constraints, thus providing a means to test for possible bugs in the polyhedral computations).

Related Work
It has long been recognized that analysis using polyhedra over all variables in a program, or even all variables in a single function, is unfeasible because of the high complexity of polyhedral operations in higher dimensions. This is also true of weaker domains such as octagons. For this reason, the Astrée analyzer uses relational domains only on "packs" of variables [3,4]: for instance, if we have four variables a, b, c, d and two packs {a, b} and {b, c, d}, the analysis will track relationships between a, b and b, c, d separately: no direct relation will be established between a and d.
A related approach is factoring of polyhedra [13]: when a polyhedron P is a Cartesian product P 1 × . . .×P n of polyhedra in lower dimension, with respectively v i vertices (or, more generally, generators), it is often advantageous to keep this product representation as much as possible instead of considering it as a polyhedron of i v i vertices, because of algorithms that need to work on the generator representation. An alternative is to dispense totally with the generator representation [23,22].
The literature on slicing is abundant, since the early 1980s [27]. Syntactic slicing extracts all program statements, variables etc. that affect the value of variable v, or, rather, a safe superset thereof. The resulting slice is executable, which is interesting for testing or debugging methods, but less so for abstract interpretation; this is why we may use lax dependency relations (Sec. 2.1), since we in effect replace any unknown dependency by nondeterministic choice. Semantic slicing relaxes the requirement that the resulting program be a syntactic subset of the original program [26]. X. Rival considers a form of abstract semantic slicing [19,20], where program executions are restricted to those affecting the reachability of undesirable program states (alarms); in contrast, our method does not suppose we have a set of properties (absence of alarms) to prove.
The design of widening operators is surprisingly difficult. The original widening operator on polyhedra [8] was sensitive to syntax: different ways of representing the same polyhedron in constraint form yielded different widened polyhedra; this problem was later fixed [11]. Because the result of iterations with widening is non-monotonic, precision is highly heuristic: in particular, replacing a widening operator by one producing smaller polyhedra at each iteration does not necessarily translate in a smaller invariant in the end [2, p. 42].
Despite this caveat, many widening operators have been proposed for convex polyhedra [2, p. 30] [22]. Many are variants on the classical widening: some apply union in lieu of the classical widening in a way that does not preclude termination [2]; the "up to" widening, also known as widening with thresholds or limited widening [12], extracts possibly relevant constraints from the program and keeps in P ▽Q the constraints from that set satisfied by both P and Q; a related idea is widening with landmarks, which uses estimates of the number of supplementary iterations necessary to enable a currently disabled transition [24]; widening with a care set uses a proof goal and counterexamples in order to guide the widening [25]. Our approach is largely orthogonal to these, and in fact can be combined with them.
In the recent years, there has been much interest in techniques for inferring invariants without doing conventional Kleene iterations. Policy iteration (also called strategy iteration; the technique is inspired by game theory) exists in two flavors. Descending policy iteration [9] solves a descending sequence of least fixed points of simpler operators; these least fixed points may be solved approximately using widenings, thus this technique is orthogonal to ours. In contrast, ascending policy iteration [10] and other techniques based on constraint programming [21] or quantifier elimination [18] provide some optimality guarantees, but impose restrictions on the kind of program instructions supported. Such restrictions may be lifted by abstracting program operations into the supported subset [16], which may in turn entail an outer loop with widenings.
We finally note that nothing in our approach is specific to polyhedra, or even to numerical domains.

Conclusion
Following our intuition that failure to analyze well parts of a program should not negatively influence precision on other parts not depending on them, we proposed four analysis schemes: two proceed by analyzes of restrictions of the program code to variable subsets, the other ones use alternative widening operators. Though we focused on improving the classical polyhedral analysis, two of our methods apply to any abstract domain, and the two other ones make a reasonable assumption on the underlying abstract domain and its widening operator.