Skip to content
Snippets Groups Projects
l2lExpandNodes.ml 15.96 KiB
(* Time-stamp: <modified the 22/08/2017 (at 11:30) by Erwan Jahier> *)


open Lxm
open Lic
open AstPredef
    
let dbg = (Lv6Verbose.get_flag "en")

(* pack useful info into a single struct *)
type local_ctx = { 
  init_prg : LicPrg.t; (* nodes before expansion *)
  curr_prg : LicPrg.t; (* nodes that have already been expanded *)
  keep_node_call : Lic.node_key -> bool; (* node calls to expand *)
}

(********************************************************************************)
let new_var label str t c =
  let vi = FreshName.var_info str t c in
  Lv6Verbose.exe
    ~flag:dbg 
    (fun () -> Printf.printf "#EN: new_var[%s](%s)->%s%s'\n"
                             label str vi.var_name_eff (Lic.string_of_clock (snd c)) 
    );
  vi

(********************************************************************************)
let get_locals node =
  match node.loclist_eff with
    | None -> []
    | Some l -> l

let rec (get_node_body : local_ctx -> Lic.node_exp -> Lic.node_body option) =
  fun lctx node ->
    match node.def_eff with
      | ExternLic
      | AbstractLic None -> None
      | AbstractLic (Some node) -> assert false
      | BodyLic node_body -> Some node_body
      | MetaOpLic -> None

(********************************************************************************)
(* When expanding a node call such as 

    x = toto(a when c);

   when toto params are (for example) on the base clock, we need to
   propagate the substitution between the toto base clock and "on(c)"
   ; for the toto arguments, it's been done during clock checking ;
   but we need to remember this information to substitute the clock
   of toto in its local variables.
*)
type new_base = clock

(* to hold the substitution of parameters by arguments *)
module IdMap = Map.Make(struct type t = Lv6Id.t let compare = compare end)
type subst = Lic.var_info IdMap.t 

let (add_subst : subst -> Lv6Id.t -> Lic.var_info -> subst) =
  fun s id vi -> IdMap.add id vi s

let (subst_in_id : subst -> Lv6Id.t -> Lic.var_info) =
  fun s id -> IdMap.find id s

let (subst_in_id_id : subst -> Lv6Id.t -> Lv6Id.t) =
  fun s id -> 
  try (subst_in_id s id).var_name_eff with Not_found -> id
                      
let rec (substitute : subst * new_base ->
                      Lic.eq_info Lxm.srcflagged -> Lic.eq_info Lxm.srcflagged) =
  fun s { it = (lhs,ve) ; src = lxm } -> 
    let lhs = List.map (subst_in_left s ) lhs in
    let newve = subst_in_val_exp s ve in
    { it = (lhs,newve) ; src = lxm }

and subst_in_var_info (s,nb) vi =
  try
    let vi = subst_in_id s vi.var_name_eff in
    let id,clk = vi.var_clock_eff in
    { vi with var_clock_eff =  subst_in_id_id s id, subst_in_clock (s,nb) clk }
  with Not_found -> 
    assert false (* sno : all i/o have been replace ; fresh locs were created *)

and subst_in_left s left =
  match left with
  | LeftVarLic(v,lxm) -> LeftVarLic(subst_in_var_info s v,lxm)
  | LeftFieldLic(left,id,t) -> LeftFieldLic(subst_in_left s left,id,t)
  | LeftArrayLic(left,i, t) -> LeftArrayLic(subst_in_left s left,i, t)
  | LeftSliceLic(left,si,t) -> LeftSliceLic(subst_in_left s left,si,t)

and (subst_in_clock : subst * new_base  -> clock -> clock) =
  fun (s,new_base) clk -> 
    match clk with
      | BaseLic -> new_base
      | ClockVar i -> ClockVar i (* sno *)
      | On ((cc,cv,ct),clk) -> 
         On((cc, subst_in_id_id s cv, ct), subst_in_clock (s,new_base) clk)
           

and (subst_in_val_exp : subst * new_base -> val_exp -> val_exp) =
  fun s ve -> 
    let newve = {
      ve with 
        ve_clk = List.map (subst_in_clock s) ve.ve_clk;
        ve_core =
        match ve.ve_core with
	       | CallByPosLic (by_pos_op, vel) -> 
            let lxm = by_pos_op.src in
            let by_pos_op = by_pos_op.it in
            let vel = List.map (subst_in_val_exp s) vel in
            let by_pos_op = match by_pos_op with
		        | CONST_REF idl -> CONST_REF idl
              | VAR_REF id -> VAR_REF (subst_in_id_id (fst s) id)
              | WHEN(clk) -> WHEN(subst_in_clock s clk)
              | HAT(i) -> HAT(i)
              | PREDEF_CALL _| CALL _ | PRE | ARROW | FBY | CURRENT _ | TUPLE
              | ARRAY | CONCAT | STRUCT_ACCESS _ | ARRAY_ACCES _ | ARRAY_SLICE _ 
              | CONST _ 
                -> by_pos_op
            in
            CallByPosLic(Lxm.flagit by_pos_op lxm, vel)
		        
	       | CallByNameLic(by_name_op, fl) ->
            let fl = List.map (fun (id,ve) -> (id, subst_in_val_exp s ve)) fl in
            CallByNameLic(by_name_op, fl) 
          | Merge(ce,cl) ->
            let cl = List.map (fun (id,ve) -> (id, subst_in_val_exp s ve)) cl in
            Merge(subst_in_val_exp s ce, cl)
    }
    in
    newve

let (mk_loc_subst:subst -> Lic.var_info list -> Lic.var_info list -> subst) =
  fun s l1 l2 -> 
  let l = List.combine l1 l2 in
  List.fold_left
    (fun s (v1,v2) -> add_subst s v1.var_name_eff v2)
    s l

(********************************************************************************)
(** The functions below accumulate 
   (1) the assertions
   (2) the new equations 
   (3) the fresh variables

The fix-point is performed on nodes.
*)

type acc = 
    Lic.val_exp srcflagged list      (* assertions *)
    * (Lic.eq_info srcflagged) list  (* equations *)
    * Lic.var_info list              (* new local vars *)

let (mk_fresh_loc : string -> local_ctx -> var_info -> clock -> var_info) =
  fun label lctx v c ->
  new_var label ((Lv6Id.to_string v.var_name_eff)) v.var_type_eff (fst v.var_clock_eff, c)

          
let rec get_new_base c_arg c_par =
  match c_arg, c_par with
  | _, BaseLic -> c_arg
  | On(_,c_arg), On(_,c_par) -> get_new_base c_arg c_par
  | _,_ -> assert false (* sno *)
    
          
let (mk_input_subst: local_ctx -> Lxm.t -> var_info list ->
     Lic.val_exp list -> acc -> subst * acc * new_base option) =
  fun lctx lxm vl vel acc ->
  assert(List.length vl = List.length vel);
  let s, acc, new_base_opt =
    List.fold_left2
      (fun (s,(a_acc,e_acc,v_acc),nbase_opt) v ve ->
       (* we create a new var for each node argument to make sure
         that there is no expression in arguments. Is is generally
         useless because of the l2lSplit pass, but, well... *)
        let carg,cpar = match ve.ve_clk, v.var_clock_eff with
          | [c1],(_,c2) ->  c1,c2
          | _ -> assert false
        in
        let new_base = get_new_base carg cpar in
        let nv = mk_fresh_loc "input" lctx v carg  in
        let neq = [LeftVarLic(nv,lxm)],ve in
        let neq = flagit neq lxm in
        let nbase_opt = match nbase_opt with
          | None ->  Some(new_base)
          | Some(b) -> assert(b=new_base); Some(b)
        in
        add_subst s v.var_name_eff nv,
        (a_acc, neq::e_acc, nv::v_acc),
        nbase_opt
      )
      (IdMap.empty, acc, None)
      vl
      vel
  in
  s, acc, new_base_opt

(* Create fresh vars if necessary, ie, if we have no trivial left parts *)
let (mk_output_subst : local_ctx -> Lxm.t -> subst -> var_info list -> Lic.left list ->
                       new_base option-> acc -> subst * acc * new_base) = 
  fun lctx lxm s vl leftl new_base_opt acc ->
  assert(List.length vl = List.length leftl);
  let s,acc,new_base_opt =
    List.fold_left2
      (fun (s,acc,new_base_opt) v left -> 
        match left with
          | LeftVarLic(v2,lxm) -> add_subst s v.var_name_eff v2, acc,new_base_opt
          | _ -> 
            let clk = Lic.clock_of_left left in
            let nv = mk_fresh_loc "output" lctx v clk in
            let nv_id = nv.var_name_eff in
	         let nve = {
              ve_core = CallByPosLic({it=VAR_REF nv_id;src = lxm },[]);
              ve_typ = [nv.var_type_eff];
              ve_clk = [snd nv.var_clock_eff];
              ve_src = lxm
            }
            in
            let neq = [left], nve in
            let neq = flagit neq lxm in
            (* for equations of the form 
               x.field = n(...);
               we create 
               - a fresh var "_v" 
               - define the equation "x.field = _v;"
               - create the substition n_output_par/_v
            *)
            let (aa,ae,av) = acc in
            let new_base = get_new_base (Lic.clock_of_left left) (snd v.var_clock_eff) in
            let new_base_opt =
              match new_base_opt with
              | Some x -> assert(x=new_base); Some x
              | None -> Some new_base
            in
            add_subst s v.var_name_eff nv, (aa, neq::ae, nv::av), new_base_opt
      )
      (s,acc,new_base_opt)
      vl 
      leftl
  in
  let new_base = match new_base_opt with
    | Some x -> x
    | None -> assert false (* a node outgth to have at least an input or an output *)
  in
  s,acc,new_base
  
let rec (expand_eq : local_ctx * acc -> Lic.eq_info Lxm.srcflagged -> local_ctx * acc) =
  fun (lctx, (asserts, eqs, locs)) { src = lxm_eq ; it = eq } ->
    match expand_eq_aux lctx eq with
      | lctx, None -> lctx, (asserts, { src = lxm_eq ; it = eq }::eqs, locs)
      | lctx, Some(nasserts, neqs, nlocs) ->
        (lctx,
         (List.rev_append nasserts asserts, 
          List.rev_append neqs eqs, 
          List.rev_append nlocs locs))

and (expand_eq_aux: local_ctx -> Lic.eq_info -> local_ctx * acc option)=
  fun lctx (lhs,ve) -> 
    match ve.ve_core with
      | CallByPosLic( { it = Lic.CALL node_key ; src = lxm }, vel) ->
        let lctx, node = 
          match LicPrg.find_node lctx.curr_prg node_key.it with 
            | Some node -> lctx, node
            | None -> (* node has not been expanded yet: fix point! *)
              let raw_node =
                match LicPrg.find_node lctx.init_prg node_key.it with 
                  | Some n -> n
                  | None ->
                    prerr_string (
                      "*** "^ (LicDump.string_of_node_key_rec false false node_key.it) ^
                        " not defined.\n*** Defined nodes are:"^ 
                        (String.concat ",\n"  
                           (List.map (fun (nk,_) -> "\""^
                             LicDump.string_of_node_key_rec false false nk^"\"")
                              (LicPrg.list_nodes lctx.init_prg)))
                    );
                    flush stderr;
                    assert false 
              in
              expand_node lctx raw_node 
        in
        let node = match node.def_eff with
          | AbstractLic (Some n) ->  { it = n ; src = lxm }
          | _  -> flagit node lxm
        in            
        if 
          (lctx.keep_node_call node.it.node_key_eff)
        then
          lctx, None
        else
          (match get_node_body lctx node.it with
            | None -> lctx, None
            | Some node_body ->
              let node_eqs = node_body.eqs_eff
              and asserts = node_body.asserts_eff in
              let node = node.it in
              let node_locs = get_locals node in
              let acc = [],[],[] in
              (* rename i/o using the calling context *)
              let s,acc,new_base = mk_input_subst lctx lxm node.inlist_eff vel acc in
              let s,acc,new_base = mk_output_subst lctx lxm s node.outlist_eff
                                                   lhs new_base acc in
              let rec (substitute_clock : clock -> clock) = 
                fun c -> 
                match c with
                | BaseLic -> new_base
                | ClockVar _ -> new_base (* SNO *)
                | On(v,clk) -> On(v, substitute_clock clk)
              in
              let clks = List.map (fun v  -> snd v.var_clock_eff) node_locs in
              (* rename locals with fresh names to avoid clashes among locals *)
              let fresh_locs = List.map2 (mk_fresh_loc "local" lctx) node_locs clks in
              let s = mk_loc_subst s node_locs fresh_locs in
              let fresh_locs = (* substitute the new vars in clocks *)
                List.map (fun v -> 
                  let id,clk = v.var_clock_eff in
                  { v with var_clock_eff =
                             subst_in_id_id s id, subst_in_clock (s,new_base) clk })
                  fresh_locs
              in
              let fresh_locs =
                List.map
                  (fun v ->
                   let id,clk = v.var_clock_eff in
                   Lv6Verbose.exe
                     ~flag:dbg
                     (fun () ->
                      Printf.printf "#EN: remplace the base clk of %s by '%s'\n"
                                    v.var_name_eff
                                    (Lic.string_of_clock new_base));
                   let clk = substitute_clock clk in
                   { v with var_clock_eff = id,clk }
                  )
                  fresh_locs
              in
              let node_eqs = List.map (substitute (s, new_base)) node_eqs in
              let subst_assert x = Lxm.flagit (subst_in_val_exp (s,new_base) x.it) x.src in
              let asserts = List.map subst_assert asserts in              
              let acc = match acc with (a,b,c) ->  (a@asserts,b@node_eqs,c@fresh_locs) in
              lctx, Some acc
          )
      | CallByPosLic (_, _)
      | Merge (_, _) 
      | CallByNameLic (_, _) -> lctx, None

and (expand_assert : local_ctx * acc -> val_exp srcflagged -> local_ctx * acc) =
  fun (lctx, (a_acc,e_acc,v_acc)) ve -> 
    (*   assert(ve); 
       is transformed into
         assert_var=ve;
         assert(assert_var);
       where assert_var is a fresh new local var
    *)
    let lxm = ve.src in
    let ve = ve.it in
    let clk = Lv6Id.of_string "dummy_expand_assert", BaseLic in
    let assert_var = new_var "assert" "assert" Bool_type_eff clk in
    let assert_eq = Lxm.flagit ([LeftVarLic(assert_var,lxm)], ve) lxm in
    let assert_op = Lic.VAR_REF(assert_var.var_name_eff) in
    let nve = { 
      ve_core = CallByPosLic((Lxm.flagit assert_op lxm, []));
      ve_typ = [Bool_type_eff];
      ve_clk = [BaseLic];
      ve_src = lxm
    }
    in
    expand_eq (lctx, ((Lxm.flagit nve lxm)::a_acc, e_acc, assert_var::v_acc)) assert_eq

and (expand_node : local_ctx -> Lic.node_exp -> local_ctx * Lic.node_exp) =
  fun lctx n ->
    match LicPrg.find_node lctx.curr_prg n.node_key_eff with 
      | Some n -> lctx, n
      | None ->
        let lctx, exp_node =
          match n.def_eff with
            | ExternLic 
            | AbstractLic _ -> lctx, n
            | BodyLic b -> 
              let locs = get_locals n in              
              let lctx,acc = List.fold_left expand_eq (lctx, ([],[],locs)) b.eqs_eff in
              let lctx,acc = List.fold_left expand_assert (lctx, acc) b.asserts_eff in
              let (asserts,neqs, nv) = acc in
              let nb = {
                eqs_eff = neqs ; 
                asserts_eff = asserts
              } 
              in
              let res =
                { n with 
                  loclist_eff = Some nv;
                  def_eff = BodyLic nb
                }
              in
              Lv6Verbose.exe ~flag:dbg (fun () ->
                Printf.printf "#EN: '%s'\n"
                  (Lic.string_of_node_key n.node_key_eff));
              lctx, res
            | MetaOpLic -> lctx, n
        in
        { lctx with curr_prg = LicPrg.add_node n.node_key_eff exp_node lctx.curr_prg },
        exp_node

(* exported *)
let (doit : Lic.node_key list -> LicPrg.t -> LicPrg.t) =
  fun node_calls_to_keep inprg ->
    let outprg = LicPrg.empty in
    (** types and constants do not change *)
    let outprg = LicPrg.fold_types  LicPrg.add_type  inprg outprg in
    let outprg = LicPrg.fold_consts LicPrg.add_const inprg outprg in
    let lctx_init = {
      init_prg = inprg;
      curr_prg = outprg;
      keep_node_call = (fun n -> List.mem n node_calls_to_keep);
    }
    in
    (** transform nodes *)
    let rec (do_node : Lic.node_key -> Lic.node_exp -> local_ctx -> local_ctx) = 
      fun nk ne lctx -> 
          fst (expand_node lctx ne)
    in
    let lctx = LicPrg.fold_nodes do_node inprg lctx_init in
    let prg = lctx.curr_prg in
    LicPrg.fold_nodes 
      (fun nk ne acc -> 
        (* remove expanded nodes *)
        if not (List.mem nk node_calls_to_keep) && not (Lic.node_is_extern ne) then 
          LicPrg.del_node nk acc 
        else 
          acc
      )
      prg
      prg