The APRON project provides a common interface for various numerical abstract domains with various expressiveness and cost versus precision trade-offs. This document describes the shape domain implementation available within APRON.
The shape domain allows manipulating and representing conjunctions of invariants of the form x==l==>y and where x and y range among a finite set of pointer program variables and numerical constraints between lengths of list segments between pointer program variables and numerical program variables. The kind of the numerical constraints allowed is a parameter of the library. This parameter has to be one of the existing numerical domains (box, octagons, polyhedra, grid) included by APRON.
Familiarity is assumed with the generic APRON framework as well as the shape abstract domain (see external links).
Please see the INSTALL
file included in the
distribution for installation instructions.
Your C or C++ program must be linked with the following libraries:
-lshapeX
(where X
denotes the chosen numerical constrains set;
BOX
for instance);
-lapron
;
-lgmp
;
-lmpfr
;
-lm
.
shape.h
file,
ap_manager_t* man = shape_manager_alloc();
ap_abstract0.h
are available on shapes through man
.
The shape library is compiled with a variety of underlying numeric set for constraints on lengths, distinguished using a suffix:
box
: uses boxes (intervals)
oct
: uses shapes
ppl
: uses polyhedra
The choice of this numeric set affects the soundness, precision, and efficiency of the analysis:
oct
is recommended.
Exact transfer functions are provided for the class of operations that are closed under shapes. These include:
Best transfer functions are provided in the following cases:
The following transfer functions use some approximate polynomial algorithms and have no precision guarantees:
Additionally, the exactness or best-precision feature of an abstract transfer
function is often lost when the MPQ
underlying numeric set
is not used, or the arguments have integer dimensions, or the user sets
the algorithm
parameter to a strictly negative value.
Finally, the exactness or best-precision feature can be lost due to
conversion between the underlying numeric type and
user-provided ap_scalar_t
types.
The shape library will set the flag_exact
and
flag_best
manager flags accordingly in all cases.
Note that, due to interval coefficients, expressions may be non-deterministic, that is, correspond to a bunch of expressions. In case of assignments, substitutions, or bound determinations of non-deterministic expressions, or conjunctions with non-deterministic constraints, we considers the join of all results, when ranging over the non-deterministic set.
The following predicates are exact, i.e., they always return
either tbool_true
or tbool_false
(provided that algorithm
is greater than or equal to 0,
that the shape has no integer dimension, and that the
MPQ
underlying domain is selected):
Note that, for non-deterministic expressions, tbool_false
is
returned as long as the saturation is not satisfied for at least one expression.
Testing for the saturation by an arbitrary expression is very imprecise.
It always return tbool_top
.
When algorithm
is set to a strictly negative value, the
shape has an integer dimension, the MPQ
underlying domain is
selected, or the conversion from user-specified ap_scalar_t
types
to internal types resulted in an over-approximation, the predicate is sound
but not exact: it is a semi-test.
That is, it mainly returns either
tbool_true
or tbool_top
.
It can conclude that the predicate is definitively tbool_false
only in very rare cases.
The shape library will set the flag_exact
and
flag_best
manager flags accordingly in all cases.
The algorithm
field in the manager provides an
implementation-specific parameter to set the required level of precision
for transfer functions.
In the shape domain, only two precision levels are recognised. They correspond to (with the exception of the widening):
algorithm ≥ 0
is the normal precision:
a transitive closure algorithm is used pervasively to achieve
best precision transfer functions and results in transfer functions that
have a cubic worst-case cost (in the number of variables),
algorithm < 0
is the low precision: the transitive closure
algorithm is not used, the best precision feature is not guaranteed, and
transfer functions have a quadratic worst-case cost
(many actually have a linear cost).
Depending on the algorithm
flag, one of the following widening
algorithm is used:
algorithm = 0
:
the right argument is closed, the standard widening is used: unstable
constraints are forgotten.
Thus, it converges in a quadratic number of steps, at worse.
algorithm < 0
:
the standard widening algorithm is used but the right argument is not closed.
algorithm = shape_pre_widening
:
this special value corresponds to a pre-widening.
It does not enforce the convergence by itself but can be safely intermixed
with a regular widening to improve the precision of the sequence, without
jeopardizing the overall convergence. As long as a regular widening is
applied infinitely often, the sequence will converge in finite time.
(Note that intermixing a regular widening with a join operator will result
in a diverging sequence. Thus, the join is not a pre-widening.
Our pre-widening is actually a join without the closure application.)
Use with care!
The ap_abstract0_shape_widening_thresholds
provides a widening
with scalar thresholds.
For each constraint of the form
±x ±y ≤ c or
±x ≤ c where the bound c is not stable,
the widening replaces c with the scalar immediately greater in
the user-supplied list, or +oo if it is greater than the greatest supplied
scalar.
The list must be sorted in strictly increasing order.
(Note that this operator is not exactly the same as the generic
ap_abstract0_widening_threshold
function which is synthesized
from ap_abstract0_sat_lincons
and
ap_abstract0_meet_lincons_array
.)
The ap_abstract0_shape_narrowing
function implements the
standard narrowing: it refines only those constraints that have
no finite bound.
Thus, it converges in a quadratic number of steps, at worse.
The shape library provides a few functions not generic enough to be
included in the APRON library.
They share the ap_abstract0_shape_
prefix.
Additionally to the widening with thresholds and narrowing functions described in the preceding section, the shape domain provides the following extra function:
ap_abstract0_shape_of_generator_array
converts a
generator set to an shape, with best-precision.
ap_abstract0_shape_add_epsilon
enlarges each constraint bound
by a user-specified factor of the maximum finite bound present in the
shape.
The following are not implemented and will raise an exception:
ap_abstract0_approximate
.
The distribution provides a fully automatic test suite with
shapetest
.
It compares the result of all transfer functions in the shape and polyhedron
domains, checking for soundness, best-precision and exactness properties.
The file shape/shape_fun.h
provides a direct access to all the
shape functions, without the abstraction provided by the manager.
Note that these functions perform less sanity checks, and so, may not be
as safe.
Wrapping and unwrapping a shape_t*
pointer within a
generic ap_abstract0_t*
is done using the
abstract0_of_shape
and shape_of_abstract0
functions.
The file shape/shape_internal.h
must be included to access to the
low-level representation of shapes
struct shape_t
and manager-specific data
struct _shape_internal_t
.
Direct access to private fields is not recommended.
shape.idl
define the documentation and the general interface of the module,
used by camlidl
to generate shape.ml*
, shape_caml.c
.
shape.h
define the manager and the interface with the Apron module Abstract0
.
shape_internal.h
define the data structure corresponding to shape graphs with
symbolic informations on the abstract nodes.
shape_fun.h
define all functions needed to use directly the shape domain.
shape_hgraph.c
define the basic operations (alloc/free/print) on heap graphs.
Your OCaml program must be linked with the following modules, in order:
gmp.cmxa
(or gmp.cma
for byte-code);
apron.cmxa
(or apron.cma
for byte-code);
shape.cmxa
(or shape.cma
for byte-code);
-cclib -lshapeMPQ
;
-cclib -lapron
.
-I
, depending
on your installation.
Examples:
ocamlc -I $HOME/lib gmp.cma apron.cma shape.cma mltest.ml -cclib -lshapeMPQ -cclib -lapron
ocamlopt -I $HOME/lib gmp.cmxa apron.cmxa shape.cmxa mltest.ml -cclib -lshapeMPQ -cclib -lapron
The shape library provides an Oct
OCaml module
There is no numeric suffix here: the OCaml wrapper is independent from the
chosen numerical type.
The Oct.manager_alloc
function returns a new manager that can
then be used with the standard Apron.Abstract0
module provided by
APRON.
The Oct
module also provides some implementation-specific functions:
of_generator_array
to convert from a set of generators to
an shape, with best abstraction,
widening_thresholds
widening,
narrowing
standard narrowing,
add_epsilon
perturbation function.
APRON
The shape abstract domain:
rahmed@liafa.jussieu.fr
.