Verifying Array Manipulating Programs with Full-Program Induction
aa r X i v : . [ c s . S E ] F e b Verifying Array Manipulating Programs withFull-Program Induction
Supratik Chakraborty , Ashutosh Gupta , and Divyesh Unadkat , Indian Institute of Technology Bombay, Mumbai, India { supratik,akg } @cse.iitb.ac.in TCS Research, Pune, India [email protected]
Abstract.
We present a full-program induction technique for proving (asub-class of) quantified as well as quantifier-free properties of programsmanipulating arrays of parametric size N . Instead of inducting over in-dividual loops, our technique inducts over the entire program (possiblycontaining multiple loops) directly via the program parameter N . Signif-icantly, this does not require generation or use of loop-specific invariants.We have developed a prototype tool Vajra to assess the efficacy of ourtechnique. We demonstrate the performance of
Vajra vis-a-vis severalstate-of-the-art tools on a set of array manipulating benchmarks.
Programs with loops manipulating arrays are common in a variety of applica-tions. Unfortunately, assertion checking in such programs is undecidable. Exist-ing tools therefore use a combination of techniques that work well for certainclasses of programs and assertions, and yield conservative results otherwise. Inthis paper, we present a new technique to add to this arsenal of techniques.Specifically, we focus on programs with loops manipulating arrays, where thesize of each array is a symbolic integer parameter N ( > N . Thus, the problem we wish to solve can be viewedas checking the validity of a parameterized Hoare triple { ϕ ( N ) } P N { ψ ( N ) } forall values of N ( > P N computes with arrays of size N ,and N is a free variable in ϕ ( · ) and ψ ( · ). Fig. 1(a) shows an example of one suchHoare triple, written using assume and assert . This triple effectively verifiesthat P i − j =0 (cid:16) P j − k =0 · ( k + 1) (cid:17) = i for all i ∈ { . . . N − } , and for all N >
FreqHorn [9], Tiler [3], Vaphor [23], or Booster [1] to prove the post-condition correct. Infact, none of the above tools succeed in automatically proving the post-conditionin Fig. 1(a). In contrast, the technique presented in this paper, called full-programinduction , proves the post-condition in Fig. 1(a) correct within a few seconds.Like several earlier approaches [28], full-program induction relies on math-ematical induction to reason about programs with loops. However, the way in / assume(true)1. for (int t1=0; t1
Fig. 1.
Original and simplified Hoare triples which the inductive claim is formulated and proved differs significantly. Specif-ically, (i) we do not require explicit or implicit loop-specific invariants to beprovided by the user or generated by a solver (viz. by constrained Horn clausesolvers [20,14,9] or recurrence solvers [25,16]), (ii) we induct on the full program (possibly containing multiple loops) with parameter N and not on iterationsof individual loops in the program, and (iii) we perform non-trivial correct-by-construction code transformations , whenever feasible, to simplify the induc-tive step of reasoning. The combination of these factors often reduces reasoningabout a program with multiple loops to reasoning about one with fewer (some-times even none) and “simpler” loops, thereby simplifying proof goals. In thispaper, we demonstrate this, focusing on programs with sequentially composed,but non-nested loops.As an illustration of simplifications that can result from application of full-program induction, consider the problem in Fig. 1(a) again. Full-program induc-tion reduces checking the validity of the Hoare triple in Fig. 1(a) to checking thevalidity of two “simpler” Hoare triples, represented in Figs. 1(b) and 1(c). Notethat the programs in Figs. 1(b) and 1(c) are loop-free. In addition, their pre- andpost-conditions are quantifier-free. The validity of these Hoare triples (Figs. 1(b)and 1(c)) can therefore be easily proved, e.g. by bounded model checking [5] witha back-end SMT solver like Z3 [24]. Note that the value computed in each it-eration of each loop in Fig. 1(a) is data-dependent on previous iterations of therespective loops. Hence, none of these loops can be trivially translated to a setof parallel assignments.Invariant-based techniques, viz. [12,15,22,6,13,29,2,18], are popularly usedto reason about array manipulating programs. If we were to prove the as-sertion in Fig. 1(a) using such techniques, it would be necessary to use ap-propriate loop-specific invariants for each of the three loops in Fig. 1(a). Theweakest loop invariants needed to prove the post-condition in this example are: ∀ i ∈ [0 ...t −
1] ( A [ i ] = 6 i + 6) for the first loop (lines 1-4), ∀ j ∈ [0 ...t −
1] ( B [ j ] = 3 j + 3 j + 1) ∧ ( A [ j ] = 6 j + 6) for the second loop (lines 5-8), and ∀ k ∈ [0 ...t −
1] ( C [ k ] = k ) ∧ ( B [ k ] = 3 k + 3 k + 1) for the third loop (lines2-12). Unfortunately, automatically deriving such quantified non-linear loop in-variants is far from trivial. Template-based invariant generators, viz. [11,8], areamong the best-performers when generating such complex invariants. However,their abilities are fundamentally limited by the set of templates from which theychoose. We therefore choose not to depend on invariants for individual loops inour work at all. Instead of inducting over the iterations of each individual loop,we propose to reason about the entire program (containing one or more loops)directly, while inducting on the parameter N . Needless to say, each approachhas its own strengths and limitations, and the right choice always depends onthe problem at hand. Our experiments show that full-program induction is ableto solve several difficult problem instances with an off-the-shelf SMT solver ( Z3 )at the back-end, which other techniques either fail to solve these instances, orrely on sophisticated recurrence solvers.The primary contributions of our work can be summarized as follows. – We introduce the notion of full-program induction for reasoning about asser-tions in programs with loops manipulating arrays. – We present practical algorithms for full-program induction. – We describe a prototype tool
Vajra that implements the algorithms, usingan off-the-shelf SMT solver, viz. Z3 , at the back-end to discharge verificationconditions. Vajra outperforms several state-of-the-art tools on a suite ofarray-manipulating benchmark programs.
Related Work.
Earlier work on inductive techniques can be broadly categorizedinto those that require loop-specific invariants to be provided or automaticallygenerated, and those that work without them. Requiring a “good” inductive in-variant for every loop in a program effectively shifts the onus of assertion checkingto that of invariant generation. Among techniques that do not require explicitinductive invariants or mid-conditions for each loop, there are some that requireloop invariants to be implicitly generated by a constraint solver. These includetechniques based on constrained Horn clause solving [20,14,9,23], accelerationand lazy interpolation for arrays [1] and those that use inductively defined pred-icates and recurrence solving [25,16], among others. Thanks to the impressivecapabilities of modern constraint solvers and the effectiveness of carefully tunedheuristics for stringing together multiple solvers, this approach has shown a lotof promise in recent years. However, at a fundamental level, these formulationsrely on solving implicitly specified loop invariants garbed as constraint solvingproblems. There are yet other techniques, such as that in [27], that truly donot depend on loop invariants being generated. In fact, the technique of [27]comes closest to our work in principle. However, [27] imposes severe restrictionson the input programs, and the example in Fig. 1 does not meet these restric-tions. Therefore, the technique of [27] is applicable only to a small part of theprogram-assertion space over which our technique works. Techniques such astiling [3] reason one loop at a time and apply only when loops have simple datadependencies across iterations (called non-interference of tiles in [3]). It effec-tively uses a slice of the post-condition of a loop as an inductive invariant, and3lso requires strong enough mid-conditions to be generated in the case of sequen-tially composed loops. We circumvent all of these requirements in the currentwork. For some other techniques for analyzing array manipulating programs,please see [6,18,17].
Recall that our objective is to check the validity of the parameterized Hoare triple { ϕ ( N ) } P N { ψ ( N ) } for all N >
0. At a high level, our approach works like anyother inductive technique. Thus, we have a base case, where we verify that theparameterized Hoare triple holds for some small values of N , say 0 < N ≤ M .We then hypothesize that { ϕ ( N − } P N − { ψ ( N − } holds for some N > M ,and try to show that this implies { ϕ ( N ) } P N { ψ ( N ) } . While this sounds simplein principle, there are several technical difficulties en route. Our contributionlies in overcoming these difficulties algorithmically for a large class of programsand assertions, thereby making full-program induction a viable and competitivetechnique for proving properties of array manipulating programs.We rely on an important, yet reasonable, assumption that can be statedas follows: For every value of N ( > , every loop in P N can be statically un-rolled a fixed number (say f ( N ) ) of times to yield a loop-free program c P N thatis semantically equivalent to P N . Note that this does not imply that reason-ing about loops can be translated into loop-free reasoning. In general, f ( N ) isa non-constant function, and hence, the number of unrollings of loops in P N may strongly depend on N . In our experience, loops in a vast majority of arraymanipulating programs (including Fig. 1(a)) satisfy the above assumption. Con-sequently, the base case of our induction reduces to checking a Hoare triple fora loop-free program. Checking such a Hoare triple is easily achieved by compil-ing the pre-condition, program and post-condition into an SMT formula, whose(un)satisfiability can be checked with an off-the-shelf back-end SMT solver.The inductive step is the most complex one, and is the focus of the rest of thepaper. Recall that the inductive hypothesis asserts that { ϕ ( N − } P N − { ψ ( N − } is valid. To make use of this hypothesis in the inductive step, we must relatethe validity of { ϕ ( N ) } P N { ψ ( N ) } to that of { ϕ ( N − } P N − { ψ ( N − } .We propose doing this, whenever possible, via two key notions – that of “differ-ence” program and “difference” pre-condition. Given a parameterized program P N , intuitively the “difference” program ∂ P N is one such that P N − ; ∂ P N is se-mantically equivalent to P N , where “;” denotes sequential composition. It turnsout that for our purposes, the semantic equivalence alluded to above is not re-ally necessary; it suffices to have ∂ P N such that { ϕ ( N ) } P N { ψ ( N ) } is valid iff { ϕ ( N ) } P N − ; ∂ P N { ψ ( N ) } is valid. We will henceforth use this interpretation ofa “difference” program. The “difference” pre-condition ∂ϕ ( N ) is a formula suchthat (i) ϕ ( N ) → ( ϕ ( N − ∧ ∂ϕ ( N )) and (ii) the execution of P N − doesn’taffect the truth of ∂ϕ ( N ). Computing ∂ P N and ∂ϕ ( N ) is not easy in general,and we discuss this in detail in the rest of the paper.Assuming we have ∂ P N and ∂ϕ ( N ) with the properties stated above, theproof obligation { ϕ ( N ) } P N { ψ ( N ) } can now be reduced to proving { ϕ ( N − } P N − { ψ ( N − } and { ψ ( N − ∧ ∂ϕ ( N ) } ∂ P N { ψ ( N ) } . The first triplefollows from the inductive hypothesis. Proving the second triple may requirestrengthening the pre-condition, say by a formula Pre ( N − { ( ψ ( N − ∧ Pre ( N − ∧ ∂ϕ ( N ) } ∂ P N { ψ ( N ) ∧ Pre ( N ) } . While this is somewhat reminiscent of loop in-variants, observe that Pre ( N ) is not really a loop-specific invariant. Instead, itis analogous to computing an invariant for the entire program, possibly con-taining multiple loops. Specifically, the above process strengthens both the pre-and post-condition of { ψ ( N − ∧ ∂ϕ ( N ) } ∂ P N { ψ ( N ) } simultaneously using Pre ( N −
1) and
Pre ( N ), respectively. The strengthened post-condition of the re-sulting Hoare triple may, in turn, require a new pre-condition Pre ′ ( N −
1) to besatisfied. This process of strengthening the pre- and post-conditions of the Hoaretriple involving ∂ P N can be iterated until a fix-point is reached, i.e. no furtherpre-conditions are needed for the parameterized Hoare triple to hold. While thefix-point was quickly reached for all benchmarks we experimented with, we alsodiscuss how to handle cases where the above process may not converge easily.Note that since we effectively strengthen the pre-condition of the Hoare triple inthe inductive step, for the overall induction to go through, it is also necessary tocheck that the strengthened assertions hold at the end of each base case check.The technique described above is called full-program induction , and the followingtheorem guarantees its soundness. Theorem 1.
Given { ϕ ( N ) } P N { ψ ( N ) } , suppose the following are true:1. For N > , { ϕ ( N ) } P N − ; ∂ P N { ψ ( N ) } holds iff { ϕ ( N ) } P N { ψ ( N ) } holds.2. For N > , there exists a formula ∂ϕ ( N ) such that (a) ∂ϕ ( N ) doesn’t referto any program variable or array element modified in P N − , and (b) ϕ ( N ) → ϕ ( N − ∧ ∂ϕ ( N ) .3. There exists an integer M ≥ and a parameterized formula Pre ( M ) suchthat (a) { ϕ ( N ) } P N { ψ ( N ) } holds for < N ≤ M , (b) { ϕ ( M ) } P M { ψ ( M ) ∧ Pre ( M ) } holds, and (c) { ψ ( N − ∧ Pre ( N − ∧ ∂ϕ ( N ) } ∂ P N { ψ ( N ) ∧ Pre ( N ) } holds for N > M .Then { ϕ N } P N { ψ N } holds for all N ≥ .Proof. For 0 < N ≤ M , condition 3(a) ensures that { ϕ ( N ) } P N { ψ ( N ) } holds.For N > M , note that by virtue of condition 1 and 2(b), { ϕ ( N ) } P N { ψ ( N ) } holds if { ϕ ( N − ∧ ∂ϕ ( N ) } P N − ; ∂ P N { ψ ( N ) ∧ Pre ( N ) } holds. With ψ ( N − ∧ Pre ( N −
1) as a mid-condition, and by virtue of condition 2(a), the latterHoare triple holds for
N > M if { ϕ ( M ) } P M { ψ ( M ) ∧ Pre ( M ) } holds and { ψ ( N − ∧ Pre ( N − ∧ ∂ϕ ( N ) } ∂ P N { ψ ( N ) ∧ Pre ( N ) } holds for all N > M .Both these triples are seen to hold by virtue of conditions 3(b) and (c). ⊓⊔ We now discuss the full-program induction algorithm, focusing on generationof three crucial components: difference program ∂ P N , difference pre-condition ∂ϕ ( N ), and the formula Pre ( N ) for strengthening pre- and post-conditions.5 .1 Preliminaries We consider array manipulating programs generated by the grammar shownbelow (adapted from [3]). PB ::= StSt ::= v := E | A [ E ] := E | if ( BoolE ) then St else St | St ; St | for ( ℓ := 0; ℓ < E ; ℓ := ℓ +1) { St1 } St1 ::= v := E | A [ E ] := E | if ( BoolE ) then St1 else
St1 | St1 ; St1E ::=
E op E | A [ E ] | v | ℓ | c | N op ::= + | - | * | / BoolE ::=
E relop E | BoolE AND BoolE | NOT BoolE | BoolE OR BoolE
This grammar restricts programs to have non-nested loops. While this limitsthe set of programs to which our technique currently applies, there is a largeclass of useful programs, with possibly long sequences of loops, that are includedin the scope of our work. In reality, our technique also applies to a subclassof programs with nested loops. However, characterizing this class of programsthrough a grammar is a bit unwieldy, and we avoid doing so for reasons of clarity.A program P N is a tuple ( V , L , A , PB , N ), where V is a set of scalar variables, L ⊆ V is a set of scalar loop counter variables, A is a set of array variables, PB is the program body, and N is a special symbol denoting a positive integerparameter. In the grammar shown above, we assume A ∈ A , v ∈ V \L , ℓ ∈ L and c ∈ Z . Furthermore, “ relop ” is assumed to be one of the relational operators and“ op ”is an arithmetic operator from the set { +, -, *, / } . We also assume that eachloop L has a unique loop counter variable ℓ which is initialized at the beginning of L and is incremented by 1 at the end of each iteration. Assignments in the body of L are assumed not to update ℓ . Finally, for each loop with termination condition ℓ < E , we assume that E is an expression in terms of N . We denote by k L ( N )the number of times loop L iterates in the program with parameter N . We verifyHoare triples of the form { ϕ ( N ) } P N { ψ ( N ) } , where ϕ ( N ) and ψ ( N ) are eitheruniversally quantified formulas of the form ∀ I ( Φ ( I, N ) = ⇒ Ψ ( A , V , I, N )) orquantifier-free formulas of the form Ξ ( A , V , N ). In the above, I is a sequence ofarray index variables, Φ is a quantifier-free formula in the theory of arithmeticover integers, and Ψ and Ξ are quantifier-free formulas in the combined theoryof arrays and arithmetic over integers.Static single assignment (SSA) [26] is a well-known technique for renamingscalar variables such that a variable is written at most once in a program. Forour purposes, we also wish to rename arrays so that each loop updates its ownversion of an array and multiple writes to an array element within the same loophappen on different versions of the array. Array SSA [19] renaming has beenstudied earlier in the context of compilers to achieve this goal. We propose usingSSA renaming for both scalars and arrays as a pre-processing step of our analysis.Therefore, we assume henceforth that the input program is SSA renamed (forboth scalars and arrays). We also assume that the post-condition is expressed interms of these SSA renamed scalar and array variables.6e represent a program using a control flow graph G = ( Locs, Edges, µ ),where
Locs denotes the set of control locations (nodes) of the program,
Edges ⊆ Locs × Locs ×{ tt , ff , U } represents the flow of control and µ : Locs → AssignSt ∪ BoolE annotates every node in
Locs with either an assignment statement (of theform v := E or A [ E ] := E ) from the set of assignment statements AssignSt , or aBoolean condition. Two distinguished control locations, called
Start and
End in Locs , represent the entry and exit points of the program. An edge ( n , n , label )represents flow of control from n to n without any other intervening node. Itis labeled tt or ff if µ ( n ) is a Boolean condition, and is labeled U otherwise. If µ ( n ) is a Boolean condition, there are two outgoing edges from n , labeled tt and ff respectively, and control flows from n to n along ( n , n , label ) only if µ ( n ) evaluates to label . If µ ( n ) is an assignment statement, there is a singleoutgoing edge from n , and it is labeled U . Henceforth, we use CFG to refer tothe control flow graph.A CFG may have cycles due to the presence of loops in the program. A back-edge of a loop is an edge from the node corresponding to the last statement inthe loop body to the node representing the loop head. An exit-edge is an edgefrom the loop head to a node outside the loop body. An incoming-edge is an edgeto the loop head from a node outside the loop body. We assume that every loophas exactly one back-edge , one incoming-edge and one exit-edge . For technicalreasons, and without loss of generality, we also assume that the exit-edge of aloop always goes to a “nop” node (say, having a statement x = x; ).Given a program, the program dependence graph (or PDG) G = ( V, DE, CE )represents data and control dependencies among program statements. Here, V denotes vertices representing assignment statements and boolean expressions, DE ⊆ V × V denotes data dependence edges and CE ⊆ V × V denotes controldependence edges. Standard dataflow analysis identifies dependencies betweenprogram variables and thereby among statements. Dependence between state-ments updating array elements requires a more careful analysis. Let S and S be two statements in loops L and L where there is a control-flow path from S to S in the CFG. Suppose S is of the form A [ f ( i , N )] = F ( . . . ); where f is an array index expression, i is the loop counter variable of L , and F is anarbitrary expression. Suppose S is of the form X = G ( A [ g ( i , N )]);, where X is a variable or array element, G is an arbitrary expression, and g is an arrayindex expression. Definition 1.
We say that S in L depends on S in L if there exists i , i such that ≤ i < k L ( N ) and ≤ i < k L ( N ) and f ( i , N ) = g ( i , N ) . The routine
ComputeRefinedPDG shown in Algorithm 1 constructs andrefines the program dependence graph G = ( V, DE, CE ) for the input program P N . It uses the function ConstructPDG (line 1) based on the technique of[10] to create an initial graph. For a node n in G , let def ( n ) and uses ( n ) re-fer to the set of variables/array elements defined and used, respectively, in thestatement/boolean expression corresponding to n . Similarly, let subscript ( v, n )refer to the index expression of the array element v referred to at node n . Pred-icate is - array ( v ) evaluates to true if the v is an array element and false if v is7 lgorithm 1 ComputeRefinedPDG ( P N : Program)1: G ( V, DE, CE ) :=
ConstructPDG ( P N ); if ∃ v, n, n ′ . ( n, n ′ ) ∈ DE ∧ is - array ( v ) ∧ v ∈ def ( n ) ∧ v ∈ uses ( n ′ ) then if n is part of a loop L then ℓ := loop counter of L ; Let φ ( n ) be the constraint (0 ≤ ℓ < k L ); else Let φ ( n ) be true ; if n ′ is part of a loop L ′ then ℓ ′ := loop counter of L ′ ; Let φ ′ ( n ′ ) be the constraint (0 ≤ ℓ ′ < k L ′ ); else Let φ ′ ( n ′ ) be true ; if φ ( n ) ∧ φ ( n ′ ) ∧ (cid:0) subscript ( v, n ) = subscript ( v, n ′ ) (cid:1) is unsatisfiable then DE = DE \ { ( n, n ′ ) } ; ⊲ Remove dependence edges with non-overlapping subscripts return G ( V, DE, CE ); Algorithm 2
PeelAllLoops (( Locs, Edges, µ ) : CFG of P N )1: P pN := ( Locs p , Edges p , µ p ), where L p = Locs , Edges p = Edges , µ p = µ ; ⊲ Copy of P N peelNodes := ∅ ; for each loop L ∈ Loops ( P pN ) do Let k L ( N ) be the expression for iteration count of L in P pN ; peelCount := Simplify ( k L ( N ) − k L ( N − if peelCount is non-constant then throw “Failed to peel non-constant number of iterations”; h P pN , Locs ′ i := PeelSingleLoop ( P pN , L, k L ( N − , peelCount ); ⊲ Transforms loop L so that last peelCount iterations of L are peeled/unrolled. UpdatedCFG and newly created CFG nodes for the peeled iterations are returned by PeelSingleLoop . peelNodes := peelNodes ∪ Locs ′ ; return h P pN , peelNodes i ; a scalar variable. Note that lines 2-14 of ComputeRefinedPDG removes datadependence edges between nodes of G that do not satisfy Definition 1. Peeling the Loops.
To relate P N to P N − , we first ensure that the correspond-ing loops in both programs iterate the same number of times by peeling extraiterations from the loops in P N . This is done by routine PeelAllLoops shownin Algorithm 2. The algorithm first makes a copy, viz. P pN , of the input CFG P N . Let Loops ( P pN ) denote the set of loops of P pN , and let k L ( N ) and k L ( N − L iterates in P pN and P pN − respectively. Thedifference k L ( N ) − k L ( N − L in P pN . If this difference is not a constant, we currently report a failureof our technique (line 6). Otherwise, routine PeelSingleLoop transforms loop L of P pN as follows: it replaces the termination condition ( ℓ < k L ( N )) of L by( ℓ < k L ( N − k L ( N ) − k L ( N − L and adds control flow edges such that the the peeled iterations areexecuted immediately after the loop body is iterated k L ( N −
1) times. Effec-tively,
PeelSingleLoop unrolls/peels the last ( k L ( N ) − k L ( N − L in P pN . The transformed CFG is returned as the updated P pN in line7. In addition, PeelSingleLoop also returns the set
Locs ′ of all CFG nodesnewly added while peeling the loop L . The overall updated CFG and the set ofall peeled nodes obtained after peeling all loops in P pN is returned in line 9. Lemma 1. { ϕ N } P N { ψ N } holds iff { ϕ N } P pN { ψ N } holds. lgorithm 3 ComputeAffected ( P N : Program, peelNodes : Peeled Statements)1: G ( V, DE, CE ) :=
ComputeRefinedPDG ( P N ); AffectedVars := { N } ; ⊲ N is in the affected set repeat WorkList := V \ peelNodes ; ⊲ all non-peeled nodes in G while WorkList = {} do Remove a node n from WorkList ; if ∃ v. is - array ( v ) ∧ ( ∃ u. u ∈ subscript ( v, n ) ∧ u ∈ AffectedVars ) then AffectedVars := AffectedVars ∪ v ; if ∃ v. v ∈ uses ( n ) then if ∃ m. m ∈ reaching - def ( v, n ) ∧ m ∈ peelNodes then AffectedVars := AffectedVars ∪ def ( n ); if ∃ m. m ∈ reaching - def ( v, n ) ∧ def ( m ) ∈ AffectedVars then
AffectedVars := AffectedVars ∪ def ( n ); if v ∈ AffectedVars ∧ n is a assignment node then AffectedVars := AffectedVars ∪ def ( n ); if v ∈ AffectedVars ∧ n is a predicate node then for each edge ( n, n ′ ) ∈ CE do AffectedVars := AffectedVars ∪ def ( n ′ ); until AffectedVars does not change return
AffectedVars ; Affected Variable Analysis.
Before we discuss the generation of ∂ P N , wepresent an analysis that identifies variables/array elements that may take dif-ferent values in P N and P N − . For example, the first k L ( N −
1) iterations of L in P N may not be semantically equivalent to the (entire) k L ( N −
1) iterationsof L in P N − . This is because the semantics of statements in L may dependon the value of N either directly or indirectly. We call variables/array elementsupdated in such statements as affected variables. For every loop with statementshaving potentially different semantics in P N and P N − , the difference program ∂ P N must have a version of the loop with statements that restore the effect ofthe first k L ( N −
1) iterations of L in P N after the (entire) k L ( N −
1) iterations of L in P N − have been executed. Furthermore, for statements in P N that are notenclosed within loops but have potentially different semantics from the corre-sponding statements in P N − , ∂ P N must also rectify the values of variables/arrayelements updated in such statements.Subroutine ComputeAffected , shown in Algorithm 3, computes the setof affected variables P N . We first construct the program dependence graph bycalling the function ComputeRefinedPDG (line 1) defined in Algorithm 1. Let
AffectedVars represent the set of affected variables/array elements. We initializeit (line 2) with variable N since its value is different in P N and P N − . For a node n in the PDG G , we use reaching - def ( v, n ) to refer to the set of nodes where thevariable/array element v is defined and the definition reaches its use at node n .In line 4, we collect nodes in the graph that are not the ones peeled from loopsin P N . The loop in lines 5-18 iterates over the collected nodes to identify affectedvariables. If a variable in the index expression of an array access is affected thenthat array element is considered affected (lines 7-8). A definition at a node n isaffected (marked in line 11) if any variable v used in the statement (checked inline 9) is defined in a peeled node (line 10). Similarly if the reaching definitionof v is affected (line 12) the definition at n is affected (line 13). A variabledefined in terms of an affected variable is also deemed to be affected (lines 14-95). Finally, a variable definition that is control dependent on an affected variableis also considered affected (lines 16-18). The computation of affected variablesis iterated until the set AffectedVars saturates.
Lemma 2.
Variables/Array elements not present in
AffectedVars have the samevalue after k L ( N − iterations of its enclosing loop (if any) in P N − as in P N . Generating the Difference Program ∂ P N . The routine
ProgramDiff inAlgorithm 4 shows how the difference program is computed. We peel each loopin the program and collect the list of peeled nodes (line 1) using Algorithm 2.We then compute the set of affected variables (line 2) using Algorithm 3. Thedifference program ∂ P N inherits the skeletal structure of the program P N afterpeeling each loop (line 4). The algorithm then traverses the CFG of each loop in P N and removes the loops (lines 16-17) that do not update any affected variablesfrom ∂ P N . For every CFG node in other loops, it determines the correspondingnode type (assignment or branch) and acts accordingly (lines 7-14). To explainthe intuition behind the steps of this algorithm, we use the convention that allvariables and arrays of P N − have the suffix Nm1 (for N-minus-1), while those of P N have the suffix N . This allows us to express variables/array elements of P N in terms of the corresponding variables/array elements of P N − in a systematicway in ∂ P N , given that the intended composition is P N − ; ∂ P N .For assignment statements using simple arithmetic operators ( +,-,*,/ ), thesub-routine AssignmentDiff in Algorithm 4 computes a “difference” statementas follows. We assume that
Nodes ( L ) returns the set of CFG nodes in loop L . Forevery assignment statement of the form v = E; in L , a corresponding statementis generated in ∂ P N that expresses v N in terms of v Nm1 and the difference (orratio) between versions of variables/arrays that appear as sub-expressions in E in P N − and P N . For example, the statement A N[i] = B N[i] + v N; in P N givesrise to the “difference” statement A N[i] = A Nm1[i] + (B N[i] - B Nm1[i])+ (v N - v Nm1); in ∂ P N . Similarly, the statement A N[i] = B N[i] * v N; in P N gives rise to the “difference” statement A N[i] = A Nm1[i] * (B N[i] /B Nm1[i]) * (v N / v Nm1); under the assumption
B Nm1[i] * v Nm1 = 0.There are additional kinds of statements that need special processing whengenerating ∂ P N . These relate to accumulation of differences (or ratios). Forexample, if P N has a loop for(i = 0; i < N; i++) sum N = sum N + A N[i]; then the difference A N[i] - A Nm1[i] is aggregated over all indices from 0through N −
2. In this case, the corresponding “difference” loop in ∂ P N hasthe following form: sum N = sum Nm1; for (i = 0; i < N-1; i++) sum N =sum N + (A N[i] - A Nm1[i]); . A similar aggregation for multiplicative ratioscan also be defined. Sub-routine AggregateAssignmentDiff in Algorithm 4generates these “difference” statements.Note that expressions like (B N[i] - B Nm1[i]) or (v N/v Nm1) can often besimplified from the already generated part of ∂ P N . For example, if the alreadygenerated part has a statement of the form B N[i] = B Nm1[i] + expr1; or v N = expr2*v Nm1; , and if expr1 and expr2 are constants or functions of N and loop counters, then we can use expr1 for B N[i] - B Nm1[i] and expr2 for10 lgorithm 4
ProgramDiff ( P N : program)1: h P N , peelNodes i := PeelAllLoops ( P N ); AffectedVars := ComputeAffected ( P N , peelNodes ); Let the CFG of P N be ( Locs, E, µ ); ∂ P N := ( Locs ′ , E ′ , µ ′ ), where Locs ′ := Locs , E ′ := E , and µ ′ := ∅ ; for each loop L ∈ Loops ( P N ) do if ∃ v such that v is updated in L and v ∈ AffectedVars then for each node n ∈ Nodes ( L ) do st N := µ ( n ); if st N is of the form w N := r N op r N then µ ′ ( n ) := AssignmentDiff ( w N := r N op r N ); else if st N is of the form w N := w N op r N wherein w N is a scalar then µ ′ ( n ) := AggregateAssignmentDiff ( L , w N := w N op r N ); else ⊲ st N is a conditional statement µ ′ ( n ) := BranchDiff ( st N , AffectedVars ); else ⊲ Remove loop L from CFG of ∂ P N ( n , n, U ) := IncomingEdge ( L ); ( n, n , ff ) := ExitEdge ( L ); E ′ := E ′ \ { ( n , n, U ) , ( n, n , ff ) } ∪ { ( n , n , U ) } ; Locs ′ := Locs ′ \ Nodes ( L ); return ∂ P N ; AssignmentDiff ( w N := r N op r N ) Let invop be the arithmetic inverse operator of op ; ⊲ + and − are inverse operators of each other, and so are × and ÷ if op ∈ { + , ×} then return w N := w Nm op ( Simplify ( r N invop r Nm ) op Simplify ( r N invop r Nm )); else if op ∈ {− , ÷} then return w N := w Nm invop ( Simplify ( r N op r Nm ) op Simplify ( r N op r Nm )); else throw “Specified operator not handled”; AggregateAssignmentDiff ( L : loop, w N := w N op r N ) n fresh := FreshNode (); µ ′ ( n fresh ) := ( w N := w Nm ); Locs ′ := Locs ′ ∪ { n fresh } ; ( n ′ , n ′′ , U ) := IncomingEdge ( L ); E ′ := E ′ \ { ( n ′ , n ′′ , U ) } ∪ { ( n ′ , n fresh , U ) , ( n fresh , n ′′ , U ) } ; if op ∈ { + , ∗} then return w N := w N op Simplify ( r N invop r Nm ); else if op ∈ {− , ÷} then return w N := w N op Simplify ( r N op r Nm ); else throw “Specified operator not handled”; BranchDiff ( st N : branch condition, AffectedVars : set of affected variables ) Let n be CFG node corresponding to st N ; if ( ∃ v such that v is read in st N and v ∈ AffectedVars ) ∨ ( st N = st N − is satisfiable) then throw “Branch conditions in P N and P N − may not evaluate to same value”; else return st N − ; v N/v Nm1 respectively. We use these optimizations aggressively in the function Simplify used in
AssignmentDiff and
AggregateAssignmentDiff .For every CFG node representing a conditional branch in P N , Algorithm BranchDiff is used to determine if the result of the condition check can dif-fer in P N and P N − . If not, the conditional statement can be retained as suchin the “difference” program. Otherwise, our current technique cannot compute ∂ P N and we report a failure of our technique (see body of BranchDiff ). Forexample, the conditional statement if (t3 == 0) in line 10 of Fig. 1(a) be-haves identically in P N − and P N , and therefore can be used as is in the loop inthe difference program. Lemma 3. ∂ P N generated by ProgramDiff is such that, for all
N > , { ϕ ( N ) } P N − ; ∂ P N { ψ ( N ) } holds iff { ϕ ( N ) } P N { ψ ( N ) } holds. lgorithm 5 SimplifyDiff ( ∂ P N : difference program)1: ∂ P N := ( Locs, E, µ ) ∂ P ′ N := ( Locs ′ , E ′ , µ ′ ), where Locs ′ := Locs , E ′ := E , and µ ′ := µ ; for each loop L ∈ Loops ( ∂ P N ) do ( n , n, U ) := IncomingEdge ( L ); ( n, n , ff ) := ExitEdge ( L ); if Loop body of L is of the form w N := w N op expr , wherein w N is a scalar variable then n acc = FreshNode (); if op ∈ { + , −} then µ ′ ( n acc ) := ( w N := w N op Simplify ( k L ( N − ∗ expr )); else if op ∈ {∗ , ÷} then µ ′ ( n acc ) := ( w N := w N op Simplify ( expr kL ( N − )); else throw “Specified operator not handled”; E ′ := E ′ - { ( n , n, U ) , ( n, n , ff ) } ∪ { ( n , n acc , U ) , ( n acc , n , U ) } ; Locs ′ := Locs ′ − Nodes ( L ) ∪ { n acc } ; if Loop body of L is of the form w N := w Nm or w N := w N then E ′ := E ′ − { ( n , n, U ) , ( n, n , ff ) } ∪ { ( n , n , U ) } ; Locs ′ := Locs ′ − Nodes ( L ); return ∂ P ′ N Simplifying the Difference Program.
While we have described a simplestrategy to generate ∂ P N above, this may lead to redundant statements in thenaively generated “difference” code. For example, we may have a loop like for(i=0; i < N-1; i++) A N[i] = A Nm1[i]; . Our implementation aggressivelyoptimizes and removes such redundant code, renaming variables/arrays as needed(see routine SimplifyDiff in Algorithm 5). The program ∂ P N may also con-tain loops that compute values of variables that can be accelerated. For example,we may have a loop for (i=0; i < N-1; i++) sum = sum + 1; . Algorithm SimplifyDiff removes this loop and introduces the statement sum = sum +(N-1); . This helps in ∂ P N having fewer and simpler loops in a lot of cases. Lemma 4.
Program ∂ P ′ N generated by SimplifyDiff is such that, for all
N > , { ϕ ( N ) } P N − ; ∂ P ′ N { ψ ( N ) } holds iff { ϕ ( N ) } P N − ; ∂ P N { ψ ( N ) } holds. Generating the Difference Pre-condition ∂ϕ ( N ) . We now present a simplesyntactic algorithm, called
SyntacticDiff , for generation of the difference pre-condition ∂ϕ ( N ). Although this suffices for all our experiments, for the sake ofcompleteness, we present later a more sophisticated algorithm for generating ∂ϕ ( N ) simultaneously with Pre ( N ).Formally, given ϕ ( N ), algorithm SyntacticDiff generates a formula ∂ϕ ( N )such that ϕ ( N ) → ( ϕ ( N − ∧ ∂ϕ ( N )). Observe that if such a ∂ϕ ( N ) exists,then ϕ ( N ) → ϕ ( N −
1) holds as well. Therefore, we can use the validity of ϕ ( N ) → ϕ ( N −
1) as a test to decide the existence of ∂ϕ ( N ).If ϕ ( N ) is of the syntactic form ∀ i ∈ { . . . N } b ϕ ( i ), then ∂ϕ ( N ) is easily seento be ˆ ϕ ( N ). If ϕ ( N ) is of the syntactic form ϕ ( N ) ∧ · · · ∧ ϕ k ( N ), then ∂ϕ ( N )can be computed as ∂ϕ ( N ) ∧ · · · ∧ ∂ϕ k ( N ). Finally, if ϕ ( N ) doesn’t belong toany of these syntactic forms or if condition 2(a) of Theorem 1 is violated by theheuristically computed ∂ϕ ( N ), then we over-approximate ∂ϕ N by True . For alarge fraction of our benchmarks, the pre-condition ϕ ( N ) was True , and hence ∂ϕ ( N ) was also True . Generating the Formula
Pre ( N − ) . We use Dijsktra’s weakest pre-conditioncomputation to obtain
Pre ( N −
1) after the “difference” pre-condition ∂ϕ ( N ) andthe “difference” program ∂ P N have been generated. The weakest pre-condition12 lgorithm 6 FPIVerify ( P N : program, ϕ ( N ): pre-condn, ψ ( N ): post-condn)1: if Base case check { ϕ (1) } P { ψ (1) } fails then return “Counterexample found!”; ∂ϕ ( N ) := SyntacticDiff ( ϕ ( N )); ∂ P N := ProgramDiff ( P N ); ∂ P N := SimplifyDiff ( ∂ P N ); ⊲ Simplify and Accelerate loops i := 0; Pre i ( N ) := ψ ( N ); c Pre i ( N ) := True ; ⊲ Cumulative conjoined pre-condition do if { c Pre i ( N − ∧ ψ ( N − ∧ ∂ϕ ( N ) } ∂ P N { c Pre i ( N ) ∧ ψ ( N ) } then return True ; ⊲ Assertion verified i := i + 1; Pre i ( N −
1) :=
LoopFreeWP ( Pre i − ( N ) , ∂ P N ); ⊲ Dijkstra’s WP sans WP -for-loops if no new Pre i ( N −
1) obtained then ⊲ Can happen if ∂ P N has a loop return FPIVerify ( ∂ P N , c Pre i ( N − ∧ ψ ( N − ∧ ∂ϕ ( N ), c Pre i ( N ) ∧ ψ ( N )); else c Pre i ( N ) := c Pre i − ( N ) ∧ Pre i ( N ); while Base case check { ϕ (1) } P { c Pre i (1) } passes; return False ; ⊲ Failed to prove by full-program induction can always be computed using quantifier elimination engines in state-of-the-artSMT solvers like Z3 if ∂ P N is loop-free. In such cases, we use a set of heuristicsto simplify the calculation of the weakest pre-condition before harnessing thepower of the quantifier elimination engine. If ∂ P N contains a loop, it may stillbe possible to obtain the weakest pre-condition if the loop doesn’t affect thepost-condition. Otherwise, we compute as much of the weakest pre-condition ascan be computed from the non-loopy parts of ∂ P N , and then try to recursivelysolve the problem by invoking full-program induction on ∂ P N with appropriatepre- and post-conditions. Verification by Full-program Induction.
The basic full-program inductionalgorithm is presented as routine
FPIVerify in Algorithm 6. The main stepsof this algorithm are: checking conditions 3(a), 3(b) and 3(c) of Theorem 1(lines 1, 18 and 10), calculating the weakest pre-condition of the relevant partof the post-condition (line 13), and strengthening the pre-condition and post-condition with the weakest pre-condition thus calculated (line 17). Since theweakest pre-condition computed in every iteration of the loop (
P re i ( N −
1) inline 13) is conjoined to strengthen the inductive pre-condition ( c P re i ( N ) in line17), it suffices to compute the weakest pre-condition of P re i − ( N ) (instead of c P re i ( N ) ∧ ψ ( N )) in line 13. The possibly multiple iterations of strengtheningof pre- and post-conditions is effected by the loop in lines 9-18. In case the loopterminates via the return statement in line 11, the inductive claim has beensuccessfully proved. If the loop terminates by a violation of the condition in line18, we report that verification by full-program induction failed. In case ∂ P N hasloops and no further weakest pre-conditions can be generated, we recursivelyinvoke FPIVerify on ∂ P N in line 15. This situation arises if, for example, wemodify the example in Fig. 1(a) by having the statement C[t3] = N; (instead of
C[t3] = 0; ) in line 10. In this case, ∂ P N has a single loop corresponding to thethird loop in Fig. 1(a). The difference program of ∂ P N is, however, loop-free, andhence the recursive invocation of full-program induction on ∂ P N easily succeeds.13 lgorithm 7 FPIDecomposeVerify ( i : integer )1: do h Pre ′ i ( N − , ∂ϕ ′ i ( N ) i := NextDecomposition ( Pre i ( N − Check if (a) ∂ϕ ′ i ( N ) ∧ Pre ′ i ( N − → Pre i ( N − (b) ϕ ( N ) → ϕ ( N − ∧ (cid:0) ∂ϕ ′ i ( N ) ∧ ∂ϕ ( N ) (cid:1) , (c) P N − does not update any variable or array element in ∂ϕ ′ i ( N ) if any check in lines 3-5 fails then if HasNextDecomposition ( Pre i ( N − then continue ; else return False ; if { c Pre i − ( N − ∧ ψ ( N − ∧ Pre i ( N − ∧ ∂ϕ ( N ) } ∂ P N { c Pre i − ( N ) ∧ ψ ( N ) ∧ Pre ′ i ( N ) } then return True ; ⊲ Assertion verified else c Pre i ( N ) := c Pre i − ( N ) ∧ Pre ′ i ( N ); i := i + 1; Pre i ( N −
1) :=
LoopFreeWP ( Pre ′ i − ( N ) , ∂ P N ); ⊲ Dijkstra’s WP sans WP -for-loops if { ϕ (1) } P { c Pre i − (1) ∧ Pre i (1) } does not hold then i := i − else prev ∂ϕ ( N ) := ∂ϕ ( N ); ∂ϕ ( N ) := ∂ϕ ′ i − ( N ) ∧ ∂ϕ ( N ); if FPIDecomposeVerify ( i ) returns False then i := i − ∂ϕ ( N ) := prev ∂ϕ ( N ); else return True ; while HasNextDecomposition ( Pre i ( N − return False ; Generalized FPI Algorithm.
While algorithm
FPIVerify suffices for all ofour experiments, we may not always be so lucky. Specifically, even if ∂ P N is loop-free, the analysis may exit the loop in lines 9-18 of FPIVerify by violating thebase case check in line 18. To handle (at least partly) such cases, we propose thefollowing strategy. Whenever a (weakest) pre-condition
Pre i ( N −
1) is generated,instead of using it directly to strengthen the current pre- and post-conditions,we “decompose” it into two formulas
Pre ′ i ( N −
1) and ∂ϕ ′ i ( N ) with a two-foldintent: (a) potentially weaken Pre i ( N −
1) to
Pre ′ i ( N − ∂ϕ ( N ) to ∂ϕ ′ i ( N ) ∧ ∂ϕ ( N ). The checks forthese intended usages of Pre ′ i ( N −
1) and ∂ϕ ′ i ( N ) are implemented in lines 3, 4,5, 11 and 17 of routine FPIDecomposeVerify , shown as Algorithm 7. Thisroutine is meant to be invoked as
FPIDecomposeVerify ( i ) after each itera-tion of the loop in lines 9-18 of routine FPIVerify (so that
Pre i ( N ), c Pre i ( N )etc. are initialized properly). In general, several “decompositions” of Pre i ( N )may be possible, and some of them may work better than others. FPIDecomp-seVerify permits multiple decompositions to be tried through the use of the
NextDecomposition and
HasNextDecomposition functions. Lines 22-25 of
FPIDecomposeVerify implement a simple back-tracking strategy, allowing asearch of the space of decompositions of
Pre i ( N − FPIDecomposeVerify , we simultaneously compute a difference formula( ∂ϕ ′ i ( N ) ∧ ∂ϕ ( N )) and an inductive pre-condition ( c Pre i − ( N ) ∧ Pre ′ i ( N )). Lemma 5.
Algorithms
FPIVerify and
FPIDecomposeVerify ensure con-ditions 2 and 3 of Theorem 1 upon successful termination. N , a straightforward extension works for multiple independent parameters,multiple independent array sizes, different induction directions, and non-uniformloop termination conditions. Limitations.
There are several scenarios under which full-program inductionmay not produce a conclusive result. Currently, we only analyze programs withnon-nested loops with + , − , × , ÷ expressions in assignments. We also do nothandle branch conditions that are dependent on the parameter N (this doesn’tinclude loop conditions, which are handled by unrolling the loop). The techniquealso remains inconclusive when the difference program ∂ P N does not have fewerloops than the original program. Reduction in verification complexity of the pro-gram, in terms of the number of loops and assignment statements dependent on N , is crucial to the success of full-program induction. Finally, our technique mayfail to verify a correct program if the heuristics used for weakest pre-conditioneither fail or return a pre-condition that causes violation of the base case checkin line 18 of FPIVerify . Despite these limitations, our experiments show thatfull-program induction performs remarkably well on a large suite of benchmarks.
We have implemented our technique in a prototype tool called
Vajra , availableat [4]. It takes a C program in SVCOMP format as input. The tool, writtenin
C++ , is built on top of the LLVM/CLANG [21] 6 . . Z3 [24] v4 . . Vajra on a test-suite of 42 safe benchmarks inspired fromdifferent algebraic functions that compute polynomials as well as a standardarray operations such as copy, min, max and compare. Our programs take asymbolic parameter N which specifies the size of each array as well as the numberof times each loop executes. Assertions, possibly quantified, are (in-)equalitiesover array elements, scalars and (non-)linear polynomial terms over N .All experiments were performed on a Ubuntu 18.04 machine with 16GB RAMand running at 2.5 GHz. We have compared Vajra against
VIAP (v1.0) [25],
Ve-riAbs (v1.3.10) [7],
Booster (v0.2)[1],
Vaphor (v1.2) [23] and
FreqHorn (v3) [9].C programs were manually converted to mini-Java as required by
Vaphor andCHC’s as required by
FreqHorn . Our results are shown in Table 1.
Vajra verified 36 benchmarks, compared to 23 verified by
VIAP , 12 by
VeriAbs , 8 by
Booster , 5 each by
Vaphor and
FreqHorn . Vajra was unable to computethe difference program for 5 benchmarks and was inconclusive on 1 benchmark.
Vajra verified 17 benchmarks on which
VIAP diverged, primarily due tothe inability of
VIAP ’s heuristics to get closed form expressions.
VIAP ver-ified 4 benchmarks that could not be verified by the current version of
Va-jra due to syntactic limiations.
Vajra , however, is two orders of magnitudefaster than
VIAP on programs that were verified by both.
Vajra proved 28benchmarks on which
VeriAbs diverged.
VeriAbs ran out of time on pro-grams where loop shrinking and merging abstractions were not strong enough15 ame ✓ ? ? ✓ ? ? ✓ ? ? ✓ ? ? ✓ ? ? ✓ ? ? ✓ ✓ ? ? ✓ ✓ ✓ ✓ ✓ ✗ ? ? ✓ ✗ ? ✓ ✗ ? ✓ ✗ ? ✓ ✓ ? ✓ ✓ ✓ ✓ ✓ ✓ ✓ Name ✓ ✓ ✓ ✓ ✗ ✓ ✓ ✓
18 TO ✗ ✓ ✓ ✓
39 TO ✗ ✓ ✓ ? ✗ ✓ ✓ ? ✗ ✓ ✓ ✓
24 TO ✗ ✓ ✓ ✗ ✓ ✓ ? ✗ ✓ ✓ ? ✗ ✓ ✓ ? ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ✓ ? ✓ ✓ ✓ ✓ ✓ ? ✓ ✓ ✓ - condg 3 ? ? ✓ - TO TOmods 4 ? ✓ - - - modp 2 ? ✓ ✓ - ? - Table 1.
First column is the benchmark name. Second column indicates the numberloops in the benchmark (excluding the assertion loop). Successive columns indicate theresults generated by tools and the time taken where T1 is
Vajra , T2 is
VIAP , T3is
VeriAbs , T4 is
Booster , T5 is
Vaphor , T6 is
FreqHorn . ✓ indicates assertionsafety, ✗ indicates assertion violation, ? indicates unknown result, and - indicates anabrupt stop. All the times are in seconds. TO is time-out of 100 secs. to prove the assertions. VeriAbs reported 1 program as unsafe due to the im-precision of its abstractions and it proved 4 benchmarks that
Vajra could not.
Vajra verified 30 benchmarks that
Booster could not.
Booster reported 4benchmarks as unsafe due to imprecise abstractions, its fixed-point computationengine reported unknown result on 12 benchmarks and it ended abruptly on3 benchmarks.
Booster also proved 2 benchmarks that couldn’t be handledby the current version of
Vajra due to syntactic limitations.
Vajra verified32 benchmarks on which
Vaphor was inconclusive. Distinguished cell abstrac-tion in
Vaphor is unable to prove safety of programs, when the value at eacharray index needs to be tracked.
Vaphor reported 9 programs unsafe due toimprecise abstraction, returned unknown on 2 programs and ended abruptly on1 program.
Vaphor proved a benchmark that
Vajra could not.
Vajra veri-fied 32 programs on which
FreqHorn diverged, especially when constants andterms that appear in the inductive invariant are not syntactically present in theprogram.
FreqHorn ran out of time on 22 programs, reported unknown resulton 12 and ended abruptly on 3 benchmarks.
FreqHorn verified a benchmarkwith a single loop that
Vajra could not. On an extended set of 231 benchmarks,
Vajra verified 110 programs out of 121 safe programs, falsified 108 out of 110unsafe programs, and was inconclusive on the remaining 13 programs.
We presented a novel property-driven verification method that performs induc-tion over the entire program via parameter N . Significantly, this obviates theneed for loop-specific invariants. Experiments show that full-program inductionperforms remarkably well vis-a-vis state-of-the-art tools for analyzing array ma-nipulating programs. Further improvements in the algorithms for computing dif-ference programs and for strengthening of pre- and post-conditions are envisagedas part of future work. 16 ata Availability Statement The datasets generated and analyzed during the current study are available inthe figshare repository: https://doi.org/10.6084/m9.figshare.11875428.v1
References
1. Alberti, F., Ghilardi, S., Sharygina, N.: Booster: An acceleration-based verificationframework for array programs. In: Proc. of ATVA. pp. 18–23 (2014)2. Beyer, D., Henzinger, T.A., Majumdar, R., Rybalchenko, A.: Invariant synthesisfor combined theories. In: Proc. of VMCAI. pp. 378–394 (2007)3. Chakraborty, S., Gupta, A., Unadkat, D.: Verifying Array Manipulating Programsby Tiling. In: Proc. of SAS. pp. 428–449 (2017)4. Chakraborty, S., Gupta, A., Unadkat, D.: Verifying Array Manipulating Pro-grams with Full-program Induction - Artifacts TACAS 2020. Figshare (2020).https://doi.org/10.6084/m9.figshare.11875428.v15. Clarke, E., Biere, A., Raimi, R., Zhu, Y.: Bounded model checking using satisfia-bility solving. FMSD (1), 7–34 (2001)6. Cousot, P., Cousot, R., Logozzo, F.: A parametric segmentation functor for fullyautomatic and scalable array content analysis. In: Proc. of POPL. pp. 105–118(2011)7. Darke, P., Prabhu, S., Chimdyalwar, B., Chauhan, A., Kumar, S., Basakchowd-hury, A., Venkatesh, R., Datar, A., Medicherla, R.K.: VeriAbs: Verification byabstraction and test generation. In: TACAS (Competition Contribution). pp. 457–462 (2018)8. Ernst, M.D., Perkins, J.H., Guo, P.J., McCamant, S., Pacheco, C., Tschantz, M.S.,Xiao, C.: The Daikon system for dynamic detection of likely invariants. Sci. Com-put. Program. (1-3), 35–45 (2007)9. Fedyukovich, G., Prabhu, S., Madhukar, K., Gupta, A.: Quantified invariants viasyntax-guided-synthesis. In: Proc. of CAV. pp. 259–277 (2019)10. Ferrante, J., Ottenstein, K.J., Warren, J.D.: The program dependence graph andits use in optimization. TOPLAS (3), 319–349 (1987)11. Flanagan, C., Leino, K.R.M.: Houdini, an annotation assistant for ESC/Java. In:Proc. of FME. pp. 500–517 (2001)12. Gopan, D., Reps, T.W., Sagiv, S.: A framework for numeric analysis of arrayoperations. In: Proc. of POPL. pp. 338–350 (2005)13. Gulwani, S., McCloskey, B., Tiwari, A.: Lifting abstract interpreters to quantifiedlogical domains. In: Proc. of POPL. pp. 235–246 (2008)14. Gurfinkel, A., Shoham, S., Vizel, Y.: Quantifiers on demand. In: Proc. of ATVA.pp. 248–266 (2018)15. Halbwachs, N., P´eron, M.: Discovering properties about arrays in simple programs.In: Proc. of PLDI. pp. 339–348 (2008)16. Henzinger, T.A., Hottelier, T., Kov´acs, L., Rybalchenko, A.: Aligators for arrays(tool paper). In: Proc. of LPAR. pp. 348–356 (2010)17. Jacobs, B., Smans, J., Philippaerts, P., Vogels, F., Penninckx, W., Piessens, F.:VeriFast: A powerful, sound, predictable, fast verifier for C and Java. In: Proc. ofNFM. pp. 41–55 (2011)18. Jhala, R., McMillan, K.L.: Array abstractions from proofs. In: Proc. of CAV. pp.193–206 (2007)
9. Knobe, K., Sarkar, V.: Array SSA form and its use in parallelization. In: Proc. ofPOPL. pp. 107–120 (1998)20. Komuravelli, A., Bjorner, N., Gurfinkel, A., McMillan, K.L.: Compositional ver-ification of procedural programs using Horn clauses over integers and arrays. In:Proc. of FMCAD. pp. 89–96 (2015)21. Lattner, C.: LLVM and Clang: Next generation compiler technology. In: The BSDConference. pp. 1–2 (2008)22. Liu, J., Rival, X.: Abstraction of arrays based on non contiguous partitions. In:Proc. of VMCAI. pp. 282–299 (2015)23. Monniaux, D., Gonnord, L.: Cell Morphing: From array programs to array-freehorn clauses. In: Proc. of SAS. pp. 361–382 (2016)24. de Moura, L.M., Bjørner, N.: Z3: an efficient SMT solver. In: Proc. of TACAS. pp.337–340 (2008)25. Rajkhowa, P., Lin, F.: Extending VIAP to handle array programs. In: Proc. ofVSTTE. pp. 38–49 (2018)26. Rosen, B.K., Wegman, M.N., Zadeck, F.K.: Global value numbers and redundantcomputations. In: Proc. of POPL. pp. 12–27 (1988)27. Seghir, M.N., Brain, M.: Simplifying the verification of quantified array assertionsvia code transformation. In: Proc. of LOPSTR. pp. 194–212 (2012)28. Sheeran, M., Singh, S., St˚almarck, G.: Checking safety properties using inductionand a SAT-solver. In: Proc. of FMCAD. pp. 127–144 (2000)29. Srivastava, S., Gulwani, S.: Program verification using templates over predicateabstraction. ACM Sigplan Notices (6), 223–234 (2009) Open Access