1 (* This module provides an annotation function for Clight programs
2 when given the cost associated to each cost labels of the
6 let error_prefix = "Clight Annotator"
7 let error = Error.global_error error_prefix
10 let cost_id_prefix = "__cost"
11 let cost_incr_prefix = "__cost_incr"
14 (* Program var and fun names, cost labels and labels *)
16 let string_set_of_list l =
17 List.fold_left (fun res s -> StringTools.Set.add s res)
18 StringTools.Set.empty l
21 (names1, cost_labels1, user_labels1)
22 (names2, cost_labels2, user_labels2) =
23 (StringTools.Set.union names1 names2,
24 CostLabel.Set.union cost_labels1 cost_labels2,
25 Label.Set.union user_labels1 user_labels2)
27 let empty_triple = (StringTools.Set.empty, CostLabel.Set.empty, Label.Set.empty)
29 let name_singleton id =
30 (StringTools.Set.singleton id, CostLabel.Set.empty, Label.Set.empty)
32 let cost_label_singleton cost_lbl =
33 (StringTools.Set.empty, CostLabel.Set.singleton cost_lbl, Label.Set.empty)
35 let label_singleton lbl =
36 (StringTools.Set.empty, CostLabel.Set.empty, Label.Set.singleton lbl)
38 let list_union l = List.fold_left triple_union empty_triple l
40 let f_ctype ctype sub_ctypes_res =
41 let res = match ctype with
42 | Clight.Tstruct (id, fields) | Clight.Tunion (id, fields) ->
43 (string_set_of_list (id :: (List.map fst fields)),
44 CostLabel.Set.empty, Label.Set.empty)
45 | Clight.Tcomp_ptr id -> name_singleton id
46 | _ -> empty_triple in
47 list_union (res :: sub_ctypes_res)
49 let f_expr _ sub_ctypes_res sub_expr_descrs_res =
50 list_union (sub_ctypes_res @ sub_expr_descrs_res)
52 let f_expr_descr ed sub_ctypes_res sub_exprs_res =
53 let res = match ed with
54 | Clight.Evar id | Clight.Efield (_, id) | Clight.Ecall (id, _, _) ->
56 | Clight.Ecost (cost_lbl, _) -> cost_label_singleton cost_lbl
57 | _ -> empty_triple in
58 list_union (res :: (sub_ctypes_res @ sub_exprs_res))
60 let f_stmt stmt sub_exprs_res sub_stmts_res =
61 let stmt_res = match stmt with
62 | Clight.Slabel (lbl, _) | Clight.Sgoto lbl -> label_singleton lbl
63 | Clight.Scost (cost_lbl, _) -> cost_label_singleton cost_lbl
64 | _ -> empty_triple in
65 list_union (stmt_res :: (sub_exprs_res @ sub_stmts_res))
67 let body_idents = ClightFold.statement f_ctype f_expr f_expr_descr f_stmt
70 let def_idents = function
71 | Clight.Internal def ->
74 (List.map fst (def.Clight.fn_params @ def.Clight.fn_vars)) in
75 let (names, cost_labels, user_labels) =
76 body_idents def.Clight.fn_body in
77 (StringTools.Set.union vars names, cost_labels, user_labels)
78 | Clight.External (id, _, _) ->
79 (StringTools.Set.singleton id, CostLabel.Set.empty, Label.Set.empty) in
80 let fun_idents (id, f_def) =
81 let (names, cost_labels, user_labels) = def_idents f_def in
82 (StringTools.Set.add id names, cost_labels, user_labels) in
83 let f idents def = triple_union idents (fun_idents def) in
84 List.fold_left f empty_triple p.Clight.prog_funct
87 let (names, _, _) = prog_idents p in names
89 let (_, cost_labels, _) = prog_idents p in cost_labels
91 let (_, _, user_labels) = prog_idents p in user_labels
94 let (_, cost_labels, user_labels) = prog_idents p in
96 CostLabel.Set.fold (fun lbl lbls -> StringTools.Set.add lbl lbls)
97 cost_labels StringTools.Set.empty in
98 Label.Set.fold (fun lbl lbls -> StringTools.Set.add lbl lbls) user_labels all
101 let (names, cost_labels, user_labels) = prog_idents p in
102 let to_string_set fold set =
103 fold (fun lbl idents -> StringTools.Set.add lbl idents) set
104 StringTools.Set.empty in
105 let cost_labels = to_string_set CostLabel.Set.fold cost_labels in
106 let user_labels = to_string_set Label.Set.fold user_labels in
107 StringTools.Set.union names (StringTools.Set.union cost_labels user_labels)
109 let fresh_ident base p =
110 StringTools.Gen.fresh_prefix (all_idents p) base
112 let fresh_universe base p =
113 let universe = fresh_ident base p in
114 StringTools.Gen.new_universe universe
116 let make_fresh base p =
117 let universe = fresh_universe base p in
118 (fun () -> StringTools.Gen.fresh universe)
121 (* Instrumentation *)
123 let int_typ = Clight.Tint (Clight.I32, AST.Signed)
125 let const_int i = Clight.Expr (Clight.Econst_int i, int_typ)
127 (* Instrument an expression. *)
129 let rec instrument_expr cost_mapping cost_incr e =
130 let Clight.Expr (e, t) = e in
132 | Clight.Ecost (lbl, e)
133 when CostLabel.Map.mem lbl cost_mapping &&
134 CostLabel.Map.find lbl cost_mapping = 0 ->
137 let e' = instrument_expr_descr cost_mapping cost_incr e in
139 and instrument_expr_descr cost_mapping cost_incr e = match e with
140 | Clight.Econst_int _ | Clight.Econst_float _ | Clight.Evar _
141 | Clight.Esizeof _ -> e
143 let e' = instrument_expr cost_mapping cost_incr e in
145 | Clight.Eaddrof e ->
146 let e' = instrument_expr cost_mapping cost_incr e in
148 | Clight.Eunop (op, e) ->
149 let e' = instrument_expr cost_mapping cost_incr e in
150 Clight.Eunop (op, e')
151 | Clight.Ebinop (op, e1, e2) ->
152 let e1' = instrument_expr cost_mapping cost_incr e1 in
153 let e2' = instrument_expr cost_mapping cost_incr e2 in
154 Clight.Ebinop (op, e1', e2')
155 | Clight.Ecast (t, e) ->
156 let e' = instrument_expr cost_mapping cost_incr e in
158 | Clight.Econdition (e1, e2, e3) ->
159 let e1' = instrument_expr cost_mapping cost_incr e1 in
160 let e2' = instrument_expr cost_mapping cost_incr e2 in
161 let e3' = instrument_expr cost_mapping cost_incr e3 in
162 Clight.Econdition (e1', e2', e3')
163 | Clight.Eandbool (e1, e2) ->
164 let e1' = instrument_expr cost_mapping cost_incr e1 in
165 let e2' = instrument_expr cost_mapping cost_incr e2 in
166 Clight.Eandbool (e1', e2')
167 | Clight.Eorbool (e1, e2) ->
168 let e1' = instrument_expr cost_mapping cost_incr e1 in
169 let e2' = instrument_expr cost_mapping cost_incr e2 in
170 Clight.Eorbool (e1', e2')
171 | Clight.Efield (e, x) ->
172 let e' = instrument_expr cost_mapping cost_incr e in
173 Clight.Efield (e', x)
174 | Clight.Ecost (lbl, e) when CostLabel.Map.mem lbl cost_mapping ->
175 let e' = instrument_expr cost_mapping cost_incr e in
176 let incr = CostLabel.Map.find lbl cost_mapping in
177 if incr = 0 then let Clight.Expr (e'', _) = e' in e''
178 else Clight.Ecall (cost_incr, const_int incr, e')
179 | Clight.Ecost (_, e) ->
180 let Clight.Expr (e', _) = instrument_expr cost_mapping cost_incr e in
182 | Clight.Ecall (x, e1, e2) -> assert false (* Should not happen. *)
184 (* Instrument a statement. *)
186 let rec instrument_body cost_mapping cost_incr stmt = match stmt with
187 | Clight.Sskip | Clight.Sbreak | Clight.Scontinue | Clight.Sreturn None
190 | Clight.Sassign (e1, e2) ->
191 let e1' = instrument_expr cost_mapping cost_incr e1 in
192 let e2' = instrument_expr cost_mapping cost_incr e2 in
193 Clight.Sassign (e1', e2')
194 | Clight.Scall (eopt, f, args) ->
195 let eopt' = match eopt with
197 | Some e -> Some (instrument_expr cost_mapping cost_incr e) in
198 let f' = instrument_expr cost_mapping cost_incr f in
199 let args' = List.map (instrument_expr cost_mapping cost_incr) args in
200 Clight.Scall (eopt', f', args')
201 | Clight.Ssequence (s1, s2) ->
202 Clight.Ssequence (instrument_body cost_mapping cost_incr s1,
203 instrument_body cost_mapping cost_incr s2)
204 | Clight.Sifthenelse (e, s1, s2) ->
205 let e' = instrument_expr cost_mapping cost_incr e in
206 let s1' = instrument_body cost_mapping cost_incr s1 in
207 let s2' = instrument_body cost_mapping cost_incr s2 in
208 Clight.Sifthenelse (e', s1', s2')
209 | Clight.Swhile (e, s) ->
210 let e' = instrument_expr cost_mapping cost_incr e in
211 let s' = instrument_body cost_mapping cost_incr s in
212 Clight.Swhile (e', s')
213 | Clight.Sdowhile (e, s) ->
214 let e' = instrument_expr cost_mapping cost_incr e in
215 let s' = instrument_body cost_mapping cost_incr s in
216 Clight.Sdowhile (e', s')
217 | Clight.Sfor (s1, e, s2, s3) ->
218 let s1' = instrument_body cost_mapping cost_incr s1 in
219 let e' = instrument_expr cost_mapping cost_incr e in
220 let s2' = instrument_body cost_mapping cost_incr s2 in
221 let s3' = instrument_body cost_mapping cost_incr s3 in
222 Clight.Sfor (s1', e', s2', s3')
223 | Clight.Sreturn (Some e) ->
224 let e' = instrument_expr cost_mapping cost_incr e in
225 Clight.Sreturn (Some e')
226 | Clight.Sswitch (e, ls) ->
227 let e' = instrument_expr cost_mapping cost_incr e in
228 let ls' = instrument_ls cost_mapping cost_incr ls in
229 Clight.Sswitch (e', ls')
230 | Clight.Slabel (lbl, s) ->
231 let s' = instrument_body cost_mapping cost_incr s in
232 Clight.Slabel (lbl, s')
233 | Clight.Scost (lbl, s) when CostLabel.Map.mem lbl cost_mapping ->
234 (* Keep the cost label in the code. *)
235 let s' = instrument_body cost_mapping cost_incr s in
236 let incr = CostLabel.Map.find lbl cost_mapping in
237 let fun_typ = Clight.Tfunction ([int_typ], Clight.Tvoid) in
238 let f = Clight.Expr (Clight.Evar cost_incr, fun_typ) in
239 let args = [Clight.Expr (Clight.Econst_int incr, int_typ)] in
240 Clight.Scost (lbl, Clight.Ssequence (Clight.Scall (None, f, args), s'))
242 let s' = instrument_body cost_mapping cost_incr s in
243 let incr = CostLabel.Map.find lbl cost_mapping in
246 let fun_typ = Clight.Tfunction ([int_typ], Clight.Tvoid) in
247 let f = Clight.Expr (Clight.Evar cost_incr, fun_typ) in
248 let args = [Clight.Expr (Clight.Econst_int incr, int_typ)] in
249 Clight.Ssequence (Clight.Scall (None, f, args), s')
251 | Clight.Scost (lbl, s) ->
252 (* Keep the cost label in the code and show the increment of 0. *)
253 let s' = instrument_body cost_mapping cost_incr s in
254 let fun_typ = Clight.Tfunction ([int_typ], Clight.Tvoid) in
255 let f = Clight.Expr (Clight.Evar cost_incr, fun_typ) in
256 let args = [Clight.Expr (Clight.Econst_int 0, int_typ)] in
257 Clight.Scost (lbl, Clight.Ssequence (Clight.Scall (None, f, args), s'))
259 instrument_body cost_mapping cost_incr s
261 and instrument_ls cost_mapping cost_incr = function
262 | Clight.LSdefault s ->
263 let s' = instrument_body cost_mapping cost_incr s in
265 | Clight.LScase (i, s, ls) ->
266 let s' = instrument_body cost_mapping cost_incr s in
267 let ls' = instrument_ls cost_mapping cost_incr ls in
268 Clight.LScase (i, s', ls')
270 (* Instrument a function. *)
272 let instrument_funct cost_mapping cost_incr (id, def) =
273 let def = match def with
274 | Clight.Internal def ->
275 let body = instrument_body cost_mapping cost_incr def.Clight.fn_body in
276 Clight.Internal { def with Clight.fn_body = body }
277 | Clight.External _ -> def
281 (* This is the declaration of the cost variable. *)
283 let cost_decl cost_id =
284 let init = [Clight.Init_int32 0] in
285 ((cost_id, init), int_typ)
287 (* This is the definition of the increment cost function. *)
289 let cost_incr_def cost_id cost_incr =
290 let int_var x = Clight.Expr (Clight.Evar x, int_typ) in
291 let param = "incr" in
292 let cost = int_var cost_id in
293 let increment = int_var param in
295 Clight.Expr (Clight.Ebinop (Clight.Oadd, cost, increment), int_typ) in
296 let stmt = Clight.Sassign (cost, cost_increment) in
298 { Clight.fn_return = Clight.Tvoid ;
299 Clight.fn_params = [(param, int_typ)] ;
300 Clight.fn_vars = [] ;
301 Clight.fn_body = stmt } in
302 (cost_incr, Clight.Internal cfun)
304 (* Create a fresh uninitialized cost variable for each external function. This
305 will be used by the Cost plug-in of the Frama-C platform. *)
307 let extern_cost_variables make_unique functs =
308 let prefix = "_cost_of_" in
309 let f (decls, map) (_, def) = match def with
310 | Clight.Internal _ -> (decls, map)
311 | Clight.External (id, _, _) ->
312 let new_var = make_unique (prefix ^ id) in
313 (decls @ [cost_decl new_var], StringTools.Map.add id new_var map) in
314 List.fold_left f ([], StringTools.Map.empty) functs
316 let save_tmp tmp_file s =
317 let cout = open_out tmp_file in
318 output_string cout s ;
322 (** [instrument prog cost_map] instruments the program [prog]. First a fresh
323 global variable --- the so-called cost variable --- is added to the program.
324 Then, each cost label in the program is replaced by an increment of the cost
325 variable, following the mapping [cost_map]. The function also returns the
326 name of the cost variable and the name of the cost increment function. *)
328 let instrument p cost_mapping =
330 (* Create a fresh 'cost' variable. *)
331 let names = names p in
332 let make_unique = StringTools.make_unique names in
333 let cost_id = make_unique cost_id_prefix in
334 let cost_decl = cost_decl cost_id in
336 (* Create a fresh uninitialized global variable for each extern function. *)
337 let (extern_cost_decls, extern_cost_variables) =
338 extern_cost_variables make_unique p.Clight.prog_funct in
340 (* Define an increment function for the cost variable. *)
342 StringTools.Gen.fresh_prefix (StringTools.Set.add cost_id names)
344 let cost_incr_def = cost_incr_def cost_id cost_incr in
346 (* Instrument each function *)
348 List.map (instrument_funct cost_mapping cost_incr) p.Clight.prog_funct in
350 (* Glue all this together. *)
351 let prog_vars = cost_decl :: extern_cost_decls @ p.Clight.prog_vars in
352 let prog_funct = cost_incr_def :: prog_funct in
354 { p with Clight.prog_vars = prog_vars ; Clight.prog_funct = prog_funct } in
356 (* Save the resulted program. Then re-parse it.
357 Indeed, the instrumentation may add side-effects in expressions, which is
358 not Clight compliant. Re-parsing the result with CIL will remove these
359 side-effects in expressions to obtain a Clight program. *)
360 let output = ClightPrinter.print_program p' in
361 let res = ClightParser.process (`Source ("annotated", output)) in
362 (res, cost_id, cost_incr, extern_cost_variables)