You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
8.8 KiB
OCaml
253 lines
8.8 KiB
OCaml
(***********************************************************************)
|
|
(* *)
|
|
(* Heptagon *)
|
|
(* *)
|
|
(* Gwenael Delaval, LIG/INRIA, UJF *)
|
|
(* Leonard Gerard, Parkas, ENS *)
|
|
(* Adrien Guatto, Parkas, ENS *)
|
|
(* Cedric Pasteur, Parkas, ENS *)
|
|
(* *)
|
|
(* Copyright 2012 ENS, INRIA, UJF *)
|
|
(* *)
|
|
(* This file is part of the Heptagon compiler. *)
|
|
(* *)
|
|
(* Heptagon is free software: you can redistribute it and/or modify it *)
|
|
(* under the terms of the GNU General Public License as published by *)
|
|
(* the Free Software Foundation, either version 3 of the License, or *)
|
|
(* (at your option) any later version. *)
|
|
(* *)
|
|
(* Heptagon is distributed in the hope that it will be useful, *)
|
|
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
|
|
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
|
|
(* GNU General Public License for more details. *)
|
|
(* *)
|
|
(* You should have received a copy of the GNU General Public License *)
|
|
(* along with Heptagon. If not, see <http://www.gnu.org/licenses/> *)
|
|
(* *)
|
|
(***********************************************************************)
|
|
|
|
(* ASSUMES no automaton, no present, no last, no reset *)
|
|
|
|
(* Removing switch statements *)
|
|
|
|
(* sketch of the transformation :
|
|
Eswitch is translated into an Eblock in the following way :
|
|
|
|
switch (e)
|
|
up : block_up
|
|
down : block_down
|
|
|
|
with one defined var y ( defnames = {y} ) and used var x
|
|
(example : block_up = block : { var t in t = x + 3; y = t + 2; }
|
|
|
|
becomes :
|
|
|
|
block : {
|
|
var ck, y_up, y_down in
|
|
ck = e
|
|
block_up[x when up(ck) /x][y_up /y]
|
|
block_down[x when up(ck) /x][y_down /y]
|
|
y = merge ck (up -> y_up) (down -> y_down)
|
|
}
|
|
*)
|
|
|
|
(* e_level_ck is used to have correct behavior for side effects :
|
|
it keep track of the fact that a call
|
|
without interaction with the dataflow was in a case of the switch *)
|
|
|
|
|
|
|
|
|
|
open Misc
|
|
open Heptagon
|
|
open Hept_utils
|
|
open Hept_mapfold
|
|
|
|
(** Give the var [name] and a [constructor], returns fresh [name_constr] *)
|
|
let fresh_case_var name constr =
|
|
let tmp (n,c) =
|
|
n^"_"^(Names.print_pp_to_name Global_printer.print_qualname c) in
|
|
Idents.gen_fresh "switch" tmp (name,constr)
|
|
|
|
let fresh_clock_id () =
|
|
Idents.gen_var "switch" "ck"
|
|
|
|
|
|
|
|
(** Environment [env]
|
|
used to sample the shared variables : x ==> x when Up(ck)... *)
|
|
module Env = struct
|
|
|
|
|
|
open Idents
|
|
open Names
|
|
open Clocks
|
|
|
|
type t = Base | Level of ck * IdentSet.t * t
|
|
|
|
let level_up constr ckid env = match env with
|
|
| Base -> Level(Con(Cbase, constr, ckid), IdentSet.empty, env)
|
|
| Level (ck, _,_) -> Level(Con(ck, constr, ckid), IdentSet.empty, env)
|
|
|
|
let level_down env = match env with
|
|
| Base -> Format.eprintf "Internal Error : wrong switch level"; assert false
|
|
| Level(_,_, env_down) -> env_down
|
|
|
|
let add_var x env = match env with
|
|
| Base -> Base
|
|
| Level (ck, h, ed) ->
|
|
Level(ck, IdentSet.add x h, ed)
|
|
|
|
(** Wraps [Evar]s with the needed [when]
|
|
corresponding to their definition level *)
|
|
let rec sample_var e env = match env with
|
|
| Base -> e
|
|
| Level (Con(_, constr, ck), h, env_d) ->
|
|
(match e.e_desc with
|
|
| Evar x ->
|
|
if IdentSet.mem x h
|
|
then e (* the var is declared at this level, nothing to do *)
|
|
else (*sample to lower level*)
|
|
{e with e_desc =
|
|
Ewhen ((sample_var e env_d), constr, ck)}
|
|
| _ ->
|
|
(Format.eprintf "'sample_var' called on full exp : %a@."
|
|
Hept_printer.print_exp e;
|
|
assert false))
|
|
| Level _ -> assert false
|
|
|
|
|
|
(** Gives back the current level clock *)
|
|
let current_level env = match env with
|
|
| Base -> Cbase
|
|
| Level (ck, _,_) -> ck
|
|
|
|
(** Set the base clock of an expression to the current level of the [env] *)
|
|
let annot_exp e env =
|
|
{ e with e_level_ck = current_level env }
|
|
|
|
end
|
|
|
|
(** Renaming environment [h]
|
|
to rename the defined shared variables : x = ... ==> x_up = ... *)
|
|
module Rename = struct
|
|
include Idents.Env
|
|
|
|
let rename n h =
|
|
try find n h with _ -> n
|
|
|
|
let rename_defnames defnames h =
|
|
fold (fun n ty acc -> add (rename n h) ty acc) defnames empty
|
|
|
|
let level_up defnames constr h =
|
|
let ident_level_up n new_h =
|
|
let old_n = rename n h in
|
|
let new_n = fresh_case_var (Idents.name old_n) constr in
|
|
add n new_n new_h
|
|
in
|
|
fold (fun n _ new_h -> ident_level_up n new_h) defnames empty
|
|
|
|
(* only use of [vd_env] is here to create y_Up with the same type as y, etc. *)
|
|
let add_to_locals vd_env locals h =
|
|
let add_one n nn (locals,vd_env) =
|
|
let orig_vd = Idents.Env.find n vd_env in
|
|
let vd_nn = mk_var_dec nn orig_vd.v_type orig_vd.v_linearity in
|
|
vd_nn::locals, Idents.Env.add vd_nn.v_ident vd_nn vd_env
|
|
in
|
|
fold add_one h (locals, vd_env)
|
|
end
|
|
|
|
(** Mapfold *)
|
|
|
|
|
|
(* apply the renaming for shared defined variables *)
|
|
let pattern _ (vd_env,env,h) pat = match pat with
|
|
| Evarpat x -> Evarpat (Rename.rename x h), (vd_env,env,h)
|
|
| _ -> raise Errors.Fallback
|
|
|
|
let var_dec _ (vd_env,env,h) vd =
|
|
let env = Env.add_var vd.v_ident env in
|
|
let vd_env = Idents.Env.add vd.v_ident vd vd_env in
|
|
vd, (vd_env,env,h)
|
|
|
|
(* apply the renaming to the defnames *)
|
|
let block funs (vd_env,env,h) b =
|
|
let b = { b with b_defnames = Rename.rename_defnames b.b_defnames h } in
|
|
Hept_mapfold.block funs (vd_env,env,h) b
|
|
|
|
(* apply the sampling on shared vars *)
|
|
let exp funs (vd_env,env,h) e =
|
|
let e = Env.annot_exp e env in
|
|
match e.e_desc with
|
|
| Evar _ -> Env.sample_var e env, (vd_env,env,h)
|
|
| _ -> Hept_mapfold.exp funs (vd_env,env,h) e
|
|
|
|
(* update stateful and loc *)
|
|
let eq funs (vd_env,env,h) eq =
|
|
let eqd = match eq.eq_desc with
|
|
| Eblock b -> (* probably created by eqdesc, so update stateful and loc *)
|
|
Eblock { b with b_stateful = eq.eq_stateful; b_loc = eq.eq_loc }
|
|
| _ -> eq.eq_desc in
|
|
Hept_mapfold.eq funs (vd_env,env,h) {eq with eq_desc = eqd}
|
|
|
|
(* remove the Eswitch *)
|
|
let eqdesc funs (vd_env,env,h) eqd = match eqd with
|
|
| Eswitch (e, sw_h_l) ->
|
|
(* create a clock var corresponding to the switch condition [e] *)
|
|
let ck = fresh_clock_id () in
|
|
let e, (vd_env,env,h) = exp_it funs (vd_env,env,h) e in
|
|
let locals = [mk_var_dec ck e.e_ty e.e_linearity] in
|
|
let equs = [mk_equation (Eeq (Evarpat ck, e))] in
|
|
|
|
(* typing have proved that defined variables are the same among states *)
|
|
let defnames = (List.hd sw_h_l).w_block.b_defnames in
|
|
|
|
(* deal with the handlers *)
|
|
let switch_handler (c_h_l, locals, equs, vd_env) sw_h =
|
|
let constr = sw_h.w_name in
|
|
(* level up *)
|
|
let h = Rename.level_up defnames constr h in
|
|
let env = Env.level_up constr ck env in
|
|
(* add to the locals the new vars from leveling_up *)
|
|
let locals,vd_env = Rename.add_to_locals vd_env locals h in
|
|
(* mapfold with updated envs *)
|
|
let b_eq, (_,_,h) = block_it funs (vd_env,env,h) sw_h.w_block in
|
|
(* inline the handler as a block *)
|
|
let equs = (mk_equation (Eblock b_eq))::equs in
|
|
((constr,h)::c_h_l, locals, equs, vd_env)
|
|
in
|
|
|
|
let (c_h_l, locals, equs, vd_env) =
|
|
List.fold_left switch_handler ([], locals, equs, vd_env) sw_h_l
|
|
in
|
|
|
|
(* create a merge equation for each defnames *)
|
|
let new_merge n vd equs =
|
|
let c_h_to_c_e (constr,h) =
|
|
constr, mk_exp (Evar(Rename.rename n h)) vd.v_type ~linearity:vd.v_linearity
|
|
in
|
|
let c_e_l = List.map c_h_to_c_e c_h_l in
|
|
let merge = mk_exp (Emerge (ck, c_e_l)) vd.v_type ~linearity:vd.v_linearity in
|
|
(mk_equation (Eeq (Evarpat (Rename.rename n h), merge))) :: equs
|
|
in
|
|
let equs =
|
|
Idents.Env.fold (fun n vd equs -> new_merge n vd equs) defnames equs
|
|
in
|
|
|
|
(* return the transformation in a block *)
|
|
let b = mk_block ~defnames:defnames ~locals:locals equs in
|
|
Eblock b, (vd_env,env,h)
|
|
| _ -> raise Errors.Fallback
|
|
|
|
let program p =
|
|
let funs = { Hept_mapfold.defaults
|
|
with pat = pattern; var_dec = var_dec; block = block;
|
|
exp = exp; eq = eq; eqdesc = eqdesc } in
|
|
let p, _ = program_it funs (Idents.Env.empty,Env.Base,Rename.empty) p in
|
|
p
|
|
|
|
|
|
|
|
|
|
|