Newer
Older
(**************************************************************************)
(* *)
(* This file is part of WP plug-in of Frama-C. *)
(* *)
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
(* CEA (Commissariat a l'energie atomique et aux energies *)
(* alternatives) *)
(* *)
(* you can redistribute it and/or modify it under the terms of the GNU *)
(* Lesser General Public License as published by the Free Software *)
(* Foundation, version 2.1. *)
(* *)
(* It is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
(* GNU Lesser General Public License for more details. *)
(* *)
(* See the GNU Lesser General Public License version 2.1 *)
(* for more details (enclosed in the file licenses/LGPLv2.1). *)
(* *)
(**************************************************************************)
(** Wp computation using the CFG *)
open Cil_types
open Cil_datatype
module Cfg (W : Mcfg.S) = struct
let dkey = Wp_parameters.register_category "calculus" (* Debugging key *)
let debug fmt = Wp_parameters.debug ~dkey fmt
(** Before storing something at a program point, we have to process the label
* at that point. *)
let do_labels wenv e obj =
let do_lab s o l = W.label wenv s l o in
let obj = do_lab None obj Clabels.here in
let stmt = Cil2cfg.get_edge_stmt e in
let labels = Cil2cfg.get_edge_labels e in
List.fold_left (do_lab stmt) obj labels
let add_hyp wenv obj h =
debug "add hyp %a@." WpPropId.pp_pred_info h;
W.add_hyp wenv h obj
let add_goal wenv obj g =
debug "add goal %a@." WpPropId.pp_pred_info g;
W.add_goal wenv g obj
(*[LC] Adding scopes for loop invariant preservation: WHY ???? *)
(*[LC] Nevertheless, if required, this form should be used (BTS #1462)
let open_scope wenv formals blocks =
List.fold_right
(fun b obj -> W.scope wenv b.blocals Mcfg.SC_Block_out obj)
blocks
(W.scope wenv formals Mcfg.SC_Function_out W.empty)
match WpPropId.is_loop_preservation (fst g) with
| None -> W.add_goal wenv g obj
| Some stmt ->
debug "add scope for loop preservation %a@." WpPropId.pp_pred_info g ;
let blocks = Kernel_function.find_all_enclosing_blocks stmt in
let kf = Kernel_function.find_englobing_kf stmt in
let formals = Kernel_function.get_formals kf in
W.merge wenv (W.add_goal wenv g (open_scope wenv formals blocks)) obj
*)
let add_assigns_goal wenv obj g_assigns = match g_assigns with
| WpPropId.AssignsAny _ | WpPropId.NoAssignsInfo -> obj
| WpPropId.AssignsLocations a ->
debug "add assign goal (@[%a@])@."
WpPropId.pretty (WpPropId.assigns_info_id a);
W.add_assigns wenv a obj
let add_assigns_hyp wenv obj h_assigns = match h_assigns with
| WpPropId.AssignsLocations (h_id, a) ->
let hid = Some h_id in
let obj = W.use_assigns wenv a.WpPropId.a_stmt hid a obj in
Some a.WpPropId.a_label, obj
| WpPropId.AssignsAny a ->
Wp_parameters.warning ~current:true ~once:true
"Missing assigns clause (assigns 'everything' instead)" ;
let obj = W.use_assigns wenv a.WpPropId.a_stmt None a obj in
Some a.WpPropId.a_label, obj
| WpPropId.NoAssignsInfo -> None, obj
(** detect if the computation of the result at [edge] is possible,
* or if it will loop. If [strategy] are provide,
* cut are done on edges with cut properties,
* and if not, cut are done on loop node back edge if any.
* TODO: maybe this should be done while building the strategy ?
* *)
exception Stop of Cil2cfg.edge
let test_edge_loop_ok cfg strategy edge =
debug "[test_edge_loop_ok] (%s strategy) for %a"
(match strategy with None -> "without" | Some _ -> "with")
Cil2cfg.pp_edge edge;
let rec collect_edge_preds set e =
let cut =
match strategy with None -> Cil2cfg.is_back_edge e
| Some strategy ->
let e_annots = WpStrategy.get_annots strategy e in
(WpStrategy.get_cut e_annots <> [])
in
if cut then () (* normal loop cut *)
else if Cil2cfg.Eset.mem e set
then (* e is already in set : loop without cut ! *)
raise (Stop e)
else (* add e to set and continue with its preds *)
let set = Cil2cfg.Eset.add e set in
let preds = Cil2cfg.pred_e cfg (Cil2cfg.edge_src e) in
List.iter (collect_edge_preds set) preds
in
try
let _ = collect_edge_preds Cil2cfg.Eset.empty edge in
debug "[test_edge_loop_ok] ok.";
true
with Stop e ->
begin
debug "[test_edge_loop_ok] loop without cut detected at %a"
Cil2cfg.pp_edge e;
false
end
(** to store the results of computations :
* we store a result for each edge, and also a list of proof obligations.
*
* Be careful that there are two modes of computation :
* the first one ([Pass1]) is used to prove the establishment of properties
* while the second (after [change_mode_if_needed]) prove the preservation.
* See {!R.set} for more details.
* *)
module R : sig
type t
val empty : Cil2cfg.t -> t
val is_pass1 : t -> bool
val change_mode_if_needed : t -> unit
val find : t -> Cil2cfg.edge -> W.t_prop
val set : WpStrategy.strategy -> W.t_env ->
t -> Cil2cfg.edge -> W.t_prop -> W.t_prop
val add_oblig : t -> Clabels.c_label -> W.t_prop -> unit
val add_memo : t -> Cil2cfg.edge -> W.t_prop -> unit
end =
struct
type t_mode = Pass1 | Pass2
module HE = Cil2cfg.HE (struct type t = W.t_prop option end)
module LabObligs : sig
type t
val empty : t
val is_empty : t -> bool
val get_of_label : t -> Clabels.c_label -> W.t_prop list
val get_of_edge : t -> Cil2cfg.edge -> W.t_prop list
val add_to_label : t -> Clabels.c_label -> W.t_prop -> t
val add_to_edge : t -> Cil2cfg.edge -> W.t_prop -> t
end = struct
type key = Olab of Clabels.c_label | Oedge of Cil2cfg.edge
let cmp_key k1 k2 = match k1, k2 with
| Olab l1, Olab l2 when Clabels.equal l1 l2 -> true
| Oedge e1, Oedge e2 when Cil2cfg.same_edge e1 e2 -> true
| _ -> false
(* TODOopt: could have a sorted list... *)
type t = (key * W.t_prop list) list
let empty = []
let is_empty obligs = (obligs = [])
let add obligs k obj =
let rec aux l_obligs = match l_obligs with
| [] -> (k, [obj])::[]
| (k', obligs)::tl when cmp_key k k' ->
(k, obj::obligs)::tl
| o::tl -> o::(aux tl)
in aux obligs
let add_to_label obligs label obj = add obligs (Olab label) obj
let add_to_edge obligs e obj = add obligs (Oedge e) obj
let get obligs k =
let rec aux l_obligs = match l_obligs with
| [] -> []
| (k', obligs)::_ when cmp_key k k' -> obligs
| _::tl -> aux tl
in aux obligs
let get_of_label obligs label = get obligs (Olab label)
let get_of_edge obligs e = get obligs (Oedge e)
end
type t = {
mutable mode : t_mode ;
cfg: Cil2cfg.t;
tbl : HE.t ;
mutable memo : LabObligs.t;
mutable obligs : LabObligs.t;
}
let empty cfg =
debug "start computing (pass 1)@.";
{ mode = Pass1; cfg = cfg; tbl = HE.create 97 ;
obligs = LabObligs.empty ; memo = LabObligs.empty ;}
let is_pass1 res = (res.mode = Pass1)
let add_oblig res label obj =
debug "add proof obligation at label %a =@. @[<hov2> %a@]@."
Clabels.pretty label W.pretty obj;
res.obligs <- LabObligs.add_to_label (res.obligs) label obj
let add_memo res e obj =
debug "Memo goal for Pass2 at %a=@. @[<hov2> %a@]@."
Cil2cfg.pp_edge e W.pretty obj;
res.memo <- LabObligs.add_to_edge (res.memo) e obj
let find res e =
let obj = HE.find res.tbl e in
match obj with None ->
Wp_parameters.warning "find edge annot twice (%a) ?"
Cil2cfg.pp_edge e;
raise Not_found
| Some obj ->
if (res.mode = Pass2)
&& (List.length
(Cil2cfg.pred_e res.cfg (Cil2cfg.edge_src e)) < 2) then
begin
(* it should be used once only : can free it *)
HE.replace res.tbl e None;
debug "clear edge %a@." Cil2cfg.pp_edge e
end;
obj
(** If needed, clear wp table to compute Pass2.
* If nothing has been stored in res.memo, there is nothing to do. *)
let change_mode_if_needed res =
if LabObligs.is_empty res.memo then ()
else
begin
debug "change to Pass2 (clear wp table)@.";
begin try
let e_start = Cil2cfg.start_edge res.cfg in
let start_goal = find res e_start in
add_memo res e_start start_goal
with Not_found -> ()
end;
HE.clear res.tbl;
(* move memo obligs of Pass1 to obligs for Pass2 *)
res.obligs <- res.memo;
res.memo <- LabObligs.empty;
res.mode <- Pass2
end
let collect_oblig wenv res e obj =
let labels = Cil2cfg.get_edge_labels e in
let add obj obligs =
List.fold_left (fun obj o -> W.merge wenv o obj) obj obligs
in
let obj =
try
debug "get proof obligation at edge %a@." Cil2cfg.pp_edge e;
let obligs = LabObligs.get_of_edge res.obligs e in
add obj obligs
with Not_found -> obj
in
let add_lab_oblig obj label =
try
debug "get proof obligation at label %a@." Clabels.pretty label;
let obligs = LabObligs.get_of_label res.obligs label in
add obj obligs
with Not_found -> obj
in
let obj = List.fold_left add_lab_oblig obj labels in
obj
(** We have found some assigns hypothesis in the strategy :
* it means that we skip the corresponding bloc, ie. we directly compute
* the result before the block : (forall assigns. P),
* and continue with empty. *)
let use_assigns wenv res obj h_assigns =
let lab, obj = add_assigns_hyp wenv obj h_assigns in
match lab with
| None -> obj
| Some label -> add_oblig res label obj; W.empty
(** store the result p for the computation of the edge e.
*
* - In Compute mode :
if we have some hyps H about this edge, store H => p
if we have some goal G about this edge, store G /\ p
if we have annotation B to be used as both H and G, store B /\ B=>P
We also have to add H and G from HI (invariants computed in Pass1 mode)
So finally, we build : [ H => [ BG /\ (BH => (G /\ P)) ] ]
*)
let set strategy wenv res e obj =
try
match (HE.find res.tbl e) with
| None -> raise Not_found
| Some obj -> obj
(* cannot warn here because it can happen with CUT properties.
* We could check that obj is the same thing than the founded result *)
(* Wp_parameters.fatal "strange loop at %a ?" Cil2cfg.pp_edge e *)
with Not_found ->
begin
let e_annot = WpStrategy.get_annots strategy e in
let h_prop = WpStrategy.get_hyp_only e_annot in
let g_prop = WpStrategy.get_goal_only e_annot in
let bh_prop, bg_prop = WpStrategy.get_both_hyp_goals e_annot in
let h_assigns = WpStrategy.get_asgn_hyp e_annot in
let g_assigns = WpStrategy.get_asgn_goal e_annot in
(* get_cut is ignored : see get_wp_edge *)
let obj = collect_oblig wenv res e obj in
let is_loop_head =
match Cil2cfg.node_type (Cil2cfg.edge_src e) with
| Cil2cfg.Vloop (Some _, _) -> true
| _ -> false
in
let compute ~goal obj =
let local_add_goal obj g =
if goal then add_goal wenv obj g else obj
in
let obj = List.fold_left (local_add_goal) obj g_prop in
let obj = List.fold_left (add_hyp wenv) obj bh_prop in
let obj =
if goal then add_assigns_goal wenv obj g_assigns else obj
in
let obj = List.fold_left (local_add_goal) obj bg_prop in
let obj = List.fold_left (add_hyp wenv) obj h_prop in
obj
in
let obj = match res.mode with
| Pass1 -> compute ~goal:true obj
| Pass2 -> compute ~goal:false obj
in
let obj = do_labels wenv e obj in
let obj =
if is_loop_head then obj (* assigns used in [wp_loop] *)
else use_assigns wenv res obj h_assigns
in
debug "[set_wp_edge] %a@." Cil2cfg.pp_edge e;
debug " = @[<hov2> %a@]@." W.pretty obj;
Format.print_flush ();
HE.replace res.tbl e (Some obj);
find res e (* this should give back obj, but also do more things *)
end
end (* module R *)
let use_loop_assigns strategy wenv e obj =
let e_annot = WpStrategy.get_annots strategy e in
let h_assigns = WpStrategy.get_asgn_hyp e_annot in
let label, obj = add_assigns_hyp wenv obj h_assigns in
match label with Some _ -> obj
| None -> assert false (* we should have assigns hyp for loops !*)
(** Compute the result for edge [e] which goes to the loop node [nloop].
* So [e] can be either a back_edge or a loop entry edge.
* Be very careful not to make an infinite loop by calling [get_loop_head]...
* *)
let wp_loop ((_, cfg, strategy, _, wenv)) nloop e get_loop_head =
let loop_with_quantif () =
if Cil2cfg.is_back_edge e then
(* Be careful not to use get_only_succ here (infinite loop) *)
(debug "[wp_loop] cut at back edge";
W.empty)
else (* edge going into the loop from outside : quantify *)
begin
debug "[wp_loop] quantify";
let obj = get_loop_head nloop in
let head = match Cil2cfg.succ_e cfg nloop with
| [h] -> h
| _ -> assert false (* already detected in [get_loop_head] *)
in use_loop_assigns strategy wenv head obj
end
in
(*
if WpStrategy.new_loop_computation strategy
&& R.is_pass1 res
&& loop_with_cut cfg strategy nloop
then
loop_with_cut_pass1 ()
else (* old mode or no inv or pass2 *)
*)
match Cil2cfg.node_type nloop with
| Cil2cfg.Vloop (Some true, _) -> (* natural loop (has back edges) *)
loop_with_quantif ()
| _ -> (* TODO : print info about the loop *)
Wp_error.unsupported
"non-natural loop without invariant property."
type callenv = {
pre_annots : WpStrategy.t_annots ;
post_annots : WpStrategy.t_annots ;
exit_annots : WpStrategy.t_annots ;
}
let callenv cfg strategy v =
let eb = match Cil2cfg.pred_e cfg v with e::_ -> e | _ -> assert false in
let en, ee = Cil2cfg.get_call_out_edges cfg v in
{
pre_annots = WpStrategy.get_annots strategy eb ;
post_annots = WpStrategy.get_annots strategy en ;
exit_annots = WpStrategy.get_annots strategy ee ;
}
let wp_call_any wenv cenv ~p_post ~p_exit =
let obj = W.merge wenv p_post p_exit in
let call_asgn = WpStrategy.get_call_asgn cenv.post_annots None in
let lab, obj = add_assigns_hyp wenv obj call_asgn in
match lab with
| Some _ -> obj
| None -> assert false
let add_call_post wenv annots kf obj =
List.fold_left (add_goal wenv) obj (WpStrategy.get_call_post annots kf)
let wp_call_kf wenv cenv stmt lval kf args precond ~p_post ~p_exit =
let call_asgn = WpStrategy.get_call_asgn cenv.post_annots (Some kf) in
let assigns = match call_asgn with
| WpPropId.AssignsLocations (_, asgn_body) ->
asgn_body.WpPropId.a_assigns
| WpPropId.AssignsAny _ -> WritesAny
| WpPropId.NoAssignsInfo -> assert false (* see above *)
in
let p_post = add_call_post wenv cenv.post_annots kf p_post in
let p_exit = add_call_post wenv cenv.exit_annots kf p_exit in
let pre_hyp, pre_goals = WpStrategy.get_call_pre cenv.pre_annots kf in
let obj = W.call wenv stmt lval kf args
~pre:(pre_hyp)
~post:((WpStrategy.get_call_hyp cenv.post_annots kf))
~pexit:((WpStrategy.get_call_hyp cenv.exit_annots kf))
~assigns ~p_post ~p_exit in
if precond
then W.call_goal_precond wenv stmt kf args ~pre:(pre_goals) obj
else obj
let wp_calls ((_, cfg, strategy, _, wenv)) res v stmt
lval call args p_post p_exit
=
debug "[wp_calls] %a@." Cil2cfg.pp_call_type call;
let cenv = callenv cfg strategy v in
match call with
| Cil2cfg.Static kf ->
let precond =
WpStrategy.is_default_behavior strategy && R.is_pass1 res
in
wp_call_kf wenv cenv stmt lval kf args precond ~p_post ~p_exit
| Cil2cfg.Dynamic fct ->
let bhv = WpStrategy.behavior_name_of_strategy strategy in
match Dyncall.get ?bhv stmt with
| None -> wp_call_any wenv cenv ~p_post ~p_exit
| Some (prop,calls) ->
let precond = R.is_pass1 res in
let do_call kf =
let wp = wp_call_kf wenv cenv stmt lval
kf args precond ~p_post ~p_exit in
kf , wp in
let pid = WpPropId.mk_property prop in
W.call_dynamic wenv stmt pid fct (List.map do_call calls)
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
let wp_stmt wenv s obj = match s.skind with
| Return (r, _) -> W.return wenv s r obj
| Instr i ->
begin match i with
| Local_init (vi, AssignInit i, _) -> W.init wenv vi (Some i) obj
| Local_init (_, ConsInit _, _) -> assert false
| (Set (lv, e, _)) -> W.assign wenv s lv e obj
| (Asm _) ->
let asm = WpPropId.mk_asm_assigns_desc s in
W.use_assigns wenv asm.WpPropId.a_stmt None asm obj
| (Call _) -> assert false
| Skip _ | Code_annot _ -> obj
end
| Break _ | Continue _ | Goto _ -> obj
| Loop _-> (* this is not a real loop (exit before looping)
just ignore it ! *) obj
| If _ -> assert false
| Switch _-> assert false
| Block _-> assert false
| UnspecifiedSequence _-> assert false
| TryExcept _ | TryFinally _ | Throw _ | TryCatch _ -> assert false
let wp_scope wenv vars scope obj =
debug "[wp_scope] %s : %a@."
(match scope with
| Mcfg.SC_Global -> "global"
| Mcfg.SC_Block_in -> "block in"
| Mcfg.SC_Block_out -> "block out"
| Mcfg.SC_Frame_in -> "frame in"
| Mcfg.SC_Frame_out -> "frame out" )
(Pretty_utils.pp_list ~sep:", " Printer.pp_varinfo) vars;
match scope with
| Mcfg.(SC_Block_in | SC_Block_out) when vars = [] -> obj
| _ -> W.scope wenv vars scope obj
(** @return the WP stored for edge [e]. Compute it if it is not already
* there and store it. Also handle the Acut annotations. *)
let rec get_wp_edge ((_kf, cfg, strategy, res, wenv) as env) e =
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
let v = Cil2cfg.edge_dst e in
debug "[get_wp_edge] get wp before %a@." Cil2cfg.pp_node v;
try
let res = R.find res e in
debug "[get_wp_edge] %a already computed@." Cil2cfg.pp_node v;
res
with Not_found ->
(* Notice that other hyp and goal are handled in R.set as usual *)
let cutp =
if R.is_pass1 res
then WpStrategy.get_cut (WpStrategy.get_annots strategy e)
else []
in
match cutp with
| [] ->
let wp = compute_wp_edge env e in
R.set strategy wenv res e wp
| cutp ->
debug "[get_wp_edge] cut at node %a@." Cil2cfg.pp_node v;
let add_cut_goal (g,p) acc =
if g then add_goal wenv acc p else acc
in
let edge_annot = List.fold_right add_cut_goal cutp W.empty in
(* put cut goal properties as goals in e if any, else true *)
let edge_annot = R.set strategy wenv res e edge_annot in
let wp = compute_wp_edge env e in
let add_cut_hyp (_,p) acc = add_hyp wenv acc p in
let oblig = List.fold_right add_cut_hyp cutp wp in
(* TODO : we could add hyp to the oblig if we have some in strategy *)
let oblig = W.loop_step oblig in
if test_edge_loop_ok cfg None e
then R.add_memo res e oblig
else R.add_oblig res Clabels.pre (W.close wenv oblig);
edge_annot
and get_only_succ env cfg v = match Cil2cfg.succ_e cfg v with
| [e'] -> get_wp_edge env e'
| ls ->
Wp_parameters.debug "CFG node %a has %d successors instead of 1@."
Cil2cfg.pp_node v (List.length ls);
Wp_error.unsupported "strange loop(s)."
and compute_wp_edge ((kf, cfg, _annots, res, wenv) as env) e =
let v = Cil2cfg.edge_dst e in
debug "[compute_edge] before %a go...@." Cil2cfg.pp_node v;
let old_loc = Cil.CurrentLoc.get () in
let () = match Cil2cfg.node_stmt_opt v with
| Some s -> Cil.CurrentLoc.set (Stmt.loc s)
| None -> ()
in
let formals = Kernel_function.get_formals kf in
let res = match Cil2cfg.node_type v with
| Cil2cfg.Vstart ->
Wp_parameters.debug "No CFG edge can lead to Vstart";
Wp_error.unsupported "strange CFGs."
| Cil2cfg.VfctIn ->
let obj = get_only_succ env cfg v in
let obj = wp_scope wenv [] Mcfg.SC_Global obj in
obj
| Cil2cfg.VblkIn (Cil2cfg.Bfct, b) ->
let obj = get_only_succ env cfg v in
let obj = wp_scope wenv b.blocals Mcfg.SC_Block_in obj in
let obj = wp_scope wenv formals Mcfg.SC_Frame_in obj in
obj
| Cil2cfg.VblkOut (Cil2cfg.Bfct, b) ->
let obj = get_only_succ env cfg v in
let obj = wp_scope wenv b.blocals Mcfg.SC_Block_out obj in
obj
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
| Cil2cfg.Vstmt s ->
let obj = get_only_succ env cfg v in
wp_stmt wenv s obj
| Cil2cfg.Vcall (stmt, lval, fct, args) ->
let en, ee = Cil2cfg.get_call_out_edges cfg v in
let objn = get_wp_edge env en in
let obje = get_wp_edge env ee in
wp_calls env res v stmt lval fct args objn obje
| Cil2cfg.Vtest (true, s, c) ->
let et, ef = Cil2cfg.get_test_edges cfg v in
let t_obj = get_wp_edge env et in
let f_obj = get_wp_edge env ef in
W.test wenv s c t_obj f_obj
| Cil2cfg.Vtest (false, _, _) ->
get_only_succ env cfg v
| Cil2cfg.Vswitch (s, e) ->
let cases, def_edge = Cil2cfg.get_switch_edges cfg v in
let cases_obj = List.map (fun (c,e) -> c, get_wp_edge env e) cases in
let def_obj = get_wp_edge env def_edge in
W.switch wenv s e cases_obj def_obj
| Cil2cfg.Vloop _ | Cil2cfg.Vloop2 _ ->
let get_loop_head = fun n -> get_only_succ env cfg n in
wp_loop env v e get_loop_head
| Cil2cfg.VfctOut
| Cil2cfg.Vexit ->
let obj = get_only_succ env cfg v (* exitpost / postcondition *) in
| Cil2cfg.Vend ->
W.empty
(* LC : unused entry point...
let obj = W.empty in
wp_scope wenv formals Mcfg.SC_Function_after_POST obj
*)
in
let Cil2cfg.{ b_closed ; b_opened } = Cil2cfg.block_scope_for_edge cfg e in
let do_block sc res b = wp_scope wenv b.blocals sc res in
let res = List.fold_left (do_block Mcfg.SC_Block_in) res b_opened in
let res = List.fold_left (do_block Mcfg.SC_Block_out) res b_closed in
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
debug "[compute_edge] before %a done@." Cil2cfg.pp_node v;
Cil.CurrentLoc.set old_loc;
res
let compute_global_init wenv filter obj =
Globals.Vars.fold_in_file_order
(fun var initinfo obj ->
if var.vstorage = Extern then obj else
let do_init = match filter with
| `All -> true
| `InitConst -> WpStrategy.isGlobalInitConst var
in if not do_init then obj
else
let old_loc = Cil.CurrentLoc.get () in
Cil.CurrentLoc.set var.vdecl ;
let obj = W.init wenv var initinfo.init obj in
Cil.CurrentLoc.set old_loc ; obj
) obj
let process_global_const wenv obj =
Globals.Vars.fold_in_file_order
(fun var _initinfo obj ->
if WpStrategy.isGlobalInitConst var
then W.const wenv var obj
else obj
) obj
(* WP of global initializations. *)
let process_global_init wenv kf obj =
if WpStrategy.is_main_init kf then
begin
let obj = W.label wenv None Clabels.init obj in
compute_global_init wenv `All obj
end
else if W.has_init wenv then
begin
let obj =
if WpStrategy.isInitConst ()
then process_global_const wenv obj else obj in
let obj = W.use_assigns wenv None None WpPropId.mk_init_assigns obj in
let obj = W.label wenv None Clabels.init obj in
compute_global_init wenv `All obj
end
else
if WpStrategy.isInitConst ()
then compute_global_init wenv `InitConst obj
else obj
let get_weakest_precondition cfg ((kf, _g, strategy, res, wenv) as env) =
debug "[wp-cfg] start Pass1";
Cil2cfg.iter_edges (fun e -> ignore (get_wp_edge env e)) cfg ;
debug "[wp-cfg] end of Pass1";
R.change_mode_if_needed res;
(* Notice that [get_wp_edge] will start Pass2 if needed,
* but if not, it will only fetch Pass1 result. *)
let e_start = Cil2cfg.start_edge cfg in
let obj = get_wp_edge env e_start in
let obj = process_global_init wenv kf obj in
let obj = match WpStrategy.strategy_kind strategy with
| WpStrategy.SKannots -> obj
| WpStrategy.SKfroms info ->
let pre = info.WpStrategy.get_pre () in
let pre = WpStrategy.get_hyp_only pre in
W.build_prop_of_from wenv pre obj
in
debug "before close: %a@." W.pretty obj;
W.close wenv obj
let compute cfg strategy =
debug "[wp-cfg] start computing with the strategy for %a"
WpStrategy.pp_info_of_strategy strategy;
if WpStrategy.strategy_has_prop_goal strategy
|| WpStrategy.strategy_has_asgn_goal strategy then
try
let kf = Cil2cfg.cfg_kf cfg in
if Cil2cfg.strange_loops cfg <> [] then
Wp_error.unsupported "non natural loop(s)" ;
let lvars = match WpStrategy.strategy_kind strategy with
| WpStrategy.SKfroms info -> info.WpStrategy.more_vars
| _ -> [] in
let wenv = W.new_env ~lvars kf in
let res = R.empty cfg in
let env = (kf, cfg, strategy, res, wenv) in
List.iter
(fun (pid,thm) -> W.add_axiom pid thm)
(WpStrategy.global_axioms strategy) ;
let goal = get_weakest_precondition cfg env in
debug "[get_weakest_precondition] %a@." W.pretty goal;
let pp_cfg_edges_annot res fmt e =
try W.pretty fmt (R.find res e)
with Not_found -> Format.fprintf fmt "<released>"
in
let annot_cfg = pp_cfg_edges_annot res in
debug "[wp-cfg] computing done.";
[goal] , annot_cfg
with Wp_error.Error (_, msg) ->
Wp_parameters.warning "@[calculus failed on strategy@ @[for %a@]@ \
because@ %s (abort)@]"
WpStrategy.pp_info_of_strategy strategy
msg;
let annot_cfg fmt _e = Format.fprintf fmt "" in
[], annot_cfg
else
begin
debug "[wp-cfg] no goal in this strategy : ignore.";
let annot_cfg fmt _e = Format.fprintf fmt "" in
[], annot_cfg
end
end