I have prepared a bundle including Eclipse itself:
http://www.cs.colostate.edu/AlphaZ/bundles/eclipse-verifier-bundle-linux64.tar.gz
The bundle is based on Eclipse Classic 3.7.1.
The above bundle contains all necessary plug-ins to use the following project:
http://www.cs.colostate.edu/AlphaZ/bundles/org.polymodel.verifier.IF.ada.tar
This verifier.IF.ada project contains examples to use the verifier. For trying out the verifier, I suggest that you download the bundle, and the above archived project, and then import the tar from Import→General→Existing Projects Into Workspace
.
The examples are from our ompVerify paper (http://www.springerlink.com/content/0gh74j23115861g6/).
http://www.cs.colostate.edu/AlphaZ/bundles/AlphaZ.tar
This is a jar file for the verifier. If run as a stand alone jar file, it verifies the legality of parallelization of some of the example programs. When used as a library, please specify the program representation according to the textual interface specified at the bottom of this page and call VerifierExample.verify
The following plug-ins are installed as jar files under dropins directory in the above bundle. Sources to these projects are available in the GeCoS repository. Please follow the instructions in this website (https://gforge.inria.fr/scm/?group_id=510) to check out sources for these plug-ins. All plug-ins are located under trunk/polytools-emf, and are licensed under EPL.
The following plug-ins are required to use the verifier.
The bundle also contains subclipse for SVN access.
The provided interface takes the following information for each statement in a polyhedral region to verify:
The output of the verifier is an instance of VerifierOutput object, containing a flag to tell if the program was valid, and a list of messages from the verifier listing violations found in the program. The messages should contain sufficient information to give feedback to the user.
These input can easily be extracted from affine control loops (example later), and array dataflow analysis is performed using the provided information and then the resulting polyhedral reduced dependence graph (PRDG) is verified.
The verifier uses Integer Set Libarary (http://freshmeat.net/projects/isl/), and accepts textual representations for ISLSets and ISLMaps.
[<parameter indices>]->{ [<indices>] : <list of constraints involving parameters and indices> }
where the constraints are delimited by any of one '&', '|', 'and', 'or'.
[<parameter indices>]->{ [<indices>] -> [<list of expressions involving parameters and indices>] }
where the expressions are delimited by ','.
Note that ISL accepts much more general syntax than shown above, since ISLMaps are relations, and not functions, but the above is sufficient to express inputs to the verifier.
The example is for C programs with OpenMP. The verifier is applicable for other programming languages with parallel constructs that can be viewed as doall
parallelization.
In case of X10, if the only statement in the body of a loop is async
, the loop can be considered as doall
loop.
void matrix_multiply(int** A, int** B, int** C, int N) { int i,j,k; #pragma omp parallel for for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { C[i][j] = 0; //S0 for (k = 0; k < N; k++) { C[i][j] += A[i][k] * B[k][j]; //S1 } } } }
Each statement is surrounded by a set of loops with affine bounds. The polyhedral domain for each statement is simply the intersection of all loop bounds that surrounds a statement. There may be a if
statement with affine constraints, such constraints from if
statements are also intersected when computing the statement domain.
For the above matrix multiply, S0
is surrounded by i,j
loops and thus has a square domain, where S1
has a cubic domain.
S0
: [N] -> { [i,j] : 0<=i<N & 0<=j<N }
S1
: [N] -> { [i,j,k]:0<=i<N & 0<=j<N & 0<=k<N }
The input schedule given to the verifier is the sequential schedule, treating doall
loops as sequential loops.
Doall
parallelism is later expressed as dimension types.
The schedule is used to characterize the relative placement of the statements. In addition, we require that all statements are mapped to a common dimensional space via scheduling specification.
Although this is not a requirement, a common convention when applying polyhedral analyses to imperative programs is to use 2d+1
representation.
For a loop with maximum depth d
, additional d+1
dimensions (with constants) are used to specify textual ordering of the loops and statements.
For the above matrix multiply, the schedules are:
S0
: [N] -> { [i,j] -> [0,i,0,j,0,0,0] }
S1
: [N] -> { [i,j,k]->[0,i,0,j,1,k,0] }
Note that the 5th dimension is 0 for S0
and 1 for S1
, and all preceding dimensions are identical. This specifies that S0
is textually before S1
at this dimension. Also note that the last two dimensions of the schedule for S0
are padded with 0s, to match the number of dimensions.
Both reads and writes are specified as a pair of variable name and access function. Variable names corresponds to the arrays read/written in the program, and access function corresponds to the indexing into the arrays. The access functions are expressed as affine functions from the corresponding statement domains.
For the above example, S0
has only one access:
C
: [N] -> { [i,j] -> [i,j] }
S1
has total of 4 accesses:
C
: [N] -> { [i,j,k] -> [i,j] }
C
: [N] -> { [i,j,k] -> [i,j] }
A
: [N] -> { [i,j,k] -> [i,k] }
B
: [N] -> { [i,j,k] -> [k,j] }
One last piece of information given to the verifier is the annotation on each dimension of the schedule. There are three types of dimensions:
The parallel dimensions corresponds to loops that are parallelized by OpenMP for
directive, or any other parallel construct for expressing doall
parallelism.
When variables are declared as private in OpenMP, the variable is private to each iteration of the parallel loop. Therefore, it is expressed by adding additional dimensions to the memory accesses, indexed by the loop iterator of the parallel loop, so that each iteration of the parallel loop access different memory location.
In other words, the following code:
#pragma omp parallel for private(c) for (i=0; i < N i++) c += ...
is translated as:
#pragma omp parallel for for (i=0; i < N i++) c[i] += ...
during verification.
The following code is a possible interface based on arrays of Strings for testing purposes.
private static void matrix_multiply() { String[][] statements = new String[][] { new String[] {"S0", "[N] -> { [i,j] : 0<=i<N & 0<=j<N }", "[N] -> { [i,j] -> [0,i,0,j,0,0,0] }"}, new String[] {"S1", "[N] -> { [i,j,k] : 0<=i<N & 0<=j<N & 0<=k<N }", "[N] -> { [i,j,k] -> [0,i,0,j,1,k,0] }"} }; String[][] reads = new String[][] { new String[] {"S1", "A", "[N] -> { [i,j,k] -> [i,k] }"}, new String[] {"S1", "B", "[N] -> { [i,j,k] -> [k,j] }"}, new String[] {"S1", "C", "[N] -> { [i,j,k] -> [i,j] }"} }; String[][] writes = new String[][] { new String[] {"S0", "C", "[N] -> { [i,j] -> [i,j] }"}, new String[] {"S1", "C", "[N] -> { [i,j,k] -> [i,j] }"} }; String[][] dims = new String[][] { //Legal 1D parallelization new String[] {"S0", "O,P,O,S,O,S,O"}, new String[] {"S1", "O,P,O,S,O,S,O"}, //illegal parallelization of k dimension //new String[] {"S0", "O,S,O,S,O,P,O"}, //new String[] {"S1", "O,S,O,S,O,P,O"} }; VerifierExample.verify(statements, reads, writes, dims); }
For a more general interface, the user must construct the following data structure, called ADAInput
that represents the polyhedral region to analyze.
In addition a map, Map<CandidateStatement, List<DIM_TYPE» is required for specifying the dimension types for each statement.
fr.irisa.cairn.model.polymodel.ada.factory.ADAUserFactory
provides methods for constructing ADAInput
.
ADAInput |------variables : List<Variable> |------statements : List<CandidateStatement> CandidateStatement |------ID : String |------domain : PolyhedralDomain |------schedule : AffineMapping |------write : WriteAccess |------reads : List<ReadAccess> Access |-----variable : Variable |-----accessFunction : AffineMapping Variable |-----name : String ReadAccess->Access WriteAccess->Access
ADAInput
is the input to Array Dataflow Analysis, which is a separate module, and thus dimension types are not part of this data structure.
Once these two data structures are ready, the following two lines will invoke the verifier.
VerifierInput input = VerifierInput.build(adaInput, dimTypes); VerifierOutput output = Verifier.verify(ISLDefaultFactory.INSTANCE, input.prdg, input.schedules, input.memoryMaps, input.dimTypes);