1
|
open Format
|
2
|
open LustreSpec
|
3
|
open Machine_code
|
4
|
open C_backend_common
|
5
|
open Utils
|
6
|
|
7
|
|
8
|
module type SPECARG =
|
9
|
sig
|
10
|
val prog : program
|
11
|
val machines : machine_t list
|
12
|
end
|
13
|
|
14
|
(**************************************************************************)
|
15
|
(* SPEC DATATYPES *)
|
16
|
(**************************************************************************)
|
17
|
|
18
|
|
19
|
type eexpr_minimachine_t = {
|
20
|
muid: tag;
|
21
|
mquantifiers: (quantifier_type * var_decl list) list;
|
22
|
mmmemory: var_decl list;
|
23
|
mmcalls: (ident * static_call) list; (* map from stateful/stateless instance to node,
|
24
|
no internals *)
|
25
|
mminstances: (ident * static_call) list; (* sub-map of mcalls, from stateful instance to node *)
|
26
|
mminit: instr_t list;
|
27
|
mmstep_logic: step_t;
|
28
|
mmstep_effects: step_t;
|
29
|
(* mmlogic: step_t; *)
|
30
|
}
|
31
|
|
32
|
type spec_machine_t = {
|
33
|
m_requires: eexpr_minimachine_t list;
|
34
|
m_ensures: eexpr_minimachine_t list;
|
35
|
m_behaviors: (string * eexpr_minimachine_t list * eexpr_minimachine_t list) list;
|
36
|
}
|
37
|
|
38
|
|
39
|
let machine_from_minimachine_effects m mm =
|
40
|
{
|
41
|
mname = m.mname;
|
42
|
mmemory = mm.mmmemory;
|
43
|
mcalls = mm.mmcalls;
|
44
|
minstances = mm.mminstances;
|
45
|
minit = []; (* ??? *)
|
46
|
mstatic = [] ; (* ??? *)
|
47
|
mstep = mm.mmstep_effects;
|
48
|
mspec = None;
|
49
|
mannot = [];
|
50
|
}
|
51
|
|
52
|
let machine_from_minimachine_logics m mm =
|
53
|
{
|
54
|
mname = m.mname;
|
55
|
mmemory = mm.mmmemory;
|
56
|
mcalls = mm.mmcalls;
|
57
|
minstances = mm.mminstances;
|
58
|
minit = []; (* ??? *)
|
59
|
mstatic = [] ; (* ??? *)
|
60
|
mstep = mm.mmstep_logic;
|
61
|
mspec = None;
|
62
|
mannot = [];
|
63
|
}
|
64
|
|
65
|
(**************************************************************************)
|
66
|
(* DATATYPES PRINTERS *)
|
67
|
(**************************************************************************)
|
68
|
|
69
|
|
70
|
|
71
|
let pp_acsl_type var fmt t =
|
72
|
let rec aux t pp_suffix =
|
73
|
match (Types.repr t).Types.tdesc with
|
74
|
| Types.Tclock t' -> aux t' pp_suffix
|
75
|
| Types.Tbool -> fprintf fmt "boolean %s%a" var pp_suffix ()
|
76
|
| Types.Treal -> fprintf fmt "real %s%a" var pp_suffix ()
|
77
|
| Types.Tint -> fprintf fmt "integer %s%a" var pp_suffix ()
|
78
|
| Types.Tarray (d, t') ->
|
79
|
let pp_suffix' fmt () = fprintf fmt "%a[%a]" pp_suffix () pp_c_dimension d in
|
80
|
aux t' pp_suffix'
|
81
|
(* | Types.Tstatic (_, t') -> fprintf fmt "const "; aux t' pp_suffix *)
|
82
|
(* | Types.Tconst ty -> fprintf fmt "%s %s" ty var *)
|
83
|
(* | Types.Tarrow (_, _) -> fprintf fmt "void (\*%s)()" var *)
|
84
|
| _ ->
|
85
|
(eprintf "internal error: pp_acsl_type %a@." Types.print_ty t; assert false)
|
86
|
in aux t (fun fmt () -> ())
|
87
|
|
88
|
let pp_acsl_var_decl fmt id =
|
89
|
pp_acsl_type id.var_id fmt id.var_type
|
90
|
|
91
|
|
92
|
let pp_quantifiers fmt (q, vars) =
|
93
|
let pp_var fmt id =
|
94
|
fprintf fmt "%s: %a" id.var_id Types.print_ty id.var_type
|
95
|
in
|
96
|
match q with
|
97
|
| Forall -> fprintf fmt "forall %a" (fprintf_list ~sep:"; " pp_var) vars
|
98
|
| Exists -> fprintf fmt "exists %a" (fprintf_list ~sep:"; " pp_var) vars
|
99
|
|
100
|
(*
|
101
|
let pp_eexpr fmt ee =
|
102
|
Format.fprintf fmt
|
103
|
"%a%t @[<v 2>mem : %a@;instances: %a@;init : %a@;logic step :@; @[<v 2>%a@]@;effects step :@; @[<v 2>%a@]@;@]@;"
|
104
|
(Utils.fprintf_list ~sep:"; " pp_quantifiers) ee.mquantifiers
|
105
|
(fun fmt -> match ee.mquantifiers with [] -> () | _ -> Format.fprintf fmt ";")
|
106
|
(Utils.fprintf_list ~sep:", " Printers.pp_var) ee.mmmemory
|
107
|
(Utils.fprintf_list ~sep:", " (fun fmt (o1, o2) -> Format.fprintf fmt "(%s, %a)" o1 pp_static_call o2)) ee.mminstances
|
108
|
(Utils.fprintf_list ~sep:"@ " pp_instr) ee.mminit
|
109
|
pp_step ee.mmstep_logic
|
110
|
pp_step ee.mmstep_effects
|
111
|
|
112
|
|
113
|
let pp_spec fmt spec =
|
114
|
Format.fprintf fmt "@[<hov 2>(*@@ ";
|
115
|
Utils.fprintf_list ~sep:"@;@@ " (fun fmt r -> Format.fprintf fmt "requires %a;" pp_eexpr r) fmt spec.m_requires;
|
116
|
Utils.fprintf_list ~sep:"@;@@ " (fun fmt r -> Format.fprintf fmt "ensures %a; " pp_eexpr r) fmt spec.m_ensures;
|
117
|
Utils.fprintf_list ~sep:"@;" (fun fmt (name, assumes, ensures) ->
|
118
|
Format.fprintf fmt "behavior %s:@[@ %a@ %a@]"
|
119
|
name
|
120
|
(Utils.fprintf_list ~sep:"@ " (fun fmt r -> Format.fprintf fmt "assumes %a;" pp_eexpr r)) assumes
|
121
|
(Utils.fprintf_list ~sep:"@ " (fun fmt r -> Format.fprintf fmt "ensures %a;" pp_eexpr r)) ensures
|
122
|
) fmt spec.m_behaviors;
|
123
|
Format.fprintf fmt "@]*)";
|
124
|
()
|
125
|
*)
|
126
|
|
127
|
|
128
|
(**************************************************************************)
|
129
|
(* CONVERTER *)
|
130
|
(**************************************************************************)
|
131
|
(* The eexpr shoud have been normalized, ie. its eexpr_normalized field updated
|
132
|
We add a new boolean output var and bound it to the main expression
|
133
|
characterizing the eexpr.
|
134
|
*)
|
135
|
|
136
|
let save_var v =
|
137
|
{ v with var_id = v.var_id^"_save" }
|
138
|
|
139
|
let rec postTransformLogicValue x = match x with
|
140
|
| StateVar v -> StateVar (save_var v)
|
141
|
| Fun (i, vl) -> Fun (i, List.map postTransformLogicValue vl)
|
142
|
| Array vl -> Array (List.map postTransformLogicValue vl)
|
143
|
| Access (x, y) -> Access (postTransformLogicValue x, postTransformLogicValue y)
|
144
|
| Power (x, y) -> Power (postTransformLogicValue x, postTransformLogicValue y)
|
145
|
| x -> x
|
146
|
|
147
|
let rec postTransformLogic x = List.map (function
|
148
|
| MLocalAssign (x, y) -> MLocalAssign (x, postTransformLogicValue y)
|
149
|
| MStateAssign (x, y) -> MStateAssign (x, postTransformLogicValue y)
|
150
|
| MStep (x, y, z) -> MStep (x, y, List.map postTransformLogicValue z)
|
151
|
| MBranch (x, y) -> MBranch (postTransformLogicValue x, List.map (function (x, y)-> (x, postTransformLogic y)) y)
|
152
|
| MReset x -> MReset x
|
153
|
) x
|
154
|
|
155
|
let rec postTransformSide = function
|
156
|
| MStateAssign (i, v)::q -> (MStateAssign (save_var i, StateVar i))::(MStateAssign (i, v))::(postTransformSide q)
|
157
|
| t::q -> t::(postTransformSide q)
|
158
|
| [] -> []
|
159
|
|
160
|
let rec postTranformMemory = function
|
161
|
| t::q -> t::(save_var t)::(postTranformMemory q)
|
162
|
| [] -> []
|
163
|
|
164
|
let translate_eexpr nd eexpr =
|
165
|
(* Format.eprintf "Translating eexpr: %a" Printers.pp_eexpr eexpr; *)
|
166
|
let output_var, eqs, locals =
|
167
|
match eexpr.eexpr_normalized with None -> assert false | Some x -> x
|
168
|
in
|
169
|
(* both inputs and outputs are considered here as inputs for the specification *)
|
170
|
let inputs = nd.node_inputs @ nd.node_outputs in
|
171
|
let inputs_quantifiers = inputs @ (List.flatten (List.map snd eexpr.eexpr_quantifiers)) in
|
172
|
|
173
|
let eexpr_local_vars = output_var :: locals @ (List.flatten (List.map snd eexpr.eexpr_quantifiers)) in
|
174
|
let visible_vars = eexpr_local_vars @ inputs in
|
175
|
|
176
|
let sort_eqs inputs eqs =
|
177
|
let eqs_logic, new_locals_logic, sch_logic = Scheduling.schedule_eqs inputs visible_vars eqs in
|
178
|
(* Format.eprintf "sch: [%a]@;new locals: [%a]@;locals:[%a]@.eexpr local vars: [%a]@.@?" *)
|
179
|
(* (Utils.fprintf_list ~sep:", " Format.pp_print_string) sch_logic *)
|
180
|
(* (Utils.fprintf_list ~sep:", " Printers.pp_var) new_locals_logic *)
|
181
|
(* (Utils.fprintf_list ~sep:", " Printers.pp_var) locals *)
|
182
|
(* (Utils.fprintf_list ~sep:", " Printers.pp_var) eexpr_local_vars; *)
|
183
|
let locals = eexpr_local_vars @ new_locals_logic @ (List.flatten (List.map snd eexpr.eexpr_quantifiers)) in
|
184
|
let sorted_eqs_rev_logic, remainder_logic =
|
185
|
List.fold_left
|
186
|
(fun (accu, eqs_remainder) v ->
|
187
|
if List.exists (fun eq -> List.mem v eq.eq_lhs) accu
|
188
|
then (* The variable was already handled by a previous selected eq.
|
189
|
Typically it is part of a tuple *)
|
190
|
((* Format.eprintf "Case 1 for variable %s@." v; *)
|
191
|
(accu, eqs_remainder)
|
192
|
)
|
193
|
else if List.exists (fun vdecl -> vdecl.var_id = v) locals || output_var.var_id = v
|
194
|
then ((* Select the eq associated to v. *)
|
195
|
(* Format.eprintf "Case 2 for variable %s@." v; *)
|
196
|
let eq_v, remainder = find_eq v eqs_remainder in
|
197
|
eq_v::accu, remainder
|
198
|
)
|
199
|
else ((* else it is a constant value, checked during typing phase *)
|
200
|
(* Format.eprintf "Case 3 for variable %s@." v; *)
|
201
|
accu, eqs_remainder
|
202
|
)
|
203
|
)
|
204
|
([], eqs_logic)
|
205
|
sch_logic
|
206
|
in
|
207
|
if List.length remainder_logic > 0 then (
|
208
|
Format.eprintf "Spec equations not used are@.%a@.Full equation set is:@.%a@.@?"
|
209
|
Printers.pp_node_eqs remainder_logic
|
210
|
Printers.pp_node_eqs eqs_logic;
|
211
|
assert false );
|
212
|
List.rev sorted_eqs_rev_logic, locals
|
213
|
in
|
214
|
|
215
|
|
216
|
(* Generating logic definition instructions *)
|
217
|
let sorted_eqs, locals = sort_eqs inputs_quantifiers eqs in
|
218
|
|
219
|
let init_args =
|
220
|
VDSet.empty,
|
221
|
[],
|
222
|
Utils.IMap.empty,
|
223
|
List.fold_right (fun l -> VDSet.add l) locals VDSet.empty,
|
224
|
[]
|
225
|
in
|
226
|
|
227
|
let m, init, j, locals, s_logic =
|
228
|
translate_eqs
|
229
|
nd
|
230
|
true (* keep_ite *)
|
231
|
eexpr_local_vars
|
232
|
init_args
|
233
|
(sorted_eqs)
|
234
|
in
|
235
|
|
236
|
(* We remove side effects *)
|
237
|
let s_logic = List.filter (fun i -> match i with MStateAssign _ -> false | _ -> true) s_logic in
|
238
|
(* and put the output var as the last instr *)
|
239
|
let s_logic =
|
240
|
let output_assign, others =
|
241
|
List.partition
|
242
|
(fun i -> match i with | MStep([v], _, _) | MLocalAssign(v,_) -> v.var_id = output_var.var_id | _ -> false)
|
243
|
s_logic
|
244
|
in
|
245
|
(* Format.eprintf "output instr: @.%a@.others:@.%a@." *)
|
246
|
(* (Utils.fprintf_list ~sep:"@." pp_instr) output_assign *)
|
247
|
(* (Utils.fprintf_list ~sep:"@." pp_instr) others; *)
|
248
|
others @ output_assign
|
249
|
in
|
250
|
let _,_,_,_, s_side_effects =
|
251
|
translate_eqs nd true (*TODO: explose ite *) eexpr_local_vars init_args (sorted_eqs)
|
252
|
in
|
253
|
let rec filter_side_effects instrs =
|
254
|
List.fold_right (
|
255
|
fun i accu ->
|
256
|
match i with
|
257
|
| MStep([v], _, _)
|
258
|
| MLocalAssign(v,_) ->
|
259
|
if v.var_id <> output_var.var_id then
|
260
|
i::accu
|
261
|
else
|
262
|
accu
|
263
|
| MBranch (g, cases) ->
|
264
|
let filtered_cases =
|
265
|
List.map (fun (l, l_instrs) -> l, filter_side_effects l_instrs) cases in
|
266
|
MBranch(g, filtered_cases) :: accu
|
267
|
| _ -> i::accu
|
268
|
|
269
|
) instrs []
|
270
|
in
|
271
|
|
272
|
let s_side_effects = filter_side_effects s_side_effects in
|
273
|
|
274
|
{
|
275
|
muid = eexpr.eexpr_tag;
|
276
|
mquantifiers = eexpr.eexpr_quantifiers;
|
277
|
mmmemory = postTranformMemory (VDSet.elements m);
|
278
|
mmcalls = []; (* no calls *)
|
279
|
mminstances = []; (* no calls *)
|
280
|
mminit = init;
|
281
|
mmstep_logic = {
|
282
|
step_inputs = inputs;
|
283
|
step_outputs = [output_var];
|
284
|
step_locals = VDSet.elements (VDSet.remove output_var (VDSet.diff locals m));
|
285
|
step_checks = [] (* Not handled yet *);
|
286
|
step_instrs = (
|
287
|
(* special treatment depending on the active backend. For horn backend,
|
288
|
common branches are not merged while they are in C or Java
|
289
|
backends. *)
|
290
|
match !Options.output with
|
291
|
| "horn" -> s_logic
|
292
|
| "C" | "java" | _ -> (*TODO: check that this is correct to remove : join_guards_list*) (postTransformLogic s_logic)
|
293
|
);
|
294
|
step_asserts = [];
|
295
|
};
|
296
|
mmstep_effects = {
|
297
|
step_inputs = inputs; (* contains nd.node_inputs + nd.node_outputs *)
|
298
|
step_outputs = nd.node_outputs; (* used by the printer to print correctly the names *)
|
299
|
step_locals = VDSet.elements (VDSet.remove output_var (VDSet.diff locals m));
|
300
|
step_checks = [] (* Not handled yet *);
|
301
|
step_instrs = (
|
302
|
(* special treatment depending on the active backend. For horn backend,
|
303
|
common branches are not merged while they are in C or Java
|
304
|
backends. *)
|
305
|
match !Options.output with
|
306
|
| "horn" -> s_side_effects
|
307
|
| "C" | "java" | _ -> (*TODO: check that this is correct to remove : join_guards_list*) (postTransformSide s_side_effects)
|
308
|
);
|
309
|
step_asserts = [];
|
310
|
}
|
311
|
}
|
312
|
|
313
|
let translate_spec nd spec =
|
314
|
(* Each eexpr of the spec is expressed as a minimachine:
|
315
|
instrs (to update memory and set local vars)
|
316
|
+ expr (to compute the output
|
317
|
+ memory defs
|
318
|
+ instances (for callee nodes)
|
319
|
*)
|
320
|
let transl = List.map (translate_eexpr nd) in
|
321
|
{
|
322
|
m_requires = transl spec.requires;
|
323
|
m_ensures = transl spec.ensures;
|
324
|
m_behaviors =
|
325
|
List.map
|
326
|
(fun (beh_id, req, ens, _) -> beh_id, transl req, transl ens)
|
327
|
spec.behaviors
|
328
|
}
|
329
|
|
330
|
|
331
|
(*
|
332
|
|
333
|
mspec = Utils.option_map (translate_spec nd) nd.node_spec;
|
334
|
mannot = List.flatten (
|
335
|
List.map (fun ann -> List.map (fun (kwd, eexpr) -> kwd, (translate_eexpr nd eexpr)) ann.annots) nd.node_annot);
|
336
|
*)
|
337
|
|
338
|
|
339
|
(**************************************************************************)
|
340
|
(* HEADER *)
|
341
|
(**************************************************************************)
|
342
|
|
343
|
(* Print basic library function as ACSL functions *)
|
344
|
let basic_lib_pp_acsl i get_val_type pp_val fmt vl =
|
345
|
match i, vl with
|
346
|
| "ite", [v1; v2; v3] -> Format.fprintf fmt "(%a?(%a):(%a))" pp_val v1 pp_val v2 pp_val v3
|
347
|
| "uminus", [v] -> Format.fprintf fmt "(- %a)" pp_val v
|
348
|
| "not", [v] -> Format.fprintf fmt "(!%a)" pp_val v
|
349
|
| "impl", [v1; v2] -> Format.fprintf fmt "((!%a) || %a)" pp_val v1 pp_val v2
|
350
|
| "=", [v1; v2] -> Format.fprintf fmt "(%a == %a)" pp_val v1 pp_val v2
|
351
|
| "mod", [v1; v2] -> Format.fprintf fmt "(%a %% %a)" pp_val v1 pp_val v2
|
352
|
| "equi", [v1; v2] -> Format.fprintf fmt "(!%a == !%a)" pp_val v1 pp_val v2
|
353
|
| "xor", [v1; v2] -> Format.fprintf fmt "(%a ^ %a)" pp_val v1 pp_val v2
|
354
|
| _, [v1; v2] -> Format.fprintf fmt "(%a %s %a)" pp_val v1 i pp_val v2
|
355
|
| _ -> raise (Failure ("No ACSL printer for function " ^ i))
|
356
|
|
357
|
let pp_acsl_tag fmt t =
|
358
|
pp_print_string fmt (if t = Corelang.tag_true then "\\true" else if t = Corelang.tag_false then "\\false" else t)
|
359
|
let rec pp_acsl_const fmt c =
|
360
|
match c with
|
361
|
| Const_int i -> pp_print_int fmt i
|
362
|
| Const_real r -> pp_print_string fmt r
|
363
|
| Const_float r -> pp_print_float fmt r
|
364
|
| Const_tag t -> pp_acsl_tag fmt t
|
365
|
| Const_array ca -> fprintf fmt "{%a }" (Utils.fprintf_list ~sep:", " pp_c_const) ca
|
366
|
| Const_struct fl -> fprintf fmt "{%a }" (Utils.fprintf_list ~sep:", " (fun fmt (f, c) -> pp_c_const fmt c)) fl
|
367
|
| Const_string _ -> assert false (* string occurs in annotations not in C *)
|
368
|
|
369
|
let rec pp_acsl_val ?(is_lhs=false) self pp_var fmt v =
|
370
|
match v with
|
371
|
| Cst c -> pp_acsl_const fmt c
|
372
|
| Array _
|
373
|
| Access _ -> assert false (* no arrays *)
|
374
|
| Power (v, n) -> assert false
|
375
|
| LocalVar v -> pp_var fmt v
|
376
|
| StateVar v ->
|
377
|
(match (Types.repr v.var_type).Types.tdesc with
|
378
|
| Types.Tbool -> fprintf fmt "((%t)?\\true:\\false)"
|
379
|
| _ -> fprintf fmt "%t")
|
380
|
(if Types.is_array_type v.var_type
|
381
|
then assert false
|
382
|
else fun fmt -> fprintf fmt "%s%s_reg.%s" self
|
383
|
(if !Options.no_pointer then "." else "->") v.var_id)
|
384
|
| Fun (n, vl) -> Format.fprintf fmt "%a" (basic_lib_pp_acsl n Machine_code.get_val_type (pp_acsl_val self pp_var)) vl
|
385
|
|
386
|
|
387
|
let rec pp_eexpr_logic_instr m eem fmt instr =
|
388
|
let self = mk_self m in
|
389
|
let conv_m = machine_from_minimachine_logics m eem in
|
390
|
match instr with
|
391
|
| MReset i -> assert false (* should not happen, calls were inlined *)
|
392
|
| MLocalAssign (i,v) -> (
|
393
|
match conv_m.mstep.step_outputs with
|
394
|
| [o] -> if i = o then
|
395
|
fprintf fmt "%a;" (pp_acsl_val self (pp_c_var_read conv_m)) v
|
396
|
else
|
397
|
fprintf fmt "\\let %a = %a;"
|
398
|
(pp_c_var_read conv_m (* TODO no variable shold be treated as output *)) i
|
399
|
(pp_acsl_val self (pp_c_var_read conv_m)) v
|
400
|
|
401
|
| _ ->
|
402
|
assert false (* should not happen: only a single output *)
|
403
|
)
|
404
|
| MStateAssign (i,v) ->
|
405
|
assert false (* should not happen, no side effects in logical predicates *)
|
406
|
| MStep ([i0], i, vl) when Basic_library.is_internal_fun i ->
|
407
|
pp_eexpr_logic_instr m eem fmt (MLocalAssign (i0, Fun (i, vl)))
|
408
|
| MStep ([i0], "ite", vl) ->
|
409
|
pp_eexpr_logic_instr m eem fmt (MLocalAssign (i0, Fun ("ite", vl)))
|
410
|
| MStep (il, i, vl) ->
|
411
|
Format.eprintf "Illegal function call %s@.%a@.@?" i pp_instr instr;
|
412
|
assert false (* should not happen, calls were inlined *)
|
413
|
| MBranch (g,hl) -> assert false (* should not happen. Oder ??? TODO *)
|
414
|
|
415
|
|
416
|
let pp_logic_eexpr m fmt ee =
|
417
|
fprintf fmt "%a%t%a"
|
418
|
(Utils.fprintf_list ~sep:"@ " pp_quantifiers) ee.mquantifiers
|
419
|
(Utils.pp_final_char_if_non_empty " " ee.mquantifiers)
|
420
|
(Utils.fprintf_list ~sep:"@;"
|
421
|
(pp_eexpr_logic_instr m ee)) ee.mmstep_logic.step_instrs
|
422
|
|
423
|
|
424
|
let pp_acsl_spec_logics stateless m fmt spec =
|
425
|
let self = mk_self m in
|
426
|
let pp_expr_def fmt ee =
|
427
|
match ee.mmstep_logic.step_instrs with
|
428
|
| [MStep([o], i, vl)] -> () (* We do nothing, the expression is simple and
|
429
|
was introduced directly in the contract *)
|
430
|
| _ -> ((* We define a new ACSL predicate *)
|
431
|
fprintf fmt "/*@@ @[<v 3>predicate spec_%i(%a%t) =@;%a@]@;*/@."
|
432
|
ee.muid
|
433
|
(Utils.fprintf_list ~sep:", " pp_acsl_var_decl) ee.mmstep_logic.step_inputs
|
434
|
(fun fmt -> if stateless then fprintf fmt "" else fprintf fmt "%t%a %s%s"
|
435
|
(Utils.pp_final_char_if_non_empty ", " ee.mmstep_logic.step_inputs)
|
436
|
pp_machine_memtype_name m.mname.node_id
|
437
|
(if !Options.no_pointer then "" else "*")
|
438
|
self)
|
439
|
(pp_logic_eexpr m) ee
|
440
|
)
|
441
|
|
442
|
in
|
443
|
List.iter (pp_expr_def fmt)
|
444
|
(spec.m_requires@
|
445
|
spec.m_ensures@
|
446
|
(List.fold_left
|
447
|
(fun accu (_,assumes, ensures) -> assumes@ensures@accu)
|
448
|
[] spec.m_behaviors)
|
449
|
)
|
450
|
|
451
|
(* Access to the value of a variable:
|
452
|
- if it's not a scalar output, then its name is enough
|
453
|
- otherwise, dereference it (it has been declared as a pointer,
|
454
|
despite its scalar Lustre type)
|
455
|
- moreover, cast arrays variables into their original array type.
|
456
|
*)
|
457
|
let pp_acsl_var_read suffix outputs fmt id =
|
458
|
if Types.is_array_type id.var_type
|
459
|
then
|
460
|
assert false (* no array *)
|
461
|
else (
|
462
|
(if List.exists (fun o -> o.var_id = id.var_id) outputs (* id is output *)
|
463
|
then fprintf fmt "((*%s)%s" id.var_id suffix
|
464
|
else fprintf fmt "(%s%s" id.var_id suffix);
|
465
|
match (Types.repr id.var_type).Types.tdesc with
|
466
|
| Types.Tbool -> fprintf fmt "?\\true:\\false)"
|
467
|
| _ -> fprintf fmt ")"
|
468
|
)
|
469
|
|
470
|
|
471
|
let pp_requires_mem stateless machines fmt m =
|
472
|
let self = mk_self m in
|
473
|
List.iter (fun v -> fprintf fmt "requires \\valid(%s);@;" v.var_id) m.mstep.step_outputs;
|
474
|
let mems = Machine_code.get_mems m machines in
|
475
|
(if not !Options.no_pointer then
|
476
|
List.iter (fun prefix -> fprintf fmt "requires \\valid(%s%a);@;" self
|
477
|
(Utils.fprintf_list ~sep:"" (fun fmt s -> fprintf fmt "->%s" s)) prefix
|
478
|
)
|
479
|
(Machine_code.get_instances m machines)
|
480
|
);
|
481
|
if not stateless then fprintf fmt "requires \\valid(%s);@;" self;
|
482
|
if List.length mems + List.length m.mstep.step_outputs > 1 then
|
483
|
fprintf fmt "requires \\separated(@[<hov>%a%t%a@]);@;"
|
484
|
(Utils.fprintf_list ~sep:",@ "
|
485
|
(fun fmt v -> pp_print_string fmt v.var_id))
|
486
|
m.mstep.step_outputs
|
487
|
(Utils.pp_final_char_if_non_empty ",@ " mems)
|
488
|
(if !Options.no_pointer then
|
489
|
fun fmt x -> fprintf fmt "%s" (if stateless then "" else self) (*maybe we want to add more pointers here like arrays *)
|
490
|
else
|
491
|
Utils.fprintf_list ~sep:",@ " (fun fmt (prefix, var) -> fprintf fmt "&%s%a->_reg.%s"
|
492
|
self
|
493
|
(Utils.fprintf_list ~sep:"" (fun fmt s -> fprintf fmt "->%s" s)) prefix
|
494
|
var.var_id))
|
495
|
mems
|
496
|
|
497
|
let pp_assigns more stateless machines fmt m =
|
498
|
let self = mk_self m in
|
499
|
let mems = Machine_code.get_mems m machines in
|
500
|
fprintf fmt "assigns @[<hov>%a%a%t%a@];@;"
|
501
|
(Utils.fprintf_list ~sep:",@ " (fun fmt v -> fprintf fmt "*%s" v.var_id))
|
502
|
m.mstep.step_outputs
|
503
|
(if !Options.no_pointer then
|
504
|
fun fmt mems -> fprintf fmt "%s" (if stateless then "" else (", *"^self)) (*maybe we want to keep some memory like array *)
|
505
|
else Utils.fprintf_list ~sep:",@ "
|
506
|
(fun fmt (prefix, var) -> fprintf fmt "%t%s%a->_reg.%s"
|
507
|
(Utils.pp_final_char_if_non_empty ",@ " mems)
|
508
|
self
|
509
|
(Utils.fprintf_list ~sep:"" (fun fmt s -> fprintf fmt "->%s" s)) prefix
|
510
|
var.var_id)
|
511
|
)
|
512
|
mems
|
513
|
(Utils.pp_final_char_if_non_empty ",@ " more)
|
514
|
(Utils.fprintf_list ~sep:",@ " (fun fmt v -> v fmt))
|
515
|
more
|
516
|
|
517
|
|
518
|
let pp_acsl_mem_valid stateless pre post machines fmt m =
|
519
|
fprintf fmt "@[<v 2>/*@@ ";
|
520
|
pp_requires_mem stateless machines fmt m;
|
521
|
pre fmt;
|
522
|
post fmt;
|
523
|
pp_assigns [] stateless machines fmt m;
|
524
|
fprintf fmt "@]*/@.";
|
525
|
()
|
526
|
|
527
|
(* Seems to be a simplification of pp_assign (in c_backend_src *)
|
528
|
let pp_expr_rhs m pp_var fmt var_type value =
|
529
|
let self = mk_self m in
|
530
|
let depth = C_backend_src.expansion_depth value in
|
531
|
(*eprintf "pp_assign %a %a %d@." Types.print_ty var_type pp_val value depth;*)
|
532
|
let loop_vars = C_backend_src.mk_loop_variables m var_type depth in
|
533
|
let reordered_loop_vars = C_backend_src.reorder_loop_variables loop_vars in
|
534
|
let rec aux fmt vars =
|
535
|
match vars with
|
536
|
| [] ->
|
537
|
fprintf fmt "%a;"
|
538
|
(C_backend_src.pp_value_suffix self loop_vars pp_var) value
|
539
|
| _ -> assert false
|
540
|
in
|
541
|
begin
|
542
|
reset_loop_counter ();
|
543
|
(*reset_addr_counter ();*)
|
544
|
aux fmt reordered_loop_vars
|
545
|
end
|
546
|
|
547
|
let pp_acsl_spec_contracts stateless pre post machines m outputs fmt spec =
|
548
|
let self = mk_self m in
|
549
|
(* let is_output = fun oid -> List.exists (fun v -> v.var_id = oid) outputs in *)
|
550
|
let pp_eexpr_expr fmt ee =
|
551
|
(* If the eexpr contains a single definition we use it directly. Otherwise,
|
552
|
we rely on an external predicate *)
|
553
|
let conv_m = machine_from_minimachine_logics m ee in
|
554
|
match ee.mmstep_logic.step_instrs with
|
555
|
| [MStep([o], i, vl)] ->
|
556
|
fprintf fmt "%a %t"
|
557
|
(Utils.fprintf_list ~sep:" " pp_quantifiers) ee.mquantifiers
|
558
|
(fun fmt ->
|
559
|
let value = Fun (i, vl) in
|
560
|
pp_expr_rhs
|
561
|
conv_m
|
562
|
(pp_c_var_read conv_m)
|
563
|
fmt
|
564
|
o.var_type value
|
565
|
)
|
566
|
| _ -> fprintf fmt "spec_%i(%a%t);"
|
567
|
ee.muid
|
568
|
(Utils.fprintf_list ~sep:", " (pp_acsl_var_read "" outputs)) ee.mmstep_logic.step_inputs
|
569
|
(fun fmt -> if stateless then fprintf fmt "" else fprintf fmt ", %s%s" (if !Options.no_pointer then "*" else "") self)
|
570
|
in
|
571
|
fprintf fmt "@[<v 2>/*@@ ";
|
572
|
(* Valid pointers *)
|
573
|
pp_requires_mem stateless machines fmt m;
|
574
|
|
575
|
(* Spec requires *)
|
576
|
Utils.fprintf_list ~sep:""
|
577
|
(fun fmt r -> fprintf fmt "requires %a@ " pp_eexpr_expr r)
|
578
|
fmt
|
579
|
spec.m_requires;
|
580
|
pre fmt;
|
581
|
Utils.fprintf_list ~sep:""
|
582
|
(fun fmt r -> fprintf fmt "ensures %a@ " pp_eexpr_expr r)
|
583
|
fmt
|
584
|
spec.m_ensures;
|
585
|
post fmt;
|
586
|
fprintf fmt "@ ";
|
587
|
(* TODO assigns + separated *)
|
588
|
(* fprintf fmt "assigns *self%t%a;@ " *)
|
589
|
(* (fun fmt -> if List.length outputs > 0 then fprintf fmt ", ") *)
|
590
|
(* (fprintf_list ~sep:"," (fun fmt v -> fprintf fmt "*%s" v.var_id)) outputs; *)
|
591
|
Utils.fprintf_list ~sep:"@ " (fun fmt (name, assumes, ensures) ->
|
592
|
fprintf fmt "behavior %s:@[@ %a@ %a@]"
|
593
|
name
|
594
|
(Utils.fprintf_list ~sep:"@ " (fun fmt r -> fprintf fmt "assumes %a" pp_eexpr_expr r)) assumes
|
595
|
(Utils.fprintf_list ~sep:"@ " (fun fmt r -> fprintf fmt "ensures %a" pp_eexpr_expr r)) ensures
|
596
|
) fmt spec.m_behaviors;
|
597
|
(* Assings *)
|
598
|
pp_assigns [] stateless machines fmt m;
|
599
|
fprintf fmt "@]@ */@.";
|
600
|
()
|
601
|
|
602
|
|
603
|
let pp_c_decl_struct_spec_var fmt id =
|
604
|
if !Options.ghost_mode then
|
605
|
fprintf fmt "/*@@ ghost %a */" pp_c_decl_struct_var id
|
606
|
else
|
607
|
fprintf fmt "%a" pp_c_decl_struct_var id
|
608
|
|
609
|
module Make =
|
610
|
functor (SpecArg: SPECARG) ->
|
611
|
struct
|
612
|
|
613
|
open SpecArg
|
614
|
|
615
|
(**************************************************************************)
|
616
|
(* INITIALIZE DATA *)
|
617
|
(**************************************************************************)
|
618
|
(* Map each machine uid to its compiled spec (and TODO later annot) *)
|
619
|
let compiled_contracts : (node_desc * spec_machine_t) list ref = ref []
|
620
|
let compiled_ee : eexpr_minimachine_t list ref = ref []
|
621
|
|
622
|
let init machines =
|
623
|
|
624
|
List.iter (
|
625
|
fun m -> (* iterate through spec eexpr and register them *)
|
626
|
let nd = m.mname in
|
627
|
let translate_eexpr ee =
|
628
|
let eem = translate_eexpr nd ee in
|
629
|
compiled_ee := eem::!compiled_ee;
|
630
|
eem
|
631
|
in
|
632
|
let tl = List.map translate_eexpr in
|
633
|
match m.mspec with
|
634
|
| None -> ()
|
635
|
| Some spec ->
|
636
|
let compiled_spec =
|
637
|
{ m_requires = tl spec.requires;
|
638
|
m_ensures = tl spec.ensures;
|
639
|
m_behaviors = List.map
|
640
|
(fun (s, eel1, eel2, _) -> (s, tl eel1, tl eel2))
|
641
|
spec.behaviors
|
642
|
}
|
643
|
in
|
644
|
compiled_contracts := (nd, compiled_spec)::!compiled_contracts
|
645
|
) machines
|
646
|
|
647
|
let get_ee_minimachine ee =
|
648
|
List.find (fun eem -> eem.muid = ee.eexpr_tag) !compiled_ee
|
649
|
|
650
|
let get_spec nd = List.assoc nd !compiled_contracts
|
651
|
|
652
|
(**************************************************************************)
|
653
|
(* HEADER *)
|
654
|
(**************************************************************************)
|
655
|
|
656
|
module HdrMod = struct
|
657
|
|
658
|
let get_spec_memories m =
|
659
|
match m.mspec with
|
660
|
None -> []
|
661
|
| Some _ ->
|
662
|
let spec = get_spec m.mname in
|
663
|
List.flatten (
|
664
|
List.map (fun ee -> ee.mmmemory)
|
665
|
(spec.m_requires@
|
666
|
spec.m_ensures@
|
667
|
(List.fold_left
|
668
|
(fun accu (_,assumes, ensures) -> assumes@ensures@accu)
|
669
|
[] spec.m_behaviors)
|
670
|
))
|
671
|
|
672
|
let _print_machine_decl_init_fun_prefix pre post fmt m =
|
673
|
let self = mk_self m in
|
674
|
fprintf fmt "/*@@requires \\valid(%s);@." self;
|
675
|
pre fmt;
|
676
|
post fmt;
|
677
|
fprintf fmt "assigns *%s;*/@." self
|
678
|
|
679
|
|
680
|
let _print_machine_decl_step_fun_prefix stateless pre post fmt m =
|
681
|
(* Print specification contracts if any *)
|
682
|
(match m.mspec with
|
683
|
| None -> pp_acsl_mem_valid stateless pre post machines fmt m
|
684
|
| Some _ ->
|
685
|
let spec = get_spec m.mname in
|
686
|
pp_acsl_spec_contracts stateless pre post machines m m.mstep.step_outputs fmt spec
|
687
|
)
|
688
|
|
689
|
let has_spec_mem m = (get_spec_memories m) <> []
|
690
|
|
691
|
let pp_registers_struct fmt m =
|
692
|
let spec_memories = get_spec_memories m in
|
693
|
(Utils.fprintf_list ~sep:"@;" pp_c_decl_struct_spec_var) fmt spec_memories
|
694
|
|
695
|
let print_machine_decl_prefix fmt m =
|
696
|
(* Print specification if any *)
|
697
|
let mems = Machine_code.get_mems m machines in
|
698
|
(match m.mspec with
|
699
|
| None -> ()
|
700
|
| Some _ ->
|
701
|
let spec = get_spec m.mname in
|
702
|
pp_acsl_spec_logics (List.length mems == 0) m fmt spec
|
703
|
)
|
704
|
|
705
|
let print_machine_decl_step_fun_prefix fmt m = _print_machine_decl_step_fun_prefix false (fun fmt -> ()) (fun fmt -> ()) fmt m
|
706
|
|
707
|
let print_machine_decl_init_fun_prefix fmt m = _print_machine_decl_init_fun_prefix (fun fmt -> ()) (fun fmt -> ()) fmt m
|
708
|
|
709
|
let print_machine_decl_stateless_fun_prefix fmt m = _print_machine_decl_step_fun_prefix true (fun fmt -> ()) (fun fmt -> ()) fmt m
|
710
|
|
711
|
let print_global_decl = fun fmt -> ()
|
712
|
|
713
|
end
|
714
|
|
715
|
(**************************************************************************)
|
716
|
(* CODE *)
|
717
|
(**************************************************************************)
|
718
|
|
719
|
module SrcMod =
|
720
|
struct
|
721
|
|
722
|
|
723
|
(**************************************************************************)
|
724
|
(* CODE *)
|
725
|
(**************************************************************************)
|
726
|
|
727
|
let pp_instr_debug m (eem: eexpr_minimachine_t) fmt instrs =
|
728
|
let self = mk_self m in
|
729
|
let conv_m = machine_from_minimachine_effects m eem in
|
730
|
Utils.fprintf_list ~sep:"@;"
|
731
|
(C_backend_src.pp_machine_instr_internal
|
732
|
[] (* no dependencies considered *)
|
733
|
conv_m
|
734
|
(* (m.mname.node_id, *)
|
735
|
(* ee.mmstep_effects.step_inputs, *)
|
736
|
(* ee.mmstep_effects.step_outputs, *)
|
737
|
(* ee.mmstep_effects.step_locals, *)
|
738
|
(* ee.mmmemory, *)
|
739
|
(* ee.mminstances, *)
|
740
|
(* ee.mmcalls) *)
|
741
|
self)
|
742
|
fmt
|
743
|
instrs
|
744
|
|
745
|
(* Print instructions *)
|
746
|
let pp_instr pp_machine_instr m (eem: eexpr_minimachine_t) fmt instrs =
|
747
|
let self = mk_self m in
|
748
|
let conv_m = machine_from_minimachine_effects m eem in
|
749
|
Utils.fprintf_list ~sep:"@;"
|
750
|
(pp_machine_instr
|
751
|
[] (* no dependencies considered *)
|
752
|
conv_m
|
753
|
(* (m.mname.node_id, *)
|
754
|
(* ee.mmstep_effects.step_inputs, *)
|
755
|
(* ee.mmstep_effects.step_outputs, *)
|
756
|
(* ee.mmstep_effects.step_locals, *)
|
757
|
(* ee.mmmemory, *)
|
758
|
(* ee.mminstances, *)
|
759
|
(* ee.mmcalls) *)
|
760
|
self)
|
761
|
fmt
|
762
|
instrs
|
763
|
|
764
|
let pp_acsl_spec_side_effects pp_machine_instr m fmt spec =
|
765
|
let pp_side_effects fmt ee =
|
766
|
(* Declare local vars *)
|
767
|
let locals =
|
768
|
List.filter
|
769
|
(fun l -> not (List.mem l m.mstep.step_locals))
|
770
|
ee.mmstep_effects.step_locals
|
771
|
in
|
772
|
(Utils.fprintf_list ~sep:";@," pp_c_decl_local_var) fmt locals;
|
773
|
Utils.pp_final_char_if_non_empty ";@," locals fmt ;
|
774
|
pp_instr pp_machine_instr m ee fmt ee.mmstep_effects.step_instrs
|
775
|
in
|
776
|
let effects = spec.m_requires@spec.m_ensures@
|
777
|
(List.fold_left
|
778
|
(fun accu (_,assumes, ensures) -> assumes@ensures@accu)
|
779
|
[] spec.m_behaviors)
|
780
|
in
|
781
|
let effects = List.filter (fun ee -> ee.mmstep_effects.step_instrs <> []) effects in
|
782
|
let all_instrs = List.flatten (List.map (fun e -> e.mmstep_effects.step_instrs) effects) in
|
783
|
if all_instrs <> [] then (
|
784
|
fprintf fmt "// Begin of specification memory update block@;";
|
785
|
if !Options.ghost_mode then
|
786
|
fprintf fmt "@[<v 3>/*@@ ghost %a@]@;*/@;"
|
787
|
(Utils.fprintf_list ~sep:"@;" pp_side_effects)
|
788
|
effects
|
789
|
else (
|
790
|
Utils.fprintf_list ~sep:"@;" pp_side_effects fmt effects;
|
791
|
fprintf fmt "@;"
|
792
|
)
|
793
|
;
|
794
|
fprintf fmt "// End of specification memory update block@."
|
795
|
)
|
796
|
|
797
|
let pp_acsl_spec_init_side_effects pp_machine_instr m fmt spec =
|
798
|
let pp_side_effects fmt ee =
|
799
|
(* Print instructions *)
|
800
|
pp_instr pp_machine_instr m ee fmt ee.mminit
|
801
|
in
|
802
|
|
803
|
let effects = spec.m_requires@spec.m_ensures@
|
804
|
(List.fold_left
|
805
|
(fun accu (_,assumes, ensures) -> assumes@ensures@accu)
|
806
|
[] spec.m_behaviors)
|
807
|
in
|
808
|
|
809
|
let effects = List.filter (fun ee -> ee.mminit <> []) effects in
|
810
|
let all_instrs = List.flatten (List.map (fun e -> e.mminit) effects) in
|
811
|
if all_instrs <> [] then (
|
812
|
fprintf fmt "// Begin of specification memory init block@;";
|
813
|
if !Options.ghost_mode then
|
814
|
fprintf fmt "@[<v 3>/*@@ ghost %a@]@;*/@;"
|
815
|
(Utils.fprintf_list ~sep:"@;" pp_side_effects)
|
816
|
effects
|
817
|
else (
|
818
|
Utils.fprintf_list ~sep:"@;" pp_side_effects fmt effects;
|
819
|
fprintf fmt "@;"
|
820
|
)
|
821
|
;
|
822
|
fprintf fmt "// End of specification memory init block@."
|
823
|
)
|
824
|
|
825
|
let print_step_code_postfix pp_machine_instr stateless fmt m =
|
826
|
(* Print specification if any: insert side effect at the end of the function body *)
|
827
|
match m.mspec with
|
828
|
| None -> ()
|
829
|
| Some _ -> (* the node contain a spec. It has been compiled in the init phase *)
|
830
|
let spec = get_spec m.mname in
|
831
|
pp_acsl_spec_side_effects pp_machine_instr m fmt spec
|
832
|
|
833
|
let print_step_code_prefix pp_machine_instr x fmt m = ()
|
834
|
let print_step_code_midfix pp_machine_instr x fmt m = ()
|
835
|
|
836
|
let print_instr_prefix m fmt x = ()
|
837
|
let print_instr_postfix m fmt x = ()
|
838
|
|
839
|
let print_init_code_postfix pp_machine_instr fmt m =
|
840
|
(* Print specification if any: insert side effect at the end of the function body *)
|
841
|
match m.mspec with
|
842
|
| None -> ()
|
843
|
| Some _ -> (* the node contain a spec. It has been compiled in the init phase *)
|
844
|
let spec = get_spec m.mname in
|
845
|
pp_acsl_spec_init_side_effects pp_machine_instr m fmt spec
|
846
|
|
847
|
end
|
848
|
|
849
|
(**************************************************************************)
|
850
|
(* MAKEFILE *)
|
851
|
(**************************************************************************)
|
852
|
|
853
|
let makefile_targets fmt basename nodename dependencies =
|
854
|
fprintf fmt "FRAMACEACSL=`frama-c -print-share-path`/e-acsl@.";
|
855
|
(* EACSL version of library file . c *)
|
856
|
fprintf fmt "%s_eacsl.c: %s.c %s.h@." basename basename basename;
|
857
|
fprintf fmt
|
858
|
"\tframa-c -e-acsl-full-mmodel -machdep x86_64 -e-acsl %s.c -then-on e-acsl -print -ocode %s_eacsl.c@."
|
859
|
basename basename;
|
860
|
fprintf fmt "@.";
|
861
|
fprintf fmt "@.";
|
862
|
|
863
|
(* EACSL version of library file . c + main .c *)
|
864
|
fprintf fmt "%s_main_eacsl.c: %s.c %s.h %s_main.c@." basename basename basename basename;
|
865
|
fprintf fmt "\tframa-c -e-acsl-full-mmodel -machdep x86_64 -e-acsl %s.c %s_main.c -then-on e-acsl -print -ocode %s_main_eacsl.i@."
|
866
|
basename basename basename;
|
867
|
(* Ugly hack to deal with eacsl bugs *)
|
868
|
fprintf fmt "\tgrep -v _fc_stdout %s_main_eacsl.i > %s_main_eacsl.c" basename basename;
|
869
|
fprintf fmt "@.";
|
870
|
fprintf fmt "@.";
|
871
|
|
872
|
(* EACSL version of binary *)
|
873
|
fprintf fmt "%s_main_eacsl: %s_main_eacsl.c@." basename basename;
|
874
|
fprintf fmt "\t${GCC} -Wno-attributes -I${INC} -I. -c %s_main_eacsl.c@." basename; (* compiling instrumented lib + main *)
|
875
|
C_backend_makefile.fprintf_dependencies fmt dependencies;
|
876
|
fprintf fmt "\t${GCC} -Wno-attributes -o %s_main_eacsl io_frontend.o %a %s %s_main_eacsl.o %a@."
|
877
|
basename
|
878
|
(Utils.fprintf_list ~sep:" " (fun fmt (s, _, _) -> Format.fprintf fmt "%s.o" s))
|
879
|
(C_backend_makefile.compiled_dependencies dependencies)
|
880
|
("${FRAMACEACSL}/e_acsl.c "
|
881
|
^ "${FRAMACEACSL}/memory_model/e_acsl_bittree.c "
|
882
|
^ "${FRAMACEACSL}/memory_model/e_acsl_mmodel.c")
|
883
|
basename
|
884
|
(Utils.fprintf_list ~sep:" " (fun fmt lib -> fprintf fmt "-l%s" lib))
|
885
|
(C_backend_makefile.lib_dependencies dependencies)
|
886
|
;
|
887
|
fprintf fmt "@.";
|
888
|
|
889
|
module MakefileMod =
|
890
|
struct
|
891
|
let other_targets = makefile_targets
|
892
|
|
893
|
end
|
894
|
|
895
|
module MainMod = C_backend_main.EmptyMod
|
896
|
|
897
|
module CoqMod = C_backend_coq.EmptyMod
|
898
|
|
899
|
end
|
900
|
(* Local Variables: *)
|
901
|
(* compile-command:"make -C ../../.." *)
|
902
|
(* End: *)
|