From 3addb23bd0208153f1bfc245f2256d3b9c3afd5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9lio=20Brun?= <lb@leliobrun.net> Date: Fri, 3 Mar 2023 15:41:15 +0900 Subject: [PATCH] fix removed plugins files by mistake... --- src/plugins/salsa/machine_salsa_opt.ml | 1067 +++++++++++++++++++++++ src/plugins/salsa/machine_salsa_opt.mli | 0 src/plugins/salsa/salsaDatatypes.ml | 440 ++++++++++ src/plugins/salsa/salsaDatatypes.mli | 0 src/plugins/salsa/salsa_plugin.ml | 60 ++ src/plugins/salsa/salsa_plugin.mli | 0 src/plugins/scopes/scopes.ml | 468 ++++++++++ src/plugins/scopes/scopes.mli | 14 + 8 files changed, 2049 insertions(+) create mode 100644 src/plugins/salsa/machine_salsa_opt.ml create mode 100644 src/plugins/salsa/machine_salsa_opt.mli create mode 100644 src/plugins/salsa/salsaDatatypes.ml create mode 100644 src/plugins/salsa/salsaDatatypes.mli create mode 100644 src/plugins/salsa/salsa_plugin.ml create mode 100644 src/plugins/salsa/salsa_plugin.mli create mode 100644 src/plugins/scopes/scopes.ml create mode 100644 src/plugins/scopes/scopes.mli diff --git a/src/plugins/salsa/machine_salsa_opt.ml b/src/plugins/salsa/machine_salsa_opt.ml new file mode 100644 index 00000000..1ebd7bfe --- /dev/null +++ b/src/plugins/salsa/machine_salsa_opt.ml @@ -0,0 +1,1067 @@ +(* We try to avoid opening modules here *) +module ST = Salsa.Types +module SDT = SalsaDatatypes +module LT = Lustre_types +module MC = Machine_code + +(* Datatype for Salsa: FormalEnv, Ranges, Var set ... *) +open SalsaDatatypes + +let report = Log.report ~plugin:"salsa" ~verbose_level:Salsa.Log.verbose_level + +(******************************************************************) +(* TODO Xavier: should those functions be declared more globally? *) + +let fun_types node = + try + match node.LT.top_decl_desc with + | LT.Node nd -> + let tin, tout = Types.split_arrow nd.LT.node_type in + Types.type_list_of_type tin, Types.type_list_of_type tout + | _ -> + Format.eprintf "%a is not a node@.@?" Printers.pp_decl node; + assert false + with Not_found -> + Format.eprintf + "Unable to find type def for function %s@.@?" + (Corelang.node_name node); + assert false + +let called_node_id m id = + let td, _ = + try List.assoc id m.MT.mcalls (* TODO Xavier: mcalls or minstances ? *) + with Not_found -> assert false + in + td +(******************************************************************) + +(* Returns the set of vars that appear in the expression *) +let rec get_expr_real_vars e = + let open MT in + match e.value_desc with + | Var v when Types.is_real_type v.LT.var_type -> + Vars.singleton v + | Var _ | Cst _ -> + Vars.empty + | Fun (_, args) -> + List.fold_left + (fun acc e -> Vars.union acc (get_expr_real_vars e)) + Vars.empty + args + | Array _ | Access _ | Power _ -> + assert false + +(* Extract the variables to appear as free variables in expressions (lhs) *) +let rec get_read_vars instrs = + let open MT in + match instrs with + | [] -> + Vars.empty + | i :: tl -> ( + let vars_tl = get_read_vars tl in + match Corelang.get_instr_desc i with + | MLocalAssign (_, e) | MStateAssign (_, e) -> + Vars.union (get_expr_real_vars e) vars_tl + | MStep (_, _, el) -> + List.fold_left + (fun accu e -> Vars.union (get_expr_real_vars e) accu) + vars_tl + el + | MBranch (e, branches) -> + let vars = Vars.union (get_expr_real_vars e) vars_tl in + List.fold_left + (fun vars (_, b) -> Vars.union vars (get_read_vars b)) + vars + branches + | MReset _ | MNoReset _ | MSpec _ | MComment _ -> + Vars.empty) + +let rec get_written_vars instrs = + let open MT in + match instrs with + | [] -> + Vars.empty + | i :: tl -> ( + let vars_tl = get_written_vars tl in + match Corelang.get_instr_desc i with + | MLocalAssign (v, _) | MStateAssign (v, _) -> + Vars.add v vars_tl + | MStep (vdl, _, _) -> + List.fold_left (fun accu v -> Vars.add v accu) vars_tl vdl + | MBranch (_, branches) -> + List.fold_left + (fun vars (_, b) -> Vars.union vars (get_written_vars b)) + vars_tl + branches + | MReset _ | MNoReset _ | MSpec _ | MComment _ -> + Vars.empty) + +(* let rec iterTransformExpr fresh_id e_salsa abstractEnv old_range = *) +(* let new_expr, new_range = *) +(* Salsa.MainEPEG.transformExpression fresh_id e_salsa abstractEnv *) +(* in *) +(* Format.eprintf "New range: %a@." RangesInt.pp_val new_range; *) +(* if Salsa.Float.errLt new_range old_range < 0 then *) + +(* iterTransformExpr fresh_id new_expr abstractEnv new_range *) +(* else *) +(* new_expr, new_range *) + +(* Takes as input a salsa expression and return an optimized one *) +let opt_num_expr_sliced ranges e_salsa = + try + let fresh_id = "toto" in + + (* TODO more meaningful name *) + let abstractEnv = RangesInt.to_abstract_env ranges in + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "Launching analysis: %s@ " + (Salsa.Print.printExpression e_salsa)); + let new_e_salsa, e_val = + Salsa.MainEPEG.transformExpression fresh_id e_salsa abstractEnv + in + report ~level:2 (fun fmt -> + Format.fprintf + fmt + " Analysis done: %s@ " + (Salsa.Print.printExpression new_e_salsa)); + + (* (\* Debug *\) *) + (* Format.eprintf "Salsa:@.input expr: %s@.outputexpr: %s@." *) + (* (Salsa.Print.printExpression e_salsa) *) + (* (Salsa.Print.printExpression new_e_salsa); *) + (* (\* Debug *\) *) + report ~level:2 (fun fmt -> + Format.fprintf fmt " Computing range progress@ "); + + let old_val = Salsa.Analyzer.evalExpr e_salsa abstractEnv [] in + let expr, expr_range = + match + RangesInt.Value.leq old_val e_val, RangesInt.Value.leq e_val old_val + with + | true, true -> + if !debug then + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "No improvement on abstract value %a@ " + RangesInt.pp_val + e_val); + e_salsa, Some old_val + | false, true -> + if !debug then + report ~level:2 (fun fmt -> Format.fprintf fmt "Improved!@ "); + new_e_salsa, Some e_val + | true, false -> + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "CAREFUL --- new range is worse!. Restoring provided expression@ "); + e_salsa, Some old_val + | false, false -> + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "Error; new range is not comparable with old end. It may need \ + some investigation!@. "; + Format.fprintf + fmt + "old: %a@.new: %a@ " + RangesInt.pp_val + old_val + RangesInt.pp_val + e_val); + + new_e_salsa, Some e_val + (* assert false *) + in + report ~level:2 (fun fmt -> Format.fprintf fmt " Computing range done@ "); + + if !debug then + report ~level:2 (fun fmt -> + Format.fprintf + fmt + " @[<v>old_expr: @[<v 0>%s@ range: %a@]@ new_expr: @[<v 0>%s@ \ + range: %a@]@ @]@ " + (Salsa.Print.printExpression e_salsa) + (* MC.pp_val e *) + RangesInt.pp_val + old_val + (Salsa.Print.printExpression new_e_salsa) + (* MC.pp_val new_e *) + RangesInt.pp_val + e_val); + expr, expr_range + with (* Not_found -> *) + | Salsa.Epeg_types.EPEGError _ -> + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "BECAUSE OF AN ERROR, Expression %s was not optimized@ " + (Salsa.Print.printExpression e_salsa) + (* MC.pp_val e *)); + e_salsa, None + +(* Optimize a given expression. It returns the modified expression, a computed + range and freshly defined variables. *) +let optimize_expr nodename m constEnv printed_vars vars_env ranges formalEnv e : + MT.value_t * RangesInt.t option * MT.instr_t list * Vars.VarSet.t = + let rec opt_expr m vars_env ranges formalEnv e = + let open MT in + match e.value_desc with + | Cst cst -> + (* Format.eprintf "optmizing constant expr @ "; *) + (* the expression is a constant, we optimize it directly if it is a real + constant *) + let typ = Typing.type_const Location.dummy_loc cst in + if Types.is_real_type typ then opt_num_expr m vars_env ranges formalEnv e + else e, None, [], Vars.empty + | Var v -> + if + (not (Vars.mem v printed_vars)) + && (* TODO xavier: comment recuperer le type de l'expression? Parfois + e.value_type vaut 'd *) + (Types.is_real_type e.value_type || Types.is_real_type v.LT.var_type) + then opt_num_expr m vars_env ranges formalEnv e + else e, None, [], Vars.empty + (* Nothing to optimize for expressions containing a single non real variable *) + (* (\* optimize only numerical vars *\) *) + (* if Type_predef.is_real_type v.LT.var_type then opt_num_expr ranges + formalEnv e *) + (* else e, None *) + | Fun (fun_id, args) -> + if + (* necessarily, this is a basic function (ie. + - * / && || mod ... ) *) + (* if the return type is real then optimize it, otherwise call + recusrsively on arguments *) + Types.is_real_type e.value_type + then opt_num_expr m vars_env ranges formalEnv e + else + (* We do not care for computed local ranges. *) + let args', il, new_locals = + List.fold_right + (fun arg (al, il, nl) -> + let arg', _, arg_il, arg_nl = + opt_expr m vars_env ranges formalEnv arg + in + arg' :: al, arg_il @ il, Vars.union arg_nl nl) + args + ([], [], Vars.empty) + in + { e with value_desc = Fun (fun_id, args') }, None, il, new_locals + | Array _ | Access _ | Power _ -> + assert false + and opt_num_expr m vars_env ranges formalEnv e = + if !debug then + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "Optimizing expression @[<hov>%a@]@ " + (MC.pp_val m) + e); + (* if !debug then Format.eprintf "Optimizing expression %a with Salsa@ " + MC.pp_val e; *) + (* Convert expression *) + (* List.iter (fun (l,c) -> Format.eprintf "%s -> %a@ " l Printers.pp_const + c) constEnv; *) + let e_salsa : Salsa.Types.expression = value_t2salsa_expr constEnv e in + + (* Format.eprintf "apres deplaige constantes ok%a @." MC.pp_val + (salsa_expr2value_t vars_env [](\* constEnv *\) e_salsa) ; *) + + (* Convert formalEnv *) + (* if !debug then Format.eprintf "Formal env is [%a]@ " FormalEnv.pp + formalEnv; *) + (* if !debug then Format.eprintf "Formal env converted to salsa@ "; *) + + (* Format.eprintf "Expression avant et apres substVars.@.Avant %a@." + MC.pp_val (salsa_expr2value_t vars_env [] e_salsa) ; *) + + (* Substitute all occurences of variables by their definition in env *) + let (e_salsa : Salsa.Types.expression), _ = + Salsa.Rewrite.substVars e_salsa (FormalEnv.to_salsa constEnv formalEnv) 0 + (* TODO: Nasrine, what is this integer value for ? *) + in + + (* Format.eprintf "Apres %a@." MC.pp_val (salsa_expr2value_t vars_env [] + e_salsa) ; *) + + (* if !debug then Format.eprintf "Substituted def in expr@ "; *) + let abstractEnv = RangesInt.to_abstract_env ranges in + (* List.iter (fun (id, _) -> Format.eprintf "absenv: %s@." id) + abstractEnv; *) + (* The expression is partially evaluated by the available ranges + valEnv2ExprEnv remplce les paires id, abstractVal par id, Cst itv - on + garde evalPartExpr remplace les variables e qui sont dans env par la cst + - on garde *) + (* if !debug then Format.eprintf "avant avant eval part@ "; *) + (* Format.eprintf "avant evalpart: %a@." MC.pp_val (salsa_expr2value_t + vars_env constEnv e_salsa); *) + let e_salsa = + Salsa.Analyzer.evalPartExpr + e_salsa + (Salsa.Analyzer.valEnv2ExprEnv abstractEnv) + [] + (* no blacklisted variables *) [] + (* no arrays *) + in + + (* Format.eprintf "apres evalpart: %a@." MC.pp_val (salsa_expr2value_t + vars_env constEnv e_salsa); *) + (* Checking if we have all necessary information *) + let free_vars = get_salsa_free_vars vars_env constEnv abstractEnv e_salsa in + if Vars.cardinal free_vars > 0 then ( + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "Warning: unbounded free vars (%a) in expression %a. We do not \ + optimize it.@ " + Vars.pp + (Vars.fold + (fun v accu -> + let v' = + { + v with + LT.var_id = nodename.LT.node_id ^ "." ^ v.LT.var_id; + } + in + Vars.add v' accu) + free_vars + Vars.empty) + (MC.pp_val m) + (salsa_expr2value_t vars_env constEnv e_salsa)); + if !debug then + report ~level:2 (fun fmt -> + Format.fprintf fmt "Some free vars, not optimizing@ "); + if !debug then + report ~level:3 (fun fmt -> + Format.fprintf fmt " ranges: %a@ " RangesInt.pp ranges); + + (* if !debug then Log.report ~level:2 (fun fmt -> Format.fprintf fmt + "Formal env was @[<v 0>%a@]@ " FormalEnv.pp formalEnv); *) + let new_e = + try salsa_expr2value_t vars_env constEnv e_salsa + with Not_found -> assert false + in + new_e, None, [], Vars.empty) + else ( + if !debug then + report ~level:3 (fun fmt -> + Format.fprintf + fmt + "@[<v 2>Analyzing expression %a@ with ranges: @[<v>%a@ @]@ @]@ " + (C_backend_common.pp_c_val + m + "" + (C_backend_common.pp_c_var_read m)) + (salsa_expr2value_t vars_env constEnv e_salsa) + (Utils.fprintf_list ~sep:",@ " (fun fmt (l, r) -> + Format.fprintf fmt "%s -> %a" l FloatIntSalsa.pp r)) + abstractEnv); + + (* Slicing expression *) + let e_salsa, seq = + try + Salsa.Rewrite.sliceExpr + e_salsa + 0 + (Salsa.Types.Nop (Salsa.Types.Lab 0)) + with _ -> + Format.eprintf + "Issues rewriting express %s@.@?" + (Salsa.Print.printExpression e_salsa); + assert false + in + let def_tmps = Salsa.Utils.flatten_seq seq [] in + (* Registering tmp ids in vars_env *) + let vars_env', new_local_vars = + List.fold_left + (fun (vs, vars) (id, _) -> + let vdecl = + Corelang.mk_fresh_var + (nodename.node_id, []) + (* TODO check that the empty env is ok. One may need to build or + access to the current env *) + Location.dummy_loc + e.MT.value_type + (Clocks.new_var true) + in + + let vs' = VarEnv.add id { vdecl; is_local = true } vs in + let vars' = Vars.add vdecl vars in + vs', vars') + (vars_env, Vars.empty) + def_tmps + in + (* Debug *) + if !debug then + report ~level:3 (fun fmt -> + Format.fprintf + fmt + "List of slices: @[<v 0>%a@]@ " + (Utils.fprintf_list ~sep:"@ " (fun fmt (id, e_id) -> + Format.fprintf + fmt + "(%s,%a) -> %a" + id + Printers.pp_var + (get_var vars_env' id).vdecl + (C_backend_common.pp_c_val + m + "" + (C_backend_common.pp_c_var_read m)) + (salsa_expr2value_t vars_env' constEnv e_id))) + def_tmps; + Format.fprintf + fmt + "Sliced expression: %a@ " + (C_backend_common.pp_c_val + m + "" + (C_backend_common.pp_c_var_read m)) + (salsa_expr2value_t vars_env' constEnv e_salsa)); + + (* Debug *) + + (* Optimize def tmp, and build the associated instructions. Update the + abstract Env with computed ranges *) + if !debug && List.length def_tmps >= 1 then + report ~level:3 (fun fmt -> + Format.fprintf fmt "@[<v 3>Optimizing sliced sub-expressions@ "); + let rev_def_tmp_instrs, ranges = + List.fold_left + (fun (accu_instrs, ranges) (id, e_id) -> + (* Format.eprintf "Cleaning/Optimizing %s@." id; *) + let e_id', e_range = + (*Salsa.MainEPEG.transformExpression id e_id abstractEnv*) + opt_num_expr_sliced ranges e_id + in + let new_e_id' = + try salsa_expr2value_t vars_env' constEnv e_id' + with Not_found -> assert false + in + + let vdecl = (get_var vars_env' id).vdecl in + + let new_local_assign = + (* let expr = salsa_expr2value_t vars_env' constEnv e_id' in *) + MT.MLocalAssign (vdecl, new_e_id') + in + let new_local_assign = + { + MT.instr_desc = new_local_assign; + MT.lustre_eq = + None + (* could be Corelang.mkeq Location.dummy_loc + ([vdecl.LT.var_id], e_id) provided it is converted as + Lustre expression rather than a Machine code value *); + } + in + let new_ranges = + match e_range with + | None -> + ranges + | Some e_range -> + RangesInt.add_def ranges id e_range + in + new_local_assign :: accu_instrs, new_ranges) + ([], ranges) + def_tmps + in + if !debug && List.length def_tmps >= 1 then + report ~level:3 (fun fmt -> Format.fprintf fmt "@]@ "); + + (* Format.eprintf "Optimizing main expression %s@.AbstractEnv is %a" + (Salsa.Print.printExpression e_salsa) RangesInt.pp ranges; *) + let expr_salsa, expr_range = opt_num_expr_sliced ranges e_salsa in + let expr = + try salsa_expr2value_t vars_env' constEnv expr_salsa + with Not_found -> assert false + in + + expr, expr_range, List.rev rev_def_tmp_instrs, new_local_vars + (* ???? Bout de code dans unstable lors du merge avec salsa ? ==== + + let new_e = try salsa_expr2value_t vars_env' constEnv new_e_salsa with + Not_found -> assert false in if !debug then Log.report ~level:2 (fun + fmt -> let old_range = Salsa.Analyzer.evalExpr e_salsa abstractEnv [] + in match RangesInt.Value.leq old_range e_val, RangesInt.Value.leq e_val + old_range with | true, true -> Format.fprintf fmt "No improvement on + abstract value %a@ " RangesInt.pp_val e_val | true, false -> ( + Format.fprintf fmt "Improved!"; Format.fprintf fmt " @[<v>old_expr: + @[<v 0>%a@ range: %a@]@ new_expr: @[<v 0>%a@ range: %a@]@ @]@ " + (MC.pp_val m) e RangesInt.pp_val (Salsa.Analyzer.evalExpr e_salsa + abstractEnv []) (MC.pp_val m) new_e RangesInt.pp_val e_val ) | false, + true -> Format.eprintf "Error; new range is worse!@.@?"; assert false | + false, false -> Format.eprintf "Error; new range is not comparabe with + old end. This should not happen!@.@?"; assert false ); new_e, Some + e_val, List.rev rev_def_tmp_instrs with (* Not_found -> *) | + Salsa.Epeg_types.EPEGError _ -> ( Log.report ~level:2 (fun fmt -> + Format.fprintf fmt "BECAUSE OF AN ERROR, Expression %a was not + optimized@ " (MC.pp_val m) e); e, None, [] ) >>>>>>> unstable *)) + in + + opt_expr m vars_env ranges formalEnv e + +(* Returns a list of assign, for each var in vars_to_print, that produce the + definition of it according to formalEnv, and driven by the ranges. *) +let assign_vars nodename m constEnv vars_env printed_vars ranges formalEnv + vars_to_print = + (* We print thhe expression in the order of definition *) + let ordered_vars = + List.stable_sort + (FormalEnv.get_sort_fun formalEnv) + (Vars.elements vars_to_print) + in + if !debug then + report ~level:4 (fun fmt -> + Format.fprintf + fmt + "Printing vars in the following order: [%a]@ " + (Utils.fprintf_list ~sep:", " Printers.pp_var) + ordered_vars); + + List.fold_right + (fun v (accu_instr, accu_ranges, accu_new_locals) -> + if !debug then + report ~level:4 (fun fmt -> + Format.fprintf fmt "Printing assign for variable %s@ " v.LT.var_id); + try + (* Obtaining unfold expression of v in formalEnv *) + let v_def = FormalEnv.get_def formalEnv v in + let e, r, il, new_v_locals = + optimize_expr + nodename + m + constEnv + printed_vars + vars_env + ranges + formalEnv + v_def + in + let instr_desc = + if + try (get_var vars_env v.LT.var_id).is_local + with Not_found -> assert false + then MT.MLocalAssign (v, e) + else MT.MStateAssign (v, e) + in + ( il @ (Corelang.mkinstr instr_desc :: accu_instr), + (match r with + | None -> + ranges + | Some v_r -> + RangesInt.add_def ranges v.LT.var_id v_r), + Vars.union accu_new_locals new_v_locals ) + with FormalEnv.NoDefinition _ -> + if + (* It should not happen with C backend, but may happen with Lustre + backend *) + !Options.output = "lustre" + then accu_instr, ranges, Vars.empty + else ( + Format.eprintf "@?"; + assert false)) + ordered_vars + ([], ranges, Vars.empty) + +(* Main recursive function: modify the instructions list while preserving the + order of assigns for state variables. Returns a quintuple: (new_instrs, + ranges, formalEnv, printed_vars, and remaining vars to be printed) *) +let rec rewrite_instrs nodename m constEnv vars_env m instrs ranges formalEnv + printed_vars vars_to_print = + let formal_env_def = FormalEnv.def constEnv vars_env in + (* Format.eprintf "Rewrite intrs : [%a]@." MC.pp_instrs instrs; *) + let assign_vars = assign_vars nodename m constEnv vars_env in + (* if !debug then ( *) + (* Log.report ~level:3 (fun fmt -> Format.fprintf fmt *) + (* "Current printed_vars: [%a]@ Vars to print: [%a]@ Formal env is [%a]@ " *) + (* Vars.pp printed_vars *) + (* Vars.pp vars_to_print *) + (* FormalEnv.pp formalEnv) *) + (* ); *) + match instrs with + | [] -> + (* End of instruction list: we produce the definition of each variable that + appears in vars_to_print. Each of them should be defined in formalEnv *) + (* if !debug then Format.eprintf "Producing definitions %a@ " Vars.pp + vars_to_print; *) + let instrs, ranges', new_expr_locals = + assign_vars printed_vars ranges formalEnv vars_to_print + in + ( instrs, + ranges', + formalEnv, + Vars.union printed_vars vars_to_print, + (* We should have printed all required vars *) + [], + (* No more vars to be printed *) + Vars.empty ) + | hd_instr :: tl_instrs -> + (* We reformulate hd_instr, producing or not a fresh instruction, updating + formalEnv, possibly ranges and vars_to_print *) + let hd_instrs, ranges, formalEnv, printed_vars, vars_to_print, hd_new_locals + = + match Corelang.get_instr_desc hd_instr with + | MT.MLocalAssign (vd, vt) + when Types.is_real_type vd.LT.var_type + && not (Vars.mem vd vars_to_print) -> + (* LocalAssign are injected into formalEnv *) + (* if !debug then Format.eprintf "Registering local assign %a@ " + MC.pp_instr hd_instr; *) + (* if !debug then Format.eprintf "%a@ " MC.pp_instr hd_instr; *) + let formalEnv' = formal_env_def formalEnv vd vt in + (* formelEnv updated with vd = vt *) + ( [], + (* no instr generated *) + ranges, + (* no new range computed *) + formalEnv', + printed_vars, + (* no new printed vars *) + vars_to_print, + (* no more or less variables to print *) + Vars.empty ) + (* no new locals *) + | MT.MLocalAssign (vd, vt) + when Types.is_real_type vd.LT.var_type && Vars.mem vd vars_to_print -> + (* if !debug then Format.eprintf "Registering and producing state assign + %a@ " MC.pp_instr hd_instr; *) + (* if !debug then Format.eprintf "Registering and producing state assign + %a@ " MC.pp_instr hd_instr; *) + (* if !debug then ( *) + (* Format.eprintf "%a@ " MC.pp_instr hd_instr; *) + (* Format.eprintf "Selected var %a: producing expression@ " + Printers.pp_var vd; *) + (* ); *) + let formalEnv' = formal_env_def formalEnv vd vt in + (* formelEnv updated with vd = vt *) + let instrs', ranges', expr_new_locals = + (* printing vd = optimized vt *) + assign_vars printed_vars ranges formalEnv' (Vars.singleton vd) + in + ( instrs', + ranges', + (* no new range computed *) + formalEnv', + (* formelEnv already updated *) + Vars.add vd printed_vars, + (* adding vd to new printed vars *) + Vars.remove vd vars_to_print, + (* removed vd from variables to print *) + expr_new_locals ) + (* adding sliced vardecl to the list *) + | MT.MStateAssign (vd, vt) + when Types.is_real_type vd.LT.var_type + (* && Vars.mem vd vars_to_print *) -> + (* StateAssign are produced since they are required by the function. We + still keep their definition in the formalEnv in case it can optimize + later outputs. vd is removed from remaining vars_to_print *) + (* if !debug then Format.eprintf "Registering and producing state assign + %a@ " MC.pp_instr hd_instr; *) + (* if !debug then ( *) + (* Format.eprintf "%a@ " MC.pp_instr hd_instr; *) + (* Format.eprintf "State assign %a: producing expression@ " + Printers.pp_var vd; *) + (* ); *) + let formalEnv' = formal_env_def formalEnv vd vt in + (* formelEnv updated with vd = vt *) + let instrs', ranges', expr_new_locals = + (* printing vd = optimized vt *) + assign_vars printed_vars ranges formalEnv' (Vars.singleton vd) + in + ( instrs', + ranges', + (* no new range computed *) + formalEnv, + (* formelEnv already updated *) + Vars.add vd printed_vars, + (* adding vd to new printed vars *) + Vars.remove vd vars_to_print, + (* removed vd from variables to print *) + expr_new_locals ) + (* adding sliced vardecl to the list *) + | MT.MLocalAssign (vd, vt) | MT.MStateAssign (vd, vt) -> + (* Format.eprintf "other assign %a@." MC.pp_instr hd_instr; *) + + (* We have to produce the instruction. But we may have to produce as + well its dependencies *) + let required_vars = get_expr_real_vars vt in + let required_vars = Vars.diff required_vars printed_vars in + (* remove already produced variables *) + (* Format.eprintf "Required vars: %a@." Vars.pp required_vars; *) + let required_vars = + Vars.diff required_vars (Vars.of_list m.MT.mmemory) + in + let prefix_instr, ranges, new_expr_dep_locals = + assign_vars printed_vars ranges formalEnv required_vars + in + + let vt', _, il, expr_new_locals = + optimize_expr + nodename + m + constEnv + (Vars.union required_vars printed_vars) + vars_env + ranges + formalEnv + vt + in + let new_instr = + match Corelang.get_instr_desc hd_instr with + | MT.MLocalAssign _ -> + Corelang.update_instr_desc hd_instr (MT.MLocalAssign (vd, vt')) + | _ -> + Corelang.update_instr_desc hd_instr (MT.MStateAssign (vd, vt')) + in + let written_vars = Vars.add vd required_vars in + ( prefix_instr @ il @ [ new_instr ], + ranges, + (* no new range computed *) + formalEnv, + (* formelEnv untouched *) + Vars.union written_vars printed_vars, + (* adding vd + dependencies to new printed vars *) + Vars.diff vars_to_print written_vars, + (* removed vd + dependencies from variables to print *) + Vars.union new_expr_dep_locals expr_new_locals ) + | MT.MStep (vdl, id, vtl) -> + (* Format.eprintf "step@."; *) + + (* if !debug then Format.eprintf "Call to a node %a@ " MC.pp_instr + hd_instr; *) + (* Call of an external function. Input expressions have to be optimized, + their free variables produced. A fresh range has to be computed for + each output variable in vdl. Output of the function call are removed + from vars to be printed *) + let node = called_node_id m id in + let node_id = Corelang.node_name node in + let tin, tout = + (* special care for arrow *) + if node_id = "_arrow" then + match vdl with + | [ v ] -> + let t = v.LT.var_type in + [ t; t ], [ t ] + | _ -> + assert false (* should not happen *) + else fun_types node + in + (* if !debug then Format.eprintf "@[<v 2>... optimizing arguments@ "; *) + let vtl', vtl_ranges, il, args_new_locals = + List.fold_right2 + (fun e typ_e (exprl, range_l, il_l, new_locals) -> + if Types.is_real_type typ_e then + let e', r', il, new_expr_locals = + optimize_expr + nodename + m + constEnv + printed_vars + vars_env + ranges + formalEnv + e + in + ( e' :: exprl, + r' :: range_l, + il @ il_l, + Vars.union new_locals new_expr_locals ) + else e :: exprl, None :: range_l, il_l, new_locals) + vtl + tin + ([], [], [], Vars.empty) + in + + (* if !debug then Format.eprintf "... done@ @]@ "; *) + + (* let required_vars = *) + (* List.fold_left2 *) + (* (fun accu e typ_e -> *) + (* if Types.is_real_type typ_e then *) + (* Vars.union accu (get_expr_real_vars e) *) + (* else (\* we do not consider non real expressions *\) *) + (* accu *) + (* ) *) + (* Vars.empty *) + (* vtl' tin *) + (* in *) + (* if !debug then Format.eprintf "Required vars: [%a]@ Printed vars: + [%a]@ Remaining required vars: [%a]@ " *) + (* Vars.pp required_vars *) + (* Vars.pp printed_vars *) + (* Vars.pp (Vars.diff required_vars printed_vars) *) + (* ; *) + (* let required_vars = Vars.diff required_vars printed_vars in (\* remove *) + (* already *) + (* produced *) + (* variables *\) *) + (* let written_vars = Vars.union required_vars (Vars.of_list vdl) in *) + (* let instrs', ranges' = assign_vars (Vars.union written_vars + printed_vars) ranges formalEnv required_vars in *) + + (* instrs' @ [Corelang.update_instr_desc hd_instr + (MT.MStep(vdl,id,vtl'))], (* New instrs *) *) + let written_vars = Vars.of_list vdl in + + ( il @ [ Corelang.update_instr_desc hd_instr (MT.MStep (vdl, id, vtl')) ], + (* New instrs *) + RangesInt.add_call ranges vdl id vtl_ranges, + (* add information bounding each vdl var *) + formalEnv, + Vars.union written_vars printed_vars, + (* adding vdl to new printed vars *) + Vars.diff vars_to_print written_vars, + args_new_locals ) + | MT.MBranch (vt, branches) -> + (* Required variables to compute vt are introduced. Then each branch is + refactored specifically *) + + (* if !debug then Format.eprintf "Branching %a@ " MC.pp_instr + hd_instr; *) + let required_vars = get_expr_real_vars vt in + let required_vars = Vars.diff required_vars printed_vars in + (* remove already produced variables *) + let vt', _, prefix_instr, prefix_new_locals = + optimize_expr + nodename + m + constEnv + printed_vars + vars_env + ranges + formalEnv + vt + in + + let new_locals = prefix_new_locals in + + (* let prefix_instr, ranges = *) + (* assign_vars (Vars.union required_vars printed_vars) ranges formalEnv + required_vars in *) + let printed_vars = Vars.union printed_vars required_vars in + + let read_vars_tl = get_read_vars tl_instrs in + (* if !debug then Format.eprintf "@[<v 2>Dealing with branches@ "; *) + let branches', written_vars, merged_ranges, new_locals = + List.fold_right + (fun (b_l, b_instrs) + (new_branches, written_vars, merged_ranges, new_locals) -> + let b_write_vars = get_written_vars b_instrs in + let b_vars_to_print = + Vars.inter b_write_vars (Vars.union read_vars_tl vars_to_print) + in + let b_fe = formalEnv in + (* because of side effect data, we copy it for each branch *) + let ( b_instrs', + b_ranges, + b_formalEnv, + b_printed, + b_vars, + b_new_locals ) = + rewrite_instrs + nodename + m + constEnv + vars_env + m + b_instrs + ranges + b_fe + printed_vars + b_vars_to_print + in + (* b_vars should be empty *) + let _ = if b_vars != [] then assert false in + + (* Producing the refactored branch *) + ( (b_l, b_instrs') :: new_branches, + Vars.union b_printed written_vars, + (* They should coincides. We use union instead of inter to ease + the bootstrap *) + RangesInt.merge merged_ranges b_ranges, + Vars.union new_locals b_new_locals )) + branches + ([], required_vars, ranges, new_locals) + in + (* if !debug then Format.eprintf "dealing with branches done@ @]@ "; *) + ( prefix_instr + @ [ + Corelang.update_instr_desc hd_instr (MT.MBranch (vt', branches')); + ], + merged_ranges, + (* Only step functions call within branches may have produced new + ranges. We merge this data by computing the join per variable *) + formalEnv, + (* Thanks to the computation of var_to_print in each branch, no new + definition should have been computed without being already + printed *) + Vars.union written_vars printed_vars, + Vars.diff vars_to_print written_vars + (* We remove vars that have been produced within branches *), + new_locals ) + | MT.MReset _ | MT.MNoReset _ | MT.MSpec _ | MT.MComment _ -> + (* if !debug then Format.eprintf "Untouched %a (non real)@ " MC.pp_instr + hd_instr; *) + + (* Untouched instruction *) + ( [ hd_instr ], + (* unmodified instr *) + ranges, + (* no new range computed *) + formalEnv, + (* no formelEnv update *) + printed_vars, + vars_to_print, + (* no more or less variables to print *) + Vars.empty ) + (* no new slides vars *) + in + + let tl_instrs, ranges, formalEnv, printed_vars, vars_to_print, tl_new_locals + = + rewrite_instrs + nodename + m + constEnv + vars_env + m + tl_instrs + ranges + formalEnv + printed_vars + vars_to_print + in + + ( hd_instrs @ tl_instrs, + ranges, + formalEnv, + printed_vars, + vars_to_print, + Vars.union hd_new_locals tl_new_locals ) + +(* TODO: deal with new variables, ie. tmp *) +let salsaStep constEnv m s = + let ranges = + RangesInt.empty + (* empty for the moment, should be build from machine annotations or + externally provided information *) + in + let annots = + List.fold_left + (fun accu annl -> + List.fold_left + (fun accu (key, range) -> + match key with + | [ "salsa"; "ranges"; var ] -> + (var, range) :: accu + | _ -> + accu) + accu + annl.LT.annots) + [] + m.MT.mannot + in + let ranges = + List.fold_left + (fun ranges (v, value) -> + match value.LT.eexpr_qfexpr.LT.expr_desc with + | LT.Expr_tuple [ minv; maxv ] -> + let get_cst e = + match e.LT.expr_desc with + | LT.Expr_const (LT.Const_real r) -> + (* calculer la valeur c * 10^e *) + Real.to_num r + | _ -> + Format.eprintf + "Invalid salsa range: %a. It should be a pair of constant \ + floats and %a is not a float.@." + Printers.pp_expr + value.LT.eexpr_qfexpr + Printers.pp_expr + e; + assert false + in + (* let minv = Salsa.Float.Domain.of_num (get_cst minv) and *) + (* maxv = Salsa.Float.Domain.of_num (get_cst maxv) in *) + (* if !debug then Format.eprintf "variable %s in [%s, %s]@ " v + (Num.string_of_num minv) (Num.string_of_num maxv); *) + RangesInt.enlarge + ranges + v + (Salsa.Float.Domain.inject_nums (get_cst minv) (get_cst maxv)) + | _ -> + Format.eprintf + "Invalid salsa range: %a. It should be a pair of floats.@." + Printers.pp_expr + value.LT.eexpr_qfexpr; + assert false) + ranges + annots + in + let formal_env = FormalEnv.empty () in + let vars_to_print = + Vars.real_vars + (Vars.union (Vars.of_list m.MT.mmemory) (Vars.of_list s.MT.step_outputs)) + in + + (* TODO: should be at least step output + may be memories *) + let vars_env = compute_vars_env m in + (* if !debug then Format.eprintf "@[<v 2>Registering node equations@ "; *) + let new_instrs, _, _, printed_vars, _, new_locals = + rewrite_instrs + m.MT.mname + m + constEnv + vars_env + m + s.MT.step_instrs + ranges + formal_env + (Vars.real_vars + (Vars.of_list + s.MT.step_inputs + (* printed_vars : real inputs are considered as already printed *))) + vars_to_print + in + + let all_local_vars = Vars.real_vars (Vars.of_list s.MT.step_locals) in + let unused = Vars.diff all_local_vars printed_vars in + let locals = + if not (Vars.is_empty unused) then ( + if !debug then + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "Unused local vars: [%a]. Removing them.@ " + Vars.pp + unused); + List.filter (fun v -> not (Vars.mem v unused)) s.MT.step_locals) + else s.MT.step_locals + in + let locals = locals @ Vars.elements new_locals in + { s with MT.step_instrs = new_instrs; MT.step_locals = locals } +(* we have also to modify local variables to declare new vars *) + +let machine_t2machine_t_optimized_by_salsa constEnv mt = + try + if !debug then + report ~level:2 (fun fmt -> + Format.fprintf + fmt + "@[<v 3>Optimizing machine %s@ " + mt.MT.mname.LT.node_id); + let new_step = salsaStep constEnv mt mt.MT.mstep in + if !debug then report ~level:2 (fun fmt -> Format.fprintf fmt "@]@ "); + { mt with MT.mstep = new_step } + with FormalEnv.NoDefinition v as exp -> + Format.eprintf "No definition for variable %a@.@?" Printers.pp_var v; + raise exp + +(* Local Variables: *) +(* compile-command:"make -C ../../.." *) +(* End: *) diff --git a/src/plugins/salsa/machine_salsa_opt.mli b/src/plugins/salsa/machine_salsa_opt.mli new file mode 100644 index 00000000..e69de29b diff --git a/src/plugins/salsa/salsaDatatypes.ml b/src/plugins/salsa/salsaDatatypes.ml new file mode 100644 index 00000000..624e47ab --- /dev/null +++ b/src/plugins/salsa/salsaDatatypes.ml @@ -0,0 +1,440 @@ +module LT = Lustre_types +module MT = Machine_code_types +module MC = Machine_code_common +module ST = Salsa.Types + +let debug = ref false +let _ = Salsa.Prelude.sliceSize := 5 + +let pp_hash ~sep f fmt r = + Format.fprintf fmt "[@[<v>"; + Hashtbl.iter (fun k v -> Format.fprintf fmt "%t%s@ " (f k v) sep) r; + Format.fprintf fmt "]@]" + +module type VALUE = sig + type t + + val union : t -> t -> t + val pp : Format.formatter -> t -> unit + val leq : t -> t -> bool +end + +module Ranges = +functor + (Value : VALUE) + -> + struct + module Value = Value + + type t = Value.t + type r_t = (LT.ident, Value.t) Hashtbl.t + + let empty : r_t = Hashtbl.create 13 + + (* Look for def of node i with inputs living in vtl_ranges, reinforce ranges + to bound vdl: each output of node i *) + let add_call ranges vdl id vtl_ranges = ranges + (* TODO assert false. On est pas obligé de faire qqchose. On peut supposer + que les ranges sont donnés pour chaque noeud *) + + let pp fmt r = + if Hashtbl.length r = 0 then Format.fprintf fmt "empty" + else + pp_hash + ~sep:";" + (fun k v fmt -> Format.fprintf fmt "%s -> %a" k Value.pp v) + fmt + r + + let pp_val = Value.pp + + let add_def ranges name r = + (* Format.eprintf "%s: declare %a@." *) + (* x.LT.var_id *) + (* Value.pp r ; *) + let fresh = Hashtbl.copy ranges in + Hashtbl.add fresh name r; + fresh + + let enlarge ranges name r = + let fresh = Hashtbl.copy ranges in + if Hashtbl.mem fresh name then + Hashtbl.replace fresh name (Value.union r (Hashtbl.find fresh name)) + else Hashtbl.add fresh name r; + fresh + + (* Compute a join per variable *) + let merge ranges1 ranges2 = + (* Format.eprintf "Mergeing rangesint %a with %a@." pp ranges1 pp + ranges2; *) + let ranges = Hashtbl.copy ranges1 in + Hashtbl.iter + (fun k v -> + if Hashtbl.mem ranges k then + (* Format.eprintf "%s: %a union %a = %a@." *) + (* k *) + (* Value.pp v *) + (* Value.pp (Hashtbl.find ranges k) *) + (* Value.pp (Value.union v (Hashtbl.find ranges k)); *) + Hashtbl.replace ranges k (Value.union v (Hashtbl.find ranges k)) + else Hashtbl.add ranges k v) + ranges2; + (* Format.eprintf "Merge result %a@." pp ranges; *) + ranges + + let to_abstract_env ranges = + Hashtbl.fold (fun id value accu -> (id, value) :: accu) ranges [] + end + +module FloatIntSalsa = struct + type t = ST.abstractValue + + let pp fmt ((f, r) : t) = + let fs, rs = Salsa.Float.Domain.print (f, r) in + Format.fprintf fmt "%s + %s" fs rs + + (* match f, r with | ST.I(a,b), ST.J(c,d) -> Format.fprintf fmt "[%f, %f] + + [%s, %s]" a b (Num.string_of_num c) (Num.string_of_num d) | ST.I(a,b), + ST.JInfty -> Format.fprintf fmt "[%f, %f] + oo" a b | ST.Empty, _ -> + Format.fprintf fmt "???" + + | _ -> assert false *) + let union v1 v2 = Salsa.Float.Domain.join v1 v2 + + (* match v1, v2 with |(ST.I(x1, x2), ST.J(y1, y2)), (ST.I(x1', x2'), ST.J(y1', + y2')) -> ST.(I(min x1 x1', max x2 x2'), J(min y1 y1', max y2 y2')) | _ -> + Format.eprintf "%a cup %a failed@.@?" pp v1 pp v2; assert false *) + let inject cst = + match cst with + | LT.Const_int i -> + Salsa.Builder.mk_cst (Salsa.Float.Domain.inject_int i) + | LT.Const_real r -> + (* TODO: this is incorrect. We should rather compute the error associated + to the float *) + (* let f = float_of_string s in *) + let n = Real.to_q r in + Salsa.Builder.mk_cst (Salsa.Float.Domain.inject_q n) + (* let r = Salsa.Prelude.r_of_f_aux r in *) + (* Salsa.Builder.mk_cst (Float.Domain.nnew r r) *) + + (* let r = float_of_string s in *) + (* if r = 0. then *) + (* Salsa.Builder.mk_cst (ST.R(-. min_float, min_float),Float.ulp (ST.R(-. + min_float, min_float))) *) + (* else *) + (* Salsa.Builder.mk_cst + (ST.I(r*.(1.-.epsilon_float),r*.(1.+.epsilon_float)),Float.ulp + (ST.I(r,r))) *) + | _ -> + assert false + + let leq = + (* Salsa.Float.feSseq *) + Salsa.Float.Domain.leq +end + +module RangesInt = Ranges (FloatIntSalsa) + +module Vars = struct + module VarSet = Set.Make (struct + type t = LT.var_decl + + let compare x y = compare x.LT.var_id y.LT.var_id + end) + + let real_vars vs = + VarSet.filter (fun v -> Types.is_real_type v.LT.var_type) vs + + let of_list = List.fold_left (fun s e -> VarSet.add e s) VarSet.empty + + include VarSet + + let remove_list (set : t) (v_list : elt list) : t = + List.fold_right VarSet.remove v_list set + + let pp fmt vs = + Utils.fprintf_list ~sep:", " Printers.pp_var fmt (VarSet.elements vs) +end + +(*************************************************************************************) +(* Converting values back and forth *) +(*************************************************************************************) + +let rec value_t2salsa_expr constEnv vt = + let value_t2salsa_expr = value_t2salsa_expr constEnv in + let res = + match vt.MT.value_desc with + (* | LT.Cst(LT.Const_tag(t) as c) -> *) + (* Format.eprintf "v2s: cst tag@."; *) + (* if List.mem_assoc t constEnv then ( *) + (* Format.eprintf "trouvé la constante %s: %a@ " t Printers.pp_const c; *) + (* FloatIntSalsa.inject (List.assoc t constEnv) *) + (* ) *) + (* else ( *) + (* Format.eprintf "Const tag %s unhandled@.@?" t ; *) + (* raise (Salsa.Prelude.Error ("Entschuldigung6, constant tag not yet + implemented")) *) + (* ) *) + | MT.Cst cst -> + (* Format.eprintf "v2s: cst tag 2: %a@." Printers.pp_const cst; *) + FloatIntSalsa.inject cst + | MT.Var v -> + (* Format.eprintf "v2s: var %s@." v.LT.var_id; *) + let sel_fun (vname, _) = v.LT.var_id = vname in + if List.exists sel_fun constEnv then + let _, cst = List.find sel_fun constEnv in + FloatIntSalsa.inject cst + else + let id = v.LT.var_id in + Salsa.Builder.mk_id id + | MT.Fun (binop, [ x; y ]) -> + let salsaX = value_t2salsa_expr x in + let salsaY = value_t2salsa_expr y in + let op = + let pred f x y = Salsa.Builder.mk_int_of_bool (f x y) in + match binop with + | "+" -> + Salsa.Builder.mk_plus + | "-" -> + Salsa.Builder.mk_minus + | "*" -> + Salsa.Builder.mk_times + | "/" -> + Salsa.Builder.mk_div + | "=" -> + pred Salsa.Builder.mk_eq + | "<" -> + pred Salsa.Builder.mk_lt + | ">" -> + pred Salsa.Builder.mk_gt + | "<=" -> + pred Salsa.Builder.mk_lte + | ">=" -> + pred Salsa.Builder.mk_gte + | _ -> + assert false + in + op salsaX salsaY + | MT.Fun (unop, [ x ]) -> + let salsaX = value_t2salsa_expr x in + Salsa.Builder.mk_uminus salsaX + | MT.Fun (f, _) -> + raise + (Salsa.Prelude.Error + ("Unhandled function " ^ f ^ " in conversion to salsa expression")) + | MT.Array _ | MT.Access _ | MT.Power _ -> + raise + (Salsa.Prelude.Error + "Unhandled construct in conversion to salsa expression") + in + (* if debug then *) + (* Format.eprintf "value_t2salsa_expr: %a -> %a@ " *) + (* MC.pp_val vt *) + (* (fun fmt x -> Format.fprintf fmt "%s" (Salsa.Print.printExpression x)) res; *) + res + +type var_decl = { vdecl : LT.var_decl; is_local : bool } + +module VarEnv = Map.Make (struct + type t = LT.ident + + let compare = compare +end) + +(* let is_local_var vars_env v = *) +(* try *) +(* (VarEnv.find v vars_env).is_local *) +(* with Not_found -> Format.eprintf "Impossible to find var %s@.@?" v; assert + false *) + +let get_var vars_env v = + try VarEnv.find v vars_env + with Not_found -> + Format.eprintf + "Impossible to find var %s in var env %a@ " + v + (Utils.fprintf_list ~sep:", " (fun fmt (id, _) -> + Format.pp_print_string fmt id)) + (VarEnv.bindings vars_env); + assert false + +let compute_vars_env m = + let env = VarEnv.empty in + let env = + List.fold_left + (fun accu v -> + VarEnv.add v.LT.var_id { vdecl = v; is_local = false } accu) + env + m.MT.mmemory + in + let env = + List.fold_left + (fun accu v -> VarEnv.add v.LT.var_id { vdecl = v; is_local = true } accu) + env + MC.( + m.MT.mstep.MT.step_inputs @ m.MT.mstep.MT.step_outputs + @ m.MT.mstep.MT.step_locals) + in + env + +let rec salsa_expr2value_t vars_env cst_env e = + (* let e = Float.evalPartExpr e [] [] in *) + let salsa_expr2value_t = salsa_expr2value_t vars_env cst_env in + let binop op e1 e2 t = + let x = salsa_expr2value_t e1 in + let y = salsa_expr2value_t e2 in + MC.mk_val (MT.Fun (op, [ x; y ])) t + in + match e with + | ST.Cst (abs_val, _) -> + (* We project ranges into constants. We forget about errors and provide the + mean/middle value of the interval *) + let new_float = Salsa.Float.Domain.to_float abs_val in + (* let new_float = Salsa.NumMartel.float_of_num c in *) + (* let new_float = *) + (* if f1 = f2 then *) + (* f1 *) + (* else *) + (* (f1 +. f2) /. 2.0 *) + (* in *) + (* Log.report ~level:3 *) + (* (fun fmt -> Format.fprintf fmt "projecting [%.45f, %.45f] -> %.45f@ " f1 + f2 new_float); *) + let cst = + let s = + if new_float = 0. then "0." + else + (* We have to convert it into our format: int * int * real *) + (* string_of_float new_float *) + let _ = Format.flush_str_formatter () in + Format.fprintf Format.str_formatter "%.11f" new_float; + Format.flush_str_formatter () + in + Parser_lustre.signed_const Lexer_lustre.token (Lexing.from_string s) + in + MC.mk_val (MT.Cst cst) Type_predef.type_real + | ST.Id (id, _) -> + (* Format.eprintf "Looking for id=%s@.@?" id; *) + if List.mem_assoc id cst_env then + let cst = List.assoc id cst_env in + (* Format.eprintf "Found cst = %a@.@?" Printers.pp_const cst; *) + MC.mk_val (MT.Cst cst) Type_predef.type_real + else + (* if is_const salsa_label then *) + (* MC.Cst(LT.Const_tag(get_const salsa_label)) *) + (* else *) + let var_id = try get_var vars_env id with Not_found -> assert false in + MC.mk_val (MT.Var var_id.vdecl) var_id.vdecl.LT.var_type + | ST.Plus (x, y, _) -> + binop "+" x y Type_predef.type_real + | ST.Minus (x, y, _) -> + binop "-" x y Type_predef.type_real + | ST.Times (x, y, _) -> + binop "*" x y Type_predef.type_real + | ST.Div (x, y, _) -> + binop "/" x y Type_predef.type_real + | ST.Uminus (x, _) -> + let x = salsa_expr2value_t x in + MC.mk_val (MT.Fun ("uminus", [ x ])) Type_predef.type_real + | ST.IntOfBool (ST.Eq (x, y, _), _) -> + binop "=" x y Type_predef.type_bool + | ST.IntOfBool (ST.Lt (x, y, _), _) -> + binop "<" x y Type_predef.type_bool + | ST.IntOfBool (ST.Gt (x, y, _), _) -> + binop ">" x y Type_predef.type_bool + | ST.IntOfBool (ST.Lte (x, y, _), _) -> + binop "<=" x y Type_predef.type_bool + | ST.IntOfBool (ST.Gte (x, y, _), _) -> + binop ">=" x y Type_predef.type_bool + | _ -> + raise + (Salsa.Prelude.Error + "Entschuldigung, salsaExpr2value_t case not yet implemented") + +let rec get_salsa_free_vars vars_env constEnv absenv e = + let f = get_salsa_free_vars vars_env constEnv absenv in + match e with + | ST.Id (id, _) -> + if (not (List.mem_assoc id absenv)) && not (List.mem_assoc id constEnv) then + Vars.singleton + (try VarEnv.find id vars_env with Not_found -> assert false).vdecl + else Vars.empty + | ST.Plus (x, y, _) + | ST.Minus (x, y, _) + | ST.Times (x, y, _) + | ST.Div (x, y, _) + | ST.IntOfBool (ST.Eq (x, y, _), _) + | ST.IntOfBool (ST.Lt (x, y, _), _) + | ST.IntOfBool (ST.Gt (x, y, _), _) + | ST.IntOfBool (ST.Lte (x, y, _), _) + | ST.IntOfBool (ST.Gte (x, y, _), _) -> + Vars.union (f x) (f y) + | ST.Uminus (x, _) -> + f x + | ST.Cst _ -> + Vars.empty + | _ -> + assert false + +module FormalEnv = struct + type fe_t = (LT.ident, int * MT.value_t) Hashtbl.t + + let cpt = ref 0 + + exception NoDefinition of LT.var_decl + + (* Returns the expression associated to v in env *) + let get_def (env : fe_t) v = + try snd (Hashtbl.find env v.LT.var_id) + with Not_found -> raise (NoDefinition v) + + let fold f = Hashtbl.fold (fun k (_, v) accu -> f k v accu) + + let to_salsa constEnv formalEnv = + fold + (fun id expr accu -> (id, value_t2salsa_expr constEnv expr) :: accu) + formalEnv + [] + + let def constEnv vars_env (env : fe_t) d expr = + incr cpt; + let fresh = Hashtbl.copy env in + let expr_salsa = value_t2salsa_expr constEnv expr in + let salsa_env = to_salsa constEnv env in + let expr_salsa, _ = Salsa.Rewrite.substVars expr_salsa salsa_env 0 in + let expr_salsa = + Salsa.Analyzer.evalPartExpr + expr_salsa + salsa_env + [] + (* no blacklisted vars *) + [] + (*no arrays *) + in + let expr_lustrec = salsa_expr2value_t vars_env [] expr_salsa in + Hashtbl.add fresh d.LT.var_id (!cpt, expr_lustrec); + fresh + + let empty () : fe_t = Hashtbl.create 13 + + let pp m fmt env = + pp_hash + ~sep:";@ " + (fun k (_, v) fmt -> Format.fprintf fmt "%s -> %a" k (MC.pp_val m) v) + fmt + env + + let get_sort_fun env = + let order = Hashtbl.fold (fun k (cpt, _) accu -> (k, cpt) :: accu) env [] in + fun v1 v2 -> + if List.mem_assoc v1.LT.var_id order && List.mem_assoc v2.LT.var_id order + then + if List.assoc v1.LT.var_id order <= List.assoc v2.LT.var_id order then + -1 + else 1 + else assert false +end + +(* Local Variables: *) +(* compile-command:"make -C ../../.." *) +(* End: *) diff --git a/src/plugins/salsa/salsaDatatypes.mli b/src/plugins/salsa/salsaDatatypes.mli new file mode 100644 index 00000000..e69de29b diff --git a/src/plugins/salsa/salsa_plugin.ml b/src/plugins/salsa/salsa_plugin.ml new file mode 100644 index 00000000..92e24712 --- /dev/null +++ b/src/plugins/salsa/salsa_plugin.ml @@ -0,0 +1,60 @@ +open Format +open Lustre_types + +let salsa_enabled = ref false + +(* "-salsa", Arg.Set salsa_enabled, "activate Salsa optimization <default>"; *) +(* "-no-salsa", Arg.Clear salsa_enabled, "deactivate Salsa optimization"; *) + +module Plugin : PluginType.S = struct + include PluginType.Default + + let name = "salsa" + + let options = + [ + "-debug", Arg.Set SalsaDatatypes.debug, "debug salsa plugin"; + ( "-verbose", + Arg.Set_int Salsa.Log.verbose_level, + "salsa plugin verbose level (default is 0)" ); + ( "-slice-depth", + Arg.Set_int Salsa.Prelude.sliceSize, + "salsa slice depth (default is 5)" ); + "-disable", Arg.Clear salsa_enabled, "disable salsa"; + ] + + let activate () = salsa_enabled := true + + let init () = + if !salsa_enabled then if !SalsaDatatypes.debug then Salsa.Log.debug := true + + let refine_machine_code prog machine_code = + if !salsa_enabled then ( + Compiler_common.check_main (); + Log.report ~level:1 (fun fmt -> + fprintf fmt ".. @[<v 0>salsa machines optimization@ "); + (* Selecting float constants for Salsa *) + let constEnv = + List.fold_left + (fun accu c_topdecl -> + match c_topdecl.top_decl_desc with + | Const c when Types.is_real_type c.const_type -> + (c.const_id, c.const_value) :: accu + | _ -> + accu) + [] + (Corelang.get_consts prog) + in + let res = + List.map + (Machine_salsa_opt.machine_t2machine_t_optimized_by_salsa constEnv) + machine_code + in + Log.report ~level:1 (fun fmt -> fprintf fmt "@]@ "); + res) + else machine_code +end + +let () = + PluginList.registered := + (module Plugin : PluginType.S) :: !PluginList.registered diff --git a/src/plugins/salsa/salsa_plugin.mli b/src/plugins/salsa/salsa_plugin.mli new file mode 100644 index 00000000..e69de29b diff --git a/src/plugins/scopes/scopes.ml b/src/plugins/scopes/scopes.ml new file mode 100644 index 00000000..3c8785a6 --- /dev/null +++ b/src/plugins/scopes/scopes.ml @@ -0,0 +1,468 @@ +open Utils +open Lustre_types +open Corelang +open Machine_code_types +open Machine_code_common + +(* (variable, node name, node instance) *) +type scope_t = (var_decl * string * string option) list * var_decl + +(* Scope to string list *) +let scope_to_sl ((sl, v) : scope_t) : string list = + List.fold_right + (fun (v, nodename, _) accu -> v.var_id :: nodename :: accu) + sl + [ v.var_id ] + +let rec compute_scopes ?(first = true) prog root_node : scope_t list = + let compute_scopes = compute_scopes ~first:false in + (* Format.eprintf "Compute scope of %s@." main_node; *) + try + let node = get_node root_node prog in + let locals = List.map fst node.node_locals in + let all_vars = node.node_inputs @ locals @ node.node_outputs in + let local_vars = if first then locals else node.node_inputs @ locals in + let local_scopes = List.map (fun x -> [], x) local_vars in + let sub_scopes = + let sub_nodes = + List.fold_left + (fun res s -> + match s with + | Eq + ({ eq_rhs = { expr_desc = Expr_appl (nodeid, _, _); _ }; _ } as + eq) -> ( + (* Obtaining the var_del associated to the first var of eq_lhs *) + try + let query v = v.var_id = List.hd eq.eq_lhs in + let vid = List.find query all_vars in + (nodeid, vid) :: res + with Not_found -> + Format.eprintf + "eq=%a@.local_vars=%a@." + Printers.pp_node_eq + eq + (Format.pp_comma_list Printers.pp_var) + local_vars; + assert false) + | Eq _ -> + res + | _ -> + assert false + (* TODO deal with Automaton *)) + [] + node.node_stmts + in + List.map + (fun (nodeid, vid) -> + let scopes = compute_scopes prog nodeid in + List.map (fun (sl, v) -> (vid, nodeid, None) :: sl, v) scopes + (* instances are not yet known, hence the None *)) + sub_nodes + in + local_scopes @ List.flatten sub_scopes + with Not_found -> [] + +let pp_scopes = + Format.( + pp_print_list (fun fmt ((_, v) as s) -> + fprintf + fmt + "%a: %a" + (pp_print_list + ~pp_sep:(fun fmt () -> pp_print_string fmt ".") + pp_print_string) + (scope_to_sl s) + Types.pp + v.var_type)) + +(* let pp_path fmt p = *) +(* Utils.fprintf_list ~sep:"." (fun fmt (id, _) -> Format.pp_print_string fmt + id) fmt p *) + +let get_node_vdecl_of_name name node = + try + List.find + (fun v -> v.var_id = name) + (node.node_inputs @ node.node_outputs @ List.map fst node.node_locals) + with Not_found -> + Format.eprintf "Cannot find variable %s in node %s@." name node.node_id; + assert false + +let rec get_path prog machines node id_list accu = + let get_path = get_path prog machines in + match id_list, accu with + | [ flow ], [] -> + (* Special treatment of first level flow: node is here main_node *) + let flow_var = get_node_vdecl_of_name flow node in + [], flow_var, node.node_id + | [ id ], (_, last_node, _) :: _ -> + (* last item, it should denote a local memory variable (local var, memory or + input *) + let id_vdecl = get_node_vdecl_of_name id (get_node last_node prog) in + List.rev accu, id_vdecl, last_node + | varid :: nodename :: id_list_tl, _ -> ( + let e_machine = get_machine machines node.node_id in + (* Format.eprintf "Looking for def %s in call %s in machine %a@." *) + (* varid nodename *) + (* Machine_code.pp_machine e_machine; *) + let find_var v = v.var_id = varid in + let instance = + List.find + (fun i -> + match get_instr_desc i with + | MStep (p, _, _) -> + List.exists find_var p + | _ -> + false) + e_machine.mstep.step_instrs + in + try + let variable, instance_node, instance_id = + match get_instr_desc instance with + | MStep (p, o, _) -> + (* Format.eprintf "Looking for machine %s@.@?" o; *) + let o_fun, _ = List.assoc o e_machine.mcalls in + if node_name o_fun = nodename then List.hd p, o_fun, o + else assert false + | _ -> + assert false + in + let next_node = node_of_top instance_node in + let accu = (variable, nodename, Some instance_id) :: accu in + (* Format.eprintf "Calling get path on %s@.@?" next_node.node_id; *) + get_path next_node id_list_tl accu + with Not_found -> + Format.eprintf "toto@."; + assert false) + | _ -> + assert false + +let check_scope all_scopes = + let all_scopes_as_sl = List.map scope_to_sl all_scopes in + fun prog machines main_node_name sl -> + if not (List.mem sl all_scopes_as_sl) then ( + Format.eprintf "%s is an invalid scope.@." (String.concat "." sl); + exit 1) + else + (* Format.eprintf "@.@.Required path: %s@." (String.concat "." sl) ; *) + let main_node = get_node main_node_name prog in + let path, flow, mid = get_path prog machines main_node sl [] in + (* Format.eprintf "computed path: %a.%s@." pp_path path flow.var_id; *) + path, flow, mid + +(* Build the two maps - (scope_name, variable) - (machine_name, list of selected + variables) *) +let check_scopes main_node_name prog machines all_scopes scopes = + let check_scope = check_scope all_scopes prog machines in + List.fold_left + (fun (accu_sl, accu_m) sl -> + let path, flow, mid = check_scope main_node_name sl in + let accu_sl = (sl, (path, flow)) :: accu_sl in + let accu_m = + let flow_id = flow.var_id in + if List.mem_assoc mid accu_m then + (mid, flow_id :: List.assoc mid accu_m) + :: List.remove_assoc mid accu_m + else (mid, [ flow_id ]) :: accu_m + in + accu_sl, accu_m) + ([], []) + scopes + +let scope_var_name vid = vid ^ "__scope" + +(**********************************************************************) +(* The following three functions are used in the main function to print the + value of the new memories, storing scopes values *) +(**********************************************************************) + +(* TODO: recuperer le type de "flow" et appeler le print correspondant iterer + sur path pour construire la suite des xx_mem._reg.yy_mem._reg......flow par + ex main_mem->n8->n9->_reg.flow *) +let extract_scopes_defs scopes = + let rec scope_path_name (path, flow) accu = + match path with + | [] -> + accu ^ "_reg." ^ scope_var_name flow.var_id, flow + | (_, _, Some instance_id) :: tl -> + scope_path_name (tl, flow) (accu ^ instance_id ^ "->") + | _ -> + assert false + in + List.map + (fun (sl, scope) -> String.concat "." sl, scope_path_name scope "main_mem.") + scopes + +let pp_scopes_files _basename _mname fmt scopes = + let scopes_vars = extract_scopes_defs scopes in + List.iteri + (fun idx _ (*(id, (var_path, var))*) -> + C_backend_common.pp_file_decl fmt "out_scopes" idx) + scopes_vars; + Format.fprintf fmt "@[<v 2>if (traces) {@ "; + List.iteri + (fun idx (id, (_, var)) -> + let file = C_backend_common.pp_file_open fmt "out_scopes" idx in + Format.fprintf fmt "fprintf(%s, \"# scope: %s\\n\");@ " file id; + Format.fprintf + fmt + "fprintf(%s, \"# node: %s\\n\");@ " + file + (Utils.desome var.var_parent_nodeid); + Format.fprintf fmt "fprintf(%s, \"# variable: %s\\n\");@ " file var.var_id) + scopes_vars; + Format.fprintf fmt "@]}@ " + +let pp_full_scopes fmt scopes = + let scopes_vars = extract_scopes_defs scopes in + List.iteri + (fun idx (id, (var_path, var)) -> + Format.fprintf fmt "@ %t;" (fun fmt -> + C_backend_common.pp_put_var + fmt + ("_scopes" ^ string_of_int (idx + 1)) + id + (*var*) var.var_type + var_path)) + scopes_vars + +(**********************************************************************) + +let update_machine main_node machine scopes = + let stateassign (vdecl_mem, vdecl_orig) = + mkinstr + (MStateAssign (vdecl_mem, mk_val (Var vdecl_orig) vdecl_orig.var_type)) + in + let selection = + (* We only register inputs for non root node *) + (if machine.mname.node_id = main_node then [] + else machine.mstep.step_inputs) + (* @ machine.mstep.step_outputs *) + @ List.map fst machine.mmemory + @ machine.mstep.step_locals + in + let selection = + List.filter + (fun v -> List.exists (fun vid -> vid = v.var_id) scopes) + selection + in + let new_mems = + List.map + (fun v -> + (* We could copy the variable but then we need to update its type let + new_v = copy_var_decl v in *) + let new_v = { v with var_id = scope_var_name v.var_id } in + new_v, v) + selection + in + { + machine with + mmemory = machine.mmemory @ List.map (fun (v, _) -> v, None) new_mems; + mstep = + { + machine.mstep with + step_instrs = + machine.mstep.step_instrs + @ mkinstr (MComment "Registering all flows") + :: List.map stateassign new_mems; + }; + } + +let rec is_valid_path path nodename prog machines = + let nodescopes = compute_scopes prog nodename in + let m = get_machine machines nodename in + match path with + | [] -> + assert false + | [ vid ] -> + let res = + List.exists + (fun v -> v.var_id = vid) + (List.map fst m.mmemory @ m.mstep.step_inputs @ m.mstep.step_locals) + in + (* if not res then *) + (* Format.eprintf "Variable %s cannot be found in machine %s@.Local vars are + %a@." vid m.mname.node_id *) + (* (Utils.fprintf_list ~sep:", " Printers.pp_var) (m.mmemory @ + m.mstep.step_inputs @ m.mstep.step_locals) *) + (* ; *) + res + | _ :: nodename :: path' -> + (* We use the scopes computed on the prog artifact *) + (* Format.eprintf "Path is %a@ Local scopes: @[<v>%a@ @]@." *) + (* (Utils.fprintf_list ~sep:"." Format.pp_print_string) path *) + (* (Utils.fprintf_list ~sep:";@ " *) + (* (fun fmt scope -> *) + (* Utils.fprintf_list ~sep:"." Format.pp_print_string fmt (scope_to_sl scope)) *) + (* ) *) + (* nodescopes; *) + if List.mem path (List.map scope_to_sl nodescopes) then + (* Format.eprintf "Valid local path, checking underneath@."; *) + is_valid_path path' nodename prog machines + else false + +(* let instok = List.exists (fun (inst', node) -> inst' = inst) m.minstances + in *) +(* if not instok then Format.eprintf "inst = %s@." inst; *) +(* instok && *) +(* let instnode = fst (snd (List.find (fun (inst', node) -> inst' = inst) + m.minstances)) in *) +(* is_valid_path path' (Corelang.node_of_top instnode).node_id prog machines *) + +(****************************************************) + +let scopes_def : string list list ref = ref [] +let inputs = ref [] +let option_show_scopes = ref false +let option_scopes = ref false +let option_all_scopes = ref false +(* let option_mems_scopes = ref false + * let option_input_scopes = ref false *) + +let scopes_map : (ident list * scope_t) list ref = ref [] + +let process_scopes main_node prog machines = + let all_scopes = compute_scopes prog !Options.main_node in + let selected_scopes = + if !option_all_scopes then List.map (fun s -> scope_to_sl s) all_scopes + else !scopes_def + in + (* Making sure all scopes are defined and were not removed by various + optmizationq *) + let selected_scopes = + List.filter + (fun sl -> + let res = is_valid_path sl main_node prog machines in + (if not res then + Format.( + eprintf + "Scope %a is cancelled due to variable removal@." + (pp_print_list + ~pp_sep:(fun fmt () -> pp_print_string fmt ".") + pp_print_string) + sl)); + res) + selected_scopes + in + let scopes_map', machines_scopes = + check_scopes main_node prog machines all_scopes selected_scopes + in + scopes_map := scopes_map'; + (* Each machine is updated with fresh memories and declared as stateful *) + let machines = + List.map + (fun m -> + let mid = m.mname.node_id in + if List.mem_assoc mid machines_scopes then + let machine_scopes = List.assoc mid machines_scopes in + update_machine main_node m machine_scopes + else m) + machines + in + machines + +let activate () = + option_scopes := true; + Options.optimization := 0; + (* no optimization *) + () + +let register_scopes s = + activate (); + option_all_scopes := false; + let scope_list = Str.split (Str.regexp ", *") s in + let scope_list = + List.map (fun scope -> Str.split (Str.regexp "\\.") scope) scope_list + in + scopes_def := List.rev scope_list + +let register_inputs s = + activate (); + let input_list = Str.split (Str.regexp "[;]") s in + let input_list = + List.map + (fun s -> + match Str.split (Str.regexp "=") s with + | [ v; e ] -> + v, e + | _ -> + raise (Invalid_argument ("Input list error: " ^ s))) + input_list + in + let input_list = + List.map (fun (v, e) -> v, Str.split (Str.regexp "[;]") e) input_list + in + inputs := input_list + +let register_all_scopes () = + activate (); + option_all_scopes := true + +module Plugin : sig + include PluginType.S + + val show_scopes : unit -> bool +end = struct + include PluginType.Default + + let name = "scopes" + let is_active () = !option_scopes || !option_show_scopes || !option_all_scopes + (* || !option_mem_scopes || !option_input_scopes *) + + let show_scopes () = + !option_show_scopes + && + (Compiler_common.check_main (); + true) + + let usage fmt = + let open Format in + fprintf + fmt + "@[<hov 0>Scopes@ enrich@ the@ internal@ memories@ to@ record@ all@ or@ \ + a@ selection@ of@ internals.@ In@ conjunction@ with@ the@ trace@ \ + option@ of@ the@ produced@ binary@ it@ can@ also@ record@ these@ flow@ \ + values@ within@ separated@ log@ files.@]@ @ "; + fprintf fmt "Options are:@ " + + let options = + [ + "-select", Arg.String register_scopes, "specifies which variables to log"; + "-input", Arg.String register_inputs, "specifies the simulation input"; + ( "-show-possible-scopes", + Arg.Set option_show_scopes, + "list possible variables to log" ); + ( "-select-all", + Arg.Unit register_all_scopes, + "select all possible variables to log" ) + (* "-select-mems", Arg.Set option_mems_scopes, "select all memory variables to log"; + * "-select-inputs", Arg.Set option_input_scopes, "select all input variables to log"; *); + ] + + let activate = activate + let check_force_stateful () = is_active () + + let refine_machine_code prog machine_code = + if show_scopes () then ( + let all_scopes = compute_scopes prog !Options.main_node in + (* Printing scopes *) + if !Options.verbose_level >= 1 then Format.printf "Possible scopes are:@ "; + Format.printf "@[<v 0>%a@ @]@.@?" pp_scopes all_scopes; + exit 0); + if is_active () then process_scopes !Options.main_node prog machine_code + else machine_code + + let c_backend_main_loop_body_suffix fmt () = + if is_active () then Format.fprintf fmt "@ %a" pp_full_scopes !scopes_map + + let c_backend_main_loop_body_prefix basename mname fmt () = + if is_active () then + Format.fprintf fmt "@ %a" (pp_scopes_files basename mname) !scopes_map +end + +let () = + PluginList.registered := + (module Plugin : PluginType.S) :: !PluginList.registered +(* Local Variables: *) +(* compile-command:"make -C ../.." *) +(* End: *) diff --git a/src/plugins/scopes/scopes.mli b/src/plugins/scopes/scopes.mli new file mode 100644 index 00000000..b6281510 --- /dev/null +++ b/src/plugins/scopes/scopes.mli @@ -0,0 +1,14 @@ +open Utils +open Lustre_types + +module Plugin : sig + include PluginType.S + + val show_scopes : unit -> bool +end + +(* (variable, node name, node instance) *) +type scope_t = (var_decl * string * string option) list * var_decl + +val compute_scopes : ?first:bool -> program_t -> ident -> scope_t list +val pp_scopes : Format.formatter -> scope_t list -> unit -- GitLab