From 478edbed94011c041ca41f88df7403f6b4356c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9lio=20Brun?= <lelio.brun@isae-supaero.fr> Date: Thu, 23 Sep 2021 16:40:08 +0200 Subject: [PATCH] proper handling of stateless nodes and automata --- src/backends/C/c_backend_spec.ml | 43 ++++++++++++++++---------- src/checks/stateless.ml | 53 ++++++++++++++++++++++++-------- src/compiler_common.ml | 6 ++-- src/compiler_common.mli | 1 + src/corelang.ml | 25 ++++++++------- src/corelang.mli | 2 ++ src/lustre_types.ml | 20 ++++++++++++ src/lustre_types.mli | 2 ++ src/machine_code.ml | 31 ++++++++++++------- src/machine_code_common.ml | 2 +- src/optimize_machine.ml | 4 +-- src/printers.ml | 20 ------------ src/spec_common.ml | 5 +-- src/spec_common.mli | 1 + src/spec_types.ml | 3 +- src/spec_types.mli | 3 +- 16 files changed, 141 insertions(+), 80 deletions(-) diff --git a/src/backends/C/c_backend_spec.ml b/src/backends/C/c_backend_spec.ml index 6839bff3..d2acd9a8 100644 --- a/src/backends/C/c_backend_spec.ml +++ b/src/backends/C/c_backend_spec.ml @@ -325,9 +325,8 @@ let pp_memory_pack pp_mem pp_self fmt (mp, mem, self) = fmt (mp.mpname.node_id, mem, self) -let pp_transition_aux ?i m pp_mem_in pp_mem_out pp_var fmt +let pp_transition_aux ?i stateless pp_mem_in pp_mem_out pp_var fmt (name, vars, mem_in, mem_out) = - let stateless = fst (get_stateless_status m) in fprintf fmt "%s_transition%a(@[<hov>%t%a%t@])" @@ -341,10 +340,10 @@ let pp_transition_aux ?i m pp_mem_in pp_mem_out pp_var fmt vars (fun fmt -> if not stateless then fprintf fmt ",@ %a" pp_mem_out mem_out) -let pp_transition m pp_mem_in pp_mem_out pp_var fmt (t, mem_in, mem_out) = +let pp_transition stateless pp_mem_in pp_mem_out pp_var fmt (t, mem_in, mem_out) = pp_transition_aux ?i:t.tindex - m + stateless pp_mem_in pp_mem_out pp_var @@ -352,7 +351,8 @@ let pp_transition m pp_mem_in pp_mem_out pp_var fmt (t, mem_in, mem_out) = (t.tname.node_id, t.tvars, mem_in, mem_out) let pp_transition_aux' ?i m = - pp_transition_aux ?i m pp_print_string pp_print_string (fun fmt v -> + let stateless = fst (get_stateless_status m) in + pp_transition_aux ?i stateless pp_print_string pp_print_string (fun fmt v -> (if is_output m v then pp_ptr_decl else pp_var_decl) fmt v) let pp_reset_cleared pp_mem_in pp_mem_out fmt (name, mem_in, mem_out) = @@ -434,7 +434,7 @@ module PrintSpec = struct fun test_output fmt e -> pp_expr ~test_output m mem_out fmt e in match p with - | Transition (f, inst, i, vars, r, mems, insts) -> + | Transition (stateless, f, inst, i, vars, r, mems, insts) -> let pp_mem_in, pp_mem_out = match inst with | None -> @@ -449,7 +449,7 @@ module PrintSpec = struct in pp_transition_aux ?i - m + stateless pp_mem_in pp_mem_out (pp_expr test_output) @@ -686,10 +686,11 @@ let pp_transition_def m fmt t = let name = t.tname.node_id in let mem_in = mk_mem_in m in let mem_out = mk_mem_out m in + let stateless = fst (get_stateless_status m) in pp_acsl (pp_predicate (pp_transition - m + stateless (pp_machine_decl' ~ghost:true) (pp_machine_decl' ~ghost:true) (pp_local m)) @@ -717,6 +718,7 @@ let pp_transition_footprint_lemma m fmt t = let name = t.tname.node_id in let mem_in = mk_mem_in m in let mem_out = mk_mem_out m in + let stateless = fst (get_stateless_status m) in let mems = ISet.( diff (of_list (List.map (fun v -> v.var_id) m.mmemory)) t.tmem_footprint) @@ -740,6 +742,7 @@ let pp_transition_footprint_lemma m fmt t = ?mems ?insts ?i:t.tindex + stateless name (vdecls_to_vals t.tvars) in @@ -1057,8 +1060,8 @@ module SrcMod = struct | e -> e let rename_predicate n = function - | Transition (f, inst, i, es, r, mf, mif) -> - Transition (f, inst, i, List.map (rename_expression n) es, r, mf, mif) + | Transition (s, f, inst, i, es, r, mf, mif) -> + Transition (s, f, inst, i, List.map (rename_expression n) es, r, mf, mif) | p -> p let rec rename_formula n = function @@ -1109,6 +1112,8 @@ module SrcMod = struct let mem = mk_mem m in let inputs = m.mstep.step_inputs in let outputs = m.mstep.step_outputs in + let stateless = fst (get_stateless_status m) in + let stateless_c = fst (get_stateless_status m_c) in let l = List.init (k - 1) (fun n -> n + 1) in pp_print_list ~pp_open_box:pp_open_vbox0 ~pp_sep:pp_print_cutcut (fun fmt n -> @@ -1121,13 +1126,13 @@ module SrcMod = struct | 0 -> pp_init pp_print_string fmt (name, rename 0 mem) | n -> - pp_transition_aux m pp_print_string pp_print_string pp_var_decl + pp_transition_aux stateless pp_print_string pp_print_string pp_var_decl fmt (name, List.map (rename_var_decl n) (inputs @ outputs), rename (n - 1) mem, rename n mem))) - (pp_transition_aux m_c pp_print_string pp_print_string pp_var_decl)) + (pp_transition_aux stateless_c pp_print_string pp_print_string pp_var_decl)) (pp_contract m) in pp_predicate @@ -1168,6 +1173,8 @@ module SrcMod = struct let mem = mk_mem m in let inputs = m.mstep.step_inputs in let outputs = m.mstep.step_outputs in + let stateless = fst (get_stateless_status m) in + let stateless_c = fst (get_stateless_status m_c) in let l = List.init k (fun n -> n + 1) in let pp = pp_implies @@ -1178,8 +1185,8 @@ module SrcMod = struct let c_inputs = List.map (fun v -> false, rename_var_decl n v) m_c.mstep.step_inputs in let c_outputs = List.map (fun v -> true, rename_var_decl n v) m_c.mstep.step_outputs in pp_and - (pp_transition_aux m pp_print_string pp_print_string pp_var_decl) - (pp_transition_aux m_c pp_print_string pp_print_string pp) + (pp_transition_aux stateless pp_print_string pp_print_string pp_var_decl) + (pp_transition_aux stateless_c pp_print_string pp_print_string pp) fmt ((name, List.map (rename_var_decl n) (inputs @ outputs), @@ -1253,6 +1260,7 @@ module SrcMod = struct let mem_out = mk_mem_out m in let inputs = m.mstep.step_inputs in let outputs = m.mstep.step_outputs in + let stateless_c = fst (get_stateless_status m_c) in let l = List.init k (fun n -> n + 1) in pp_axiomatic (fun fmt () -> fprintf fmt "%s_k_Induction" name) @@ -1280,7 +1288,7 @@ module SrcMod = struct (pp_comma_list pp_var_decl) fmt (n, mem_in, mem_out))) - (pp_transition_aux m_c pp_print_string pp_print_string pp_var_decl)) + (pp_transition_aux stateless_c pp_print_string pp_print_string pp_var_decl)) (pp_contract m))))) fmt ((), @@ -1326,6 +1334,7 @@ module SrcMod = struct in let inputs = m.mstep.step_inputs in let outputs = m.mstep.step_outputs in + let stateless = fst (get_stateless_status m) in let pp_if_outputs pp = if outputs = [] then pp_print_nothing else pp in @@ -1358,7 +1367,7 @@ module SrcMod = struct pp_spec_vars fmt (); pp_acsl_cut (fun fmt () -> - if fst (get_stateless_status m) then + if stateless then fprintf fmt "%a@,%a@,%a@,%a@,%a@,%a" @@ -1388,7 +1397,7 @@ module SrcMod = struct (pp_ensures (pp_memory_pack_aux pp_ptr pp_print_string)) (name, mem, self) (pp_ensures - (pp_transition_aux m (pp_old pp_ptr) pp_ptr (fun fmt v -> + (pp_transition_aux stateless (pp_old pp_ptr) pp_ptr (fun fmt v -> (if is_output m v then pp_ptr_decl else pp_var_decl) fmt v))) (name, inputs @ outputs, mem, mem) pp_spec diff --git a/src/checks/stateless.ml b/src/checks/stateless.ml index 6f163714..80d67e03 100644 --- a/src/checks/stateless.ml +++ b/src/checks/stateless.ml @@ -62,6 +62,9 @@ let rec check_expr expr = i); check_expr e' && reset_opt && stateless_node +and check_eexpr e = + check_expr e.eexpr_qfexpr + and compute_node nd = (* returns true iff the node is stateless.*) let eqs, aut = get_node_eqs nd in @@ -69,21 +72,47 @@ and compute_node nd = && (* A node containinig an automaton will be stateful *) List.for_all (fun eq -> check_expr eq.eq_rhs) eqs +and compute_contract nd = + let c = node_as_contract nd in + let eqs, aut = get_contract_eqs c in + aut = [] + && (* A node containinig an automaton will be stateful *) + List.for_all (fun eq -> check_expr eq.eq_rhs) eqs + && + List.for_all check_eexpr c.assume + && + List.for_all check_eexpr c.guarantees + +and compute_node_or_contract nd = + match nd.node_spec, nd.node_iscontract with + | None, false + | Some (NodeSpec _), false + | Some (Contract _), false -> + compute_node nd + | Some (Contract _), true -> + compute_contract nd + | _ -> + assert false + and check_node td = match td.top_decl_desc with - | Node nd -> ( - match nd.node_stateless with - | None -> - let stateless = compute_node nd in - nd.node_stateless <- Some stateless; - if nd.node_dec_stateless && not stateless then - raise (Error (td.top_decl_loc, Stateful_kwd nd.node_id)) - else ( + | Node nd -> + begin match nd.node_stateless with + | None -> + let stateless = compute_node_or_contract nd in + nd.node_stateless <- Some stateless; + if stateless + then (if not nd.node_dec_stateless + then Log.report ~level:2 (fun fmt -> + Format.fprintf fmt "%s: node -> function@;" nd.node_id)) + else if nd.node_dec_stateless + then raise (Error (td.top_decl_loc, Stateful_kwd nd.node_id)); nd.node_dec_stateless <- stateless; - stateless) - | Some stl -> - stl) - | ImportedNode nd -> + stateless + | Some stl -> + stl + end +| ImportedNode nd -> if nd.nodei_prototype = Some "C" && not nd.nodei_stateless then raise (Error (td.top_decl_loc, Stateful_ext_C nd.nodei_id)); nd.nodei_stateless diff --git a/src/compiler_common.ml b/src/compiler_common.ml index 2c087aef..91d5b00f 100644 --- a/src/compiler_common.ml +++ b/src/compiler_common.ml @@ -72,8 +72,9 @@ let expand_automata decls = let check_stateless_decls decls = Log.report ~level:1 (fun fmt -> - fprintf fmt "@ .. checking stateless/stateful status@ "); - try Stateless.check_prog decls + fprintf fmt "@ @[<v 2>.. checking stateless/stateful status@ "); + try (Stateless.check_prog decls; + Log.report ~level:1 (fun fmt -> fprintf fmt "@]@ ")) with Stateless.Error (loc, err) as exc -> eprintf "Stateless status error: %a%a@." @@ -426,3 +427,4 @@ let update_vdecl_parents_prog prog = | _ -> ()) prog + diff --git a/src/compiler_common.mli b/src/compiler_common.mli index 5b686c31..9aa99565 100644 --- a/src/compiler_common.mli +++ b/src/compiler_common.mli @@ -28,3 +28,4 @@ val clock_decls : Clocks.t Env.t -> program_t -> Clocks.t Env.t val create_dest_dir : unit -> unit val track_exception : unit -> unit + diff --git a/src/corelang.ml b/src/corelang.ml index 32a0f891..4b78ff1f 100644 --- a/src/corelang.ml +++ b/src/corelang.ml @@ -765,18 +765,18 @@ let get_node_var id node = node.node_id; *) raise Not_found +let get_eqs stmts = + List.fold_right + (fun stmt (res_eq, res_aut) -> + match stmt with + | Eq eq -> + eq :: res_eq, res_aut + | Aut aut -> + res_eq, aut :: res_aut) + stmts + ([], []) + let get_node_eqs = - let get_eqs stmts = - List.fold_right - (fun stmt (res_eq, res_aut) -> - match stmt with - | Eq eq -> - eq :: res_eq, res_aut - | Aut aut -> - res_eq, aut :: res_aut) - stmts - ([], []) - in let table_eqs = Hashtbl.create 23 in fun nd -> try @@ -787,6 +787,9 @@ let get_node_eqs = Hashtbl.replace table_eqs nd.node_id (nd.node_stmts, res); res +let get_contract_eqs c = + get_eqs c.stmts + let get_node_eq id node = let eqs, _ = get_node_eqs node in try List.find (fun eq -> List.mem id eq.eq_lhs) eqs diff --git a/src/corelang.mli b/src/corelang.mli index aa5af920..3500c8e1 100644 --- a/src/corelang.mli +++ b/src/corelang.mli @@ -156,6 +156,8 @@ val get_node_var : ident -> node_desc -> var_decl val get_node_eqs : node_desc -> eq list * automata_desc list +val get_contract_eqs : contract_desc -> eq list * automata_desc list + val get_node_eq : ident -> node_desc -> eq val get_node_interface : node_desc -> imported_node_desc diff --git a/src/lustre_types.ml b/src/lustre_types.ml index 4ca126ce..a9aad140 100644 --- a/src/lustre_types.ml +++ b/src/lustre_types.ml @@ -246,6 +246,26 @@ let tag_true = "true" let tag_false = "false" +(* Project the contract node as a pure contract: local memories are pushed back + in the contract definition. Should mainly be used to print it *) +let node_as_contract nd = + match nd.node_spec with + | None | Some (NodeSpec _) -> + raise (Invalid_argument "Not a contract") + | Some (Contract c) -> + (* While a processed contract shall have no locals, sttms nor consts, an + unprocessed one could. So we conservatively merge elements, to enable + printing unprocessed contracts *) + let consts, locals = + List.partition (fun v -> v.var_dec_const) nd.node_locals + in + { + c with + consts = consts @ c.consts; + locals = locals @ c.locals; + stmts = nd.node_stmts @ c.stmts; + } + (* Local Variables: *) (* compile-command:"make -C .." *) (* End: *) diff --git a/src/lustre_types.mli b/src/lustre_types.mli index aa030847..7cfdbfa3 100644 --- a/src/lustre_types.mli +++ b/src/lustre_types.mli @@ -233,3 +233,5 @@ type spec_types = val tag_true : label val tag_false : label + +val node_as_contract: node_desc -> contract_desc diff --git a/src/machine_code.ml b/src/machine_code.ml index 0d757dd3..7e9f43ba 100644 --- a/src/machine_code.ml +++ b/src/machine_code.ml @@ -239,6 +239,7 @@ let reset_instance env i r c = None, [] let translate_eq env ctx nd inputs locals outputs i eq = + let stateless = fst (get_stateless_status_node nd) in let id = nd.node_id in let translate_expr = translate_expr env in let translate_act = translate_act env in @@ -257,6 +258,7 @@ let translate_eq env ctx nd inputs locals outputs i eq = [ mk_transition ~i:(i - 1) + stateless id (vdecls_to_vals (inputs @ locals_pi @ outputs_pi)); a; @@ -276,6 +278,7 @@ let translate_eq env ctx nd inputs locals outputs i eq = @ [ mk_transition ~i + stateless id (vdecls_to_vals (inputs @ locals_i @ outputs_i)); ]; @@ -306,7 +309,7 @@ let translate_eq env ctx nd inputs locals outputs i eq = ctl (MStep ([ var_x ], inst, [ c1; c2 ])) (mk_memory_pack ~inst (node_name td)) - (mk_transition ~inst (node_name td) [ vdecl_to_val var_x ]) + (mk_transition ~inst false (node_name td) [ vdecl_to_val var_x ]) { ctx with j = IMap.add inst (td, []) ctx.j } in { ctx with si = mkinstr (MSetReset inst) :: ctx.si } @@ -339,7 +342,7 @@ let translate_eq env ctx nd inputs locals outputs i eq = let vl = List.map translate_expr el in let node_f = node_from_name f in let call_f = node_f, NodeDep.filter_static_inputs (node_inputs node_f) el in - let inst = new_instance node_f eq.eq_rhs.expr_tag in + let i = new_instance node_f eq.eq_rhs.expr_tag in let env_cks = List.fold_right (fun arg cks -> arg.expr_clock :: cks) @@ -349,17 +352,20 @@ let translate_eq env ctx nd inputs locals outputs i eq = let call_ck = Clock_calculus.compute_root_clock (Clock_predef.ck_tuple env_cks) in - let r, reset_inst = reset_instance inst r call_ck in + let r, reset_inst = reset_instance i r call_ck in + let stateless = Stateless.check_node node_f in + let inst = if stateless then None else Some i in + let mp = if stateless then True else mk_memory_pack ?inst (node_name node_f) in let ctx = ctl ~ck:call_ck - (MStep (var_p, inst, vl)) - (mk_memory_pack ~inst (node_name node_f)) - (mk_transition ?r ~inst (node_name node_f) (vl @ vdecls_to_vals var_p)) + (MStep (var_p, i, vl)) + mp + (mk_transition ?r ?inst stateless (node_name node_f) (vl @ vdecls_to_vals var_p)) { ctx with - j = IMap.add inst call_f ctx.j; - s = (if Stateless.check_node node_f then [] else reset_inst) @ ctx.s; + j = IMap.add i call_f ctx.j; + s = (if stateless then [] else reset_inst) @ ctx.s; } in (*Clocks.new_var true in Clock_calculus.unify_imported_clock (Some call_ck) @@ -369,8 +375,8 @@ let translate_eq env ctx nd inputs locals outputs i eq = { ctx with si = - (if Stateless.check_node node_f then ctx.si - else mkinstr (MSetReset inst) :: ctx.si); + (if stateless then ctx.si + else mkinstr (MSetReset i) :: ctx.si); } | [ x ], _ -> begin try @@ -495,8 +501,10 @@ let transition_0 nd = } let transition_toplevel nd i = + let stateless = fst (get_stateless_status_node nd) in let tr = mk_transition + stateless nd.node_id ~i (vdecls_to_vals (nd.node_inputs @ nd.node_outputs)) @@ -541,6 +549,7 @@ let translate_spec env = function NodeSpec s let translate_decl nd sch = + let stateless = fst (get_stateless_status_node nd) in (* Format.eprintf "Translating node %s@." nd.node_id; *) (* Extracting eqs, variables .. *) let eqs, auts = get_node_eqs nd in @@ -621,7 +630,7 @@ let translate_decl nd sch = ~instr_spec: ((if fst (get_stateless_status_node nd) then [] else [ mk_memory_pack ~i:0 nd.node_id ]) - @ [ mk_transition ~i:0 nd.node_id (vdecls_to_vals nd.node_inputs) ]) + @ [ mk_transition ~i:0 stateless nd.node_id (vdecls_to_vals nd.node_inputs) ]) MClearReset in let mnode_spec = Utils.option_map (translate_spec env) nd.node_spec in diff --git a/src/machine_code_common.ml b/src/machine_code_common.ml index 0ea7a177..922f3b79 100644 --- a/src/machine_code_common.ml +++ b/src/machine_code_common.ml @@ -59,7 +59,7 @@ module PrintSpec = struct fun fmt e -> pp_expr m fmt e in match p with - | Transition (f, inst, i, vars, _r, _mems, _insts) -> + | Transition (_, f, inst, i, vars, _r, _mems, _insts) -> fprintf fmt "Transition_%a<%a>%a%a" diff --git a/src/optimize_machine.ml b/src/optimize_machine.ml index f9d32b40..cc42e739 100644 --- a/src/optimize_machine.ml +++ b/src/optimize_machine.ml @@ -644,9 +644,9 @@ let expr_spec_replace : e let predicate_spec_replace fvar = function - | Transition (f, inst, i, vars, r, mems, insts) -> + | Transition (s, f, inst, i, vars, r, mems, insts) -> Transition - (f, inst, i, List.map (expr_spec_replace fvar) vars, r, mems, insts) + (s, f, inst, i, List.map (expr_spec_replace fvar) vars, r, mems, insts) | p -> p diff --git a/src/printers.ml b/src/printers.ml index 15bae926..5d3a818c 100644 --- a/src/printers.ml +++ b/src/printers.ml @@ -538,26 +538,6 @@ let pp_spec fmt spec = import.outputs)) spec.imports -(* Project the contract node as a pure contract: local memories are pushed back - in the contract definition. Should mainly be used to print it *) -let node_as_contract nd = - match nd.node_spec with - | None | Some (NodeSpec _) -> - raise (Invalid_argument "Not a contract") - | Some (Contract c) -> - (* While a processed contract shall have no locals, sttms nor consts, an - unprocessed one could. So we conservatively merge elements, to enable - printing unprocessed contracts *) - let consts, locals = - List.partition (fun v -> v.var_dec_const) nd.node_locals - in - { - c with - consts = consts @ c.consts; - locals = locals @ c.locals; - stmts = nd.node_stmts @ c.stmts; - } - (* Printing top contract as comments in regular output and as contract in kind2 *) let pp_contract fmt nd = let c = node_as_contract nd in diff --git a/src/spec_common.ml b/src/spec_common.ml index 85e4edbb..bdae2bcf 100644 --- a/src/spec_common.ml +++ b/src/spec_common.ml @@ -79,12 +79,13 @@ let vals vs = List.map (fun v -> Val v) vs let mk_pred_call pred = Predicate pred -let mk_transition ?(mems = ISet.empty) ?(insts = IMap.empty) ?r ?i ?inst id vars +let mk_transition ?(mems = ISet.empty) ?(insts = IMap.empty) ?r ?i ?inst stateless id vars = let tr = mk_pred_call (Transition - ( id, + ( stateless, + id, inst, i, vals vars, diff --git a/src/spec_common.mli b/src/spec_common.mli index df43a864..d2ac2230 100644 --- a/src/spec_common.mli +++ b/src/spec_common.mli @@ -16,6 +16,7 @@ val mk_transition : ?r:'a -> ?i:int -> ?inst:ident -> + bool -> ident -> 'a list -> 'a formula_t diff --git a/src/spec_types.ml b/src/spec_types.ml index 8eb61540..7d2324a3 100644 --- a/src/spec_types.ml +++ b/src/spec_types.ml @@ -24,7 +24,8 @@ let type_of_l_value : type a. (a, left_v) expression_t -> Types.t = function type 'a predicate_t = | Transition : - ident (* node name *) + bool (* stateless *) + * ident (* node name *) * ident option (* instance *) * int option diff --git a/src/spec_types.mli b/src/spec_types.mli index 5edb4568..f7ab12cb 100644 --- a/src/spec_types.mli +++ b/src/spec_types.mli @@ -17,7 +17,8 @@ val type_of_l_value : ('a, left_v) expression_t -> Types.t type 'a predicate_t = | Transition : - ident (* node name *) + bool (* stateless *) + * ident (* node name *) * ident option (* instance *) * int option -- GitLab