1
+ :- module (_ , [] , [assertions , regtypes , nativeprops , modes ]).
2
+
3
+ :- doc(title , "Implementing a Polyhedra analysis in CiaoPP" ).
4
+
5
+ :- doc(module , "
6
+
7
+
8
+ In this tutorial we show how to implement a relational Abstract Domain
9
+ in CiaoPP . We implement a Polyhedra analysis to abstract numerical relations.
10
+
11
+ In order to keep this tutorial simple , we will not discuss technical
12
+ details about how to represent polyhedra nor how to implement some
13
+ complex operations . To ease the development of this domains we use an
14
+ already implemented library in the Ciao system : the @tt {poly_clpq } library
15
+ which implements a number of operations to manipulate (rational ,
16
+ closed ) polyhedra based on CLP (Q ) operations . Concretely we will use
17
+ the @tt {project /3}, @tt {convexhull /3}, @tt {simplify /2}, and
18
+ @tt {contains /2} predicates . The two first predicates are based on the
19
+ paper :
20
+
21
+ @begin {verbatim } \"Computing convex hulls with a linear
22
+ solver \", Florence Benoy , Andy King , Fr éd éric Mesnard . TPLP 5(1-2):
23
+ 259-271 (2005). @end{verbatim}
24
+
25
+ We begin by describing the abstraction and some operations to manipulate it :
26
+
27
+
28
+ @section {Operations over the Abstract Substitution }
29
+
30
+ Since we aim to abstract a rather complex property a substitution -like
31
+ abstraction pairing variables with values would not be very useful .
32
+ Instead of that , we define our abstraction as a pair @tt {(Ctrs , Vars )} such that :
33
+ @begin {itemize }
34
+ @item @tt {Ctrs } is a set of closed constraints defining a polyhedron and ,
35
+ @item @tt {Vars } is the set of variables over which the polyhedron is defined .
36
+ @end{itemize}
37
+
38
+ Notice that all the variables appearing in @tt {Ctrs } must appear in
39
+ @tt {Vars }. Moreover, carrying @tt{Vars} we are also inferring a type
40
+ property , only numerical variables can be in @tt {Vars }.
41
+
42
+ Finally , given a polyhedron @tt {([] , [X ])} we can infer that @tt {X } is
43
+ a numerical variable over which nothing else is known .
44
+
45
+ Now that we have defined how our abstraction looks like we can
46
+ implement the first predicate that CiaoPP requires :
47
+
48
+ @subsubsection {@var {asub(+ASub )}}
49
+ The predicate @tt {asub /1} is used to define the regtype @tt {asub /1}.
50
+ In our case is defined as follows :
51
+ ```ciao
52
+
53
+ asub('$bottom' ):-!.
54
+ asub (([] , _ )) :- ! .
55
+ asub ((Ctr , Vs )) :-
56
+ varset(Ctr , CtrVs ),
57
+ ord_subset(CtrVs , Vs ), ! ,
58
+ valid_ctrs(Ctr ).
59
+
60
+ valid_ctrs ([] ).
61
+ valid_ctrs ([>=(_ , _ )|Cs ]) :-
62
+ valid_ctrs(Cs ).
63
+ valid_ctrs ([=(_ , _ )|Cs ]) :-
64
+ valid_ctrs(Cs ).
65
+ valid_ctrs ([=<(_ , _ )|Cs ]) :-
66
+ valid_ctrs(Cs ).
67
+ ```
68
+
69
+ @subsubsection{@var{project(+Vars, +ASub, -Proj)}}
70
+
71
+ The predicate @tt {project /3} projects the information captured by
72
+ @var {ASub } over the set of variables @tt {Vars }. Luckily, the library
73
+ @tt{poly_clpq} already defines a project predicate which keeps the
74
+ constraints related with the given set of variables . However, the
75
+ obtained polyhedron may not be minimal (and we want to keep the
76
+ minimal representation ). Because of that, we also invoke the
77
+ @tt{simplify_poly/2} predicate which yields a reduced polyhedron.
78
+
79
+ ```ciao
80
+ project (_ , '$bottom' , '$bottom' ).
81
+ project (Vs , (Ctrs , CVs ), Proj ) :-
82
+ ord_intersection(Vs , CVs , Vs_pr ),
83
+ poly_clpq :project(Vs_pr , Ctrs , Ctr_pr ),
84
+ simplify_poly((Ctr_pr , Vs_pr ), Proj ).
85
+ ```
86
+
87
+ @subsubsection{@var{augment(+Vars, +ASub, -ASub_aug)}}
88
+
89
+ The predicate @tt {augment /3} increases the abstraction with the
90
+ variables in @var {Vars }. This variables are @em{free, fresh
91
+ variables }. Because of that, we know that they are not numerical at
92
+ this analysis point . Thus the predicate has to unify @var{ASub} with
93
+ @var{ASub_aug}.
94
+
95
+ ```ciao
96
+ augment (_Vars , Poly , Poly ) :- ! . %% They are free fresh vars so they are not in the polyhedron (yet)
97
+ ```
98
+
99
+ @subsubsection{@var{extend(+Sv, +Prime, +Call, -Success)}}
100
+
101
+ The @tt {extend /4} operation is used to update the information that
102
+ exists at call -time @var {Call } with the inferred information
103
+ @var {Prime }. In this case, we can just merge the constraints at
104
+ call -time whith the \"prime\" constraints and then simplify the
105
+ resulting constraint store .
106
+
107
+
108
+ ```ciao
109
+ extend (_Sv ,'$bottom' ,_Call ,'$bottom' ).
110
+ extend (_Sv , Prime , Call ,Success ):-
111
+ Prime = (Poly_pr , Vars_pr ),
112
+ Call = (Poly_cl , Vars_cl ),
113
+ ord_union(Poly_pr , Poly_cl , Poly_mr ), %% Merge Poly
114
+ ord_union(Vars_pr , Vars_cl , Vars_mr ), %% Merge Vars
115
+ simplify_poly((Poly_mr , Vars_mr ), Success ).
116
+ ```
117
+
118
+ Now that we have defined the operations required by CiaoPP to operate
119
+ over the abstraction , we turn our attention to define the operations
120
+ over the lattice :
121
+
122
+ @section {Operations over the Lattice }
123
+
124
+ @subsubsection {@var {less_or_equal(+ASub1 , +ASub2 )}}
125
+
126
+ The @tt {less_or_equal /2} predicate succeeds if the abstraction
127
+ @var {ASub1 } is less or equal than @var {ASub2 }. In this case we use the
128
+ @tt{contains/2} predicate to check whether @var{ASub2} contains
129
+ @var{ASub1}.
130
+
131
+ ```ciao
132
+
133
+ less_or_equal ('$bottom' , _ ) :- ! .
134
+ less_or_equal ((Poly1 , Vars ), (Poly2 , Vars )) :-
135
+ poly_clpq :contains(Poly2 , Poly1 ), ! .
136
+ ```
137
+
138
+ @subsubsection{@var{lub(+ASub1, +ASub2, -Lub)} and @var{widen(+ASub1, +ASub2, -Widen)}}
139
+
140
+ The predicate @tt {lub /3} is used to compute the @em {least upper bound }
141
+ of two abstractions . Thus, given two polyhedrons, their convexhull is
142
+ a safe candidate for least upper bound . Before, computing the
143
+ convexhull we invoke the predicate @tt {match_dimensions /4} which will
144
+ keep both abstractions defined over the variables appearing in both polyhedra .
145
+
146
+ The predicate @tt {widen /3} is used to acelerate the computation of the fixpoint . Many different widens can be defined for a polyhedra domain. That is outside of the scope of this tutorial, thus we use @tt{lub/3}.
147
+
148
+ ```ciao
149
+
150
+ lub (ASub1 ,'$bottom' ,ASub1 ):- ! .
151
+ lub ('$bottom' ,ASub2 ,ASub2 ):- ! .
152
+ lub (ASub1 ,ASub2 ,Lub ):-
153
+ match_dimensions(ASub1 ,ASub2 ,New_ASub1 ,New_ASub2 ),
154
+ New_ASub1 = (Poly1 ,Vars ),
155
+ New_ASub2 = (Poly2 ,Vars ),
156
+ poly_clpq :convhull(Poly1 , Poly2 , Poly_h ),! ,
157
+ Lub = (Poly_h ,Vars ).
158
+ lub (_ASub1 ,_ASub2 ,'$top' ).
159
+
160
+ widen (L1 , L2 , W ) :-
161
+ lub(L1 , L2 , W ).
162
+
163
+ match_dimensions (ASub1 ,ASub2 ,ASub1 ,ASub2 ):-
164
+ ASub1 = (_Poly1 ,Vars1 ),
165
+ ASub2 = (_Poly2 ,Vars2 ),
166
+ Vars1 == Vars2 ,! .
167
+ match_dimensions (ASub1 ,ASub2 ,New_ASub1 ,New_ASub2 ):-
168
+ ASub1 = (_Poly1 ,Vars1 ),
169
+ ASub2 = (_Poly2 ,Vars2 ),
170
+ ord_intersection(Vars1 ,Vars2 ,Vars ),
171
+ polyhedra_clpq :project(Vars , ASub1 ,New_ASub1 ),
172
+ polyhedra_clpq :project(Vars ,ASub2 ,New_ASub2 ).
173
+
174
+
175
+ ```
176
+
177
+ @section{Abstracting operations}
178
+
179
+ Now that we have described the lattice and the abstract representation used , we describe how the elements of the concrete program are abstracted .
180
+
181
+ @subsubsection{@var{topmost(+Vars, +MaybeCall, -TopAbs)}}
182
+
183
+ The predicate @tt {topmost /3} is used to compute an abstraction such that nothing is known about the variables in @var {Vars }. We have to consider the case in which @var{MaybeCall} unifies with an atom @tt{not_provided}, that means that a top-most abstraction over the variables in @var{Vars} has to be defined.
184
+ ```ciao
185
+
186
+ topmost (_ , not_provided , ([] ,[] )).
187
+ topmost (_ , Poly , Poly ) :- ! .
188
+ ```
189
+
190
+
191
+ @subsubsection{@var{amgu(+Var, +Term, +Call, -Succ)}}
192
+ The predicate @tt {amgu /4} computes the @em {abstract most general unifier } for the unification @var {Var =Term } given the current abstracted information @var {Call }. Hence, @var{Succ} is the result of update @var{Call} with such an information.
193
+ Is important to note that @var {Var } is indeed a variable so cases are only need to be considered about @var {Term }.
194
+
195
+ For our polyhedra domain , defining @tt {amgu /4} is pretty straightforward :
196
+
197
+ ```ciao
198
+ amgu(Var , Term , Poly1 , Poly2 ) :-
199
+ number(Term ), !,
200
+ poly_add_constraint(Poly1 , Var =Term , Poly2 ).
201
+ amgu (Var , Term , Poly1 , Poly2 ) :-
202
+ var(Term ), ! ,
203
+ poly_add_constraint(Poly1 , Var = Term , Poly2 ).
204
+ amgu (_ , _ , Poly1 , Poly1 ).
205
+
206
+
207
+ poly_add_constraint ('$bottom' , _ , '$bottom' ).
208
+ poly_add_constraint ((Poly , Vars ), Const , NASub ) :-
209
+ varset(Const , ConstVars ), sort(ConstVars , ConstVars_s ),
210
+ Poly_add = [Const |Poly ],
211
+ ord_union(Vars , ConstVars_s , Vars_add ),
212
+ simplify_poly((Poly_add , Vars_add ), NASub ).
213
+ ```
214
+
215
+ @subsubsection{@var{known_builtin(+Sg_Key)}}
216
+
217
+ This flag allow the analyzer to know whith literals our domain can
218
+ abstract . By default, the analyzer will consider that is analyzing a
219
+ pure program , i . e., only unifications and predicate calls are present.
220
+ However , we may want to predefine the abstraction for some known
221
+ predicates or , as in this case , capture arithmetical expressions .
222
+
223
+ ```ciao
224
+
225
+ known_builtin ('is/2' ).
226
+ known_builtin ('>/2' ).
227
+ known_builtin ('>=/2' ).
228
+ known_builtin ('</2' ).
229
+ known_builtin ('=</2' ).
230
+ ```
231
+
232
+ @subsubsection{@var{abstract_literal(+Sg_key, +Sg, +Call, -Succ)}}
233
+
234
+ Now that we have declared which builtins our domain knows how to
235
+ handle we have to describe their abstraction . The predicate
236
+ @tt{abstract_literal/4} implements how to abstract a literal
237
+ (@var{Sg}) which has a known @var{Sg_key} key.
238
+ In our case we can add the new constraints to the polyhedron in @var {Call } and then simplify it to obtain the success abstraction @var {Succ }.
239
+
240
+ ```ciao
241
+
242
+ abstract_literal ('is/2' , is(X , Y ), Poly_call , Poly_succ ) :-
243
+ %% Once again, we are keeping it simple
244
+ poly_add_constraint(Poly_call , X = Y , Poly_succ ).
245
+ abstract_literal ('>=/2' , >=(X , Y ), Poly_call , Poly_succ ) :-
246
+ poly_add_constraint(Poly_call , >= (X ,Y ), Poly_succ ).
247
+ abstract_literal ('>/2' , >(X , Y ), Poly_call , Poly_succ ) :-
248
+ abstract_literal('>=/2' , '>=' (X , Y + 1 ),Poly_call , Poly_succ ).
249
+ abstract_literal ('=</2' , =<(X , Y ), Poly_call , Poly_succ ) :-
250
+ abstract_literal('>=/2' , '>=' (Y ,X ),Poly_call , Poly_succ ).
251
+ abstract_literal ('</2' , <(X , Y ), Poly_call , Poly_succ ) :-
252
+ abstract_literal('>/2' , '>' (Y ,X ), Poly_call , Poly_succ ).
253
+ ```
254
+
255
+ @section{Other Auxiliary Predicates and Input-Output Operations}
256
+
257
+ Finally , a number of extra predicates can be defined . Among those, the
258
+ following are predefined but may need to be redefined .
259
+
260
+ @subsubsection{@var{needs(+Option)}}
261
+
262
+ Succeeds if the domains needs an operation @var {+Option } for termination
263
+ or correctness . The supported operations are:
264
+
265
+ @begin{enumerate}
266
+ @item @tt{widen} : whether widening is necessary for termination,
267
+ @item @tt{clauses_lub} : whether the lub must be performed over the
268
+ abstract substitution split by clase ,
269
+ @item @tt {aux_info } :whether the information in the abstract
270
+ substitutions is not complete and an external solver may be
271
+ needed ,currently only used when outputing the analysis in a
272
+ @tt {. dump} file.
273
+ @end{enumerate}
274
+ by default it is assumed that nothing is needed . However, we will set widen as needed in this case.
275
+
276
+ @subsubsection{@var{asub_to_native(+ASub,+Qv,+OutFlag,-NativeStat,-NativeComp}}
277
+
278
+ @var{NativeStat} and @var{NativeComp} are the list of native (state
279
+ and computational , resp . ) properties that are the concretization of
280
+ abstract substitution @var {ASub } on variables @var {Qv } for the
281
+ domain . These are later translated to the properties which are visible
282
+ in the preprocessing unit .
283
+
284
+ @subsubsection{@var{input_interface(+Prop, +Kind, ?Struct0, -Struct1)}}
285
+ @var{Prop} is a native property that is relevant to domain
286
+ (i.e., the domain knows how to fully --@var{+Kind}=perfect-- or
287
+ approximately --@var {-Kind }=approx -- abstract it ) and
288
+ @var {Struct1 } is a (domain defined ) structure resulting of
289
+ adding the (domain dependent ) information conveyed by @var {Prop }
290
+ to structure @var {Struct0 }. This way, the properties relevant to
291
+ a domain are being accumulated .
292
+
293
+ @subsubsection{@var{input_user_interface(+Struct, +Qv, -ASub, +Sg, ?MaybeCallASub)}}
294
+ @var{ASub} is the abstraction of the information collected in
295
+ @var{Struct} (a domain defined structure) on variables
296
+ @var{Qv}.
297
+
298
+ ```ciao
299
+ needs (widen ) :- ! .
300
+
301
+ asub_to_native (ASub , _ , _ , [ASub ], [] ).
302
+
303
+ input_interface (_Prop , _Kind , _Struct0 , _Struct1 ) :- fail .
304
+
305
+ input_user_interface (Struct , Qv , ASub , _ , _ ) :- topmost(Qv , Struct , ASub ).
306
+ ```
307
+ ").
0 commit comments