restructuring transformations are called global data-flow transformations. In our earlier work [2], we have presented an
Automatic Verification of Algebraic Transformations K.C. Shashidhar∗ Maurice Bruynooghe† Abstract Programmers applying nontrivial code transformations are often faced with the problem of ensuring the functional equivalence of original and transformed programs. A pragmatic approach to the problem is to provide a tool to the programmers to check the equivalence of the two programs after applying the transformations. In prior work we have developed such a tool to handle important transformations like loop and sub-expression propagation. Here we report our progress on extending the tool to also handle algebraic transformations.
1
Introduction
Code transformations come into play in a situation when a programmer wants much better results than what optimizations of a compiler can provide. Such a situation is common for programmers of mobile computing and communicating systems. They are required to program complex signal processing algorithms for complex platform architectures and yet meet stringent constraints on the performance and energy consumption of the final implementation. Research has shown that a methodical application of global source-to-source code transformations on an original implementation of the algorithm can greatly help in meeting such constraints (for example, [1]). Every stage in an implementation activity brings forth an associated verification problem. Source code transformations are no exception. The problem here is to ensure that the transformed program preserves the functionality of the original program. A pragmatic approach to this problem is to separate the two concerns viz., applying transformations and verifying that they preserve the functional equivalence. This implies an ex post facto solution that requires a program equivalence checking tool. Our work is focused on this requirement. Since, in general, the program equivalence problem is undecidable, we target the most important transformations that are applied on a decidable class of programs. Code transformations considered. Our work particularly targets source code transformations that reduce the accesses to the data memory hierarchy. Data transfers between different layers of the hierarchy are minimized if the data accessed during program execution have temporal and spatial locality. This is best achieved by restructuring transformations on the for-loops in the complete program, called global loop transformations. But often, data-flow dependences come in the way of ∗ {kodambal, † {maurice,
catthoor}@imec.be gerda}@cs.kuleuven.ac.be
Francky Catthoor∗
Gerda Janssens†
achieving locality of data. Then it is required to explore the possibilities of removing these dependences by certain transformations which reorder the computation, either by introduction or elimination of temporary variables to hold intermediate values (sub-expression propagation) or by taking advantage of the algebraic properties of the operators (algebraic transformations). Such restructuring transformations are called global data-flow transformations. In our earlier work [2], we have presented an intraprocedural method for showing the equivalence of two programs whose corresponding program functions are related through loop and sub-expression propagation transformations. The contribution of the present work is in the extension of the method to also allow global algebraic data-flow transformations together with the other two transformations in a single pass of the check. An example problem. Suppose that we are given a pair of program functions as in Figure 1, where one has been obtained by applying loop and data-flow transformations on the other. Both of these functions, when executed, assign the computed values to the elements of the output array variable C[] and terminate. The difference between the two is that, the original function assigns values to C[] as, ∀k : 0 ≤ k < 256, C[3*k] = A[2*k] + g(B[k+1]) + f(A[2*k+1]) + B[k+2];
whereas, the transformed function assigns C[] as, C[3*k] = g(B[k+1]) + B[k+2] + f(A[2*k+1]) + A[2*k];
Obviously, if we can ignore a possible overflow in the evaluation of integer expressions, the integer addition is both associative and commutative. Hence, if the same values are input to the two program functions, the same values are assigned to the elements of the output variable C[] in both the functions. That is, the two program functions are input-output equivalent under the applied transformations. We have developed a method to check this equivalence fully automatically.
2
Program representation
The class of programs that we consider are required to have the following properties: ① Single-assignment form: Every memory location is written only once; ② Static control-flow: There are no while-loops and the data dependent if-conditions are simple enough to be handled by if-conversions; ③ Affine indices: All expressions in the index and the loop bounds are affine; and ④ No pointer references: All references to the memory are with explicit indexing. We rely on a code pre-processing phase in order to ensure that the original program has the above properties before applying code transformations.
/* Original function */ void foo(int A[], int B[], int C[]) { int k, tmp1[256], tmp2[266], tmp3[256];
s1: s2:
s3: s4:
s5:
/* Transformed function */ void foo(int A[], int B[], int C[]) { int k, tmp4[256], tmp5[256];
for(k=0; k