Skip to content
Snippets Groups Projects
l2lSplit.ml 14.8 KiB
Newer Older
(** After this lic2lic pass, there is only one operator per equation.
Pascal Raymond's avatar
Pascal Raymond committed

Source 2 source transformation :
Pascal Raymond's avatar
Pascal Raymond committed

CONDITION :
- après L2lRmPoly (et DoAliasType ?)
Pascal Raymond's avatar
Pascal Raymond committed
*)
Pascal Raymond's avatar
Pascal Raymond committed

let dbg = (Lv6Verbose.get_flag "split")
let info msg =
  let t = Sys.time() in
  Lv6Verbose.exe ~flag:dbg (fun () -> Printf.eprintf "%4.4f: %s%!" t msg)
(********************************************************************************)
let new_var type_eff clock_eff = 
  let id = Lv6Id.of_string (FreshName.local_var "v") in 
      var_nature_eff = AstCore.VarLocal;
      var_type_eff   = type_eff;
      var_clock_eff  = id,clock_eff;
Pascal Raymond's avatar
Pascal Raymond committed
    } in
(********************************************************************************)
(* functions that deal with tuple breaking *)
let rec (get_vel_from_tuple : val_exp -> val_exp list) =
    | { ve_core = CallByPosLic({it=Lic.TUPLE }, vel) } -> 
        List.flatten (List.map get_vel_from_tuple vel)
let rec (remove_tuple : val_exp list -> val_exp list) =
  fun vel -> 
    List.flatten (List.map get_vel_from_tuple vel)

let rec (remove_tuple_from_eq : eq_info srcflagged -> eq_info srcflagged) =
  (* transform "...=((x1,x2),x3)" into "...=(x1,x2,x3)" *)
      match ve.ve_core with
        | CallByPosLic({it=op;src=lxm }, vel) -> 
          { ve with 
            ve_core = CallByPosLic({it=op;src=lxm}, (remove_tuple vel)) }
        | _ -> ve
let to_be_broken = function
    (* We are only interested in operators that can deal with tuples! *)
  | CallByPosLic({ it = Lic.ARROW }, _) -> true
  | CallByPosLic({ it = Lic.FBY }, _) -> true
  | CallByPosLic({ it = Lic.PRE }, _) -> true
  | CallByPosLic({ it = Lic.CURRENT _ }, _) -> true
  | CallByPosLic({ it = Lic.TUPLE }, _) -> true
  | CallByPosLic({ it = Lic.WHEN _ }, _) -> true
  | CallByPosLic({ it = Lic.PREDEF_CALL({ it = (("Lustre","if"),[]) })}, _) -> true

let (break_it_do : val_exp -> val_exp list) =
      match ve.ve_core with
	     | CallByPosLic({it=Lic.PREDEF_CALL({ it = (("Lustre","if"),[]) });src=lxm}, [c;ve1;ve2]) ->
          let vel1 = get_vel_from_tuple ve1
          and vel2 = get_vel_from_tuple ve2 
          in
          assert (List.length vel1 = List.length vel2);
          List.map2
		      (fun ve1 ve2 -> 
		        { ve_core = 
                  CallByPosLic({it=Lic.PREDEF_CALL({ it = (("Lustre","if"),[]);src=lxm });src=lxm}, 
                               [c;ve1;ve2]);
                ve_typ = ve1.ve_typ;
                ve_clk = ve1.ve_clk;
              }
		      )
		      vel1
		      vel2
	     | CallByPosLic({it=WHEN clk; src=lxm}, vel) -> (
          let vel = List.flatten (List.map get_vel_from_tuple vel) in
          List.map 
		      (fun ve -> 
              { ve with 
                ve_core=CallByPosLic({it=WHEN clk ; src=lxm }, [ve])}) 
	     | CallByPosLic({it=Lic.TUPLE ; src=lxm }, vel) -> (remove_tuple vel)
	     | CallByPosLic({it=op ; src=lxm }, [ve]) ->
          let vel = get_vel_from_tuple ve in
          List.map 
            (fun ve -> { ve with ve_core=CallByPosLic({it=op;src=lxm}, [ve])})
            vel
	     | CallByPosLic({it=CURRENT c ; src=lxm }, [clk;ve]) ->
          let vel = get_vel_from_tuple ve in
          List.map 
            (fun ve -> { ve with ve_core=CallByPosLic({it=CURRENT c;src=lxm}, [clk;ve])})
	     | CallByPosLic({it=op ; src=lxm }, [ve1;ve2]) ->
          let vel1 = get_vel_from_tuple ve1
          and vel2 = get_vel_from_tuple ve2 
          in
          assert (List.length vel1 = List.length vel2);
          List.map2
		      (fun ve1 ve2 -> 
		        { ve_core = CallByPosLic({it=op ; src=lxm }, [ve1;ve2]);
                ve_typ = ve1.ve_typ;
                ve_clk = ve1.ve_clk }
		      )
		      vel1
		      vel2
	     | _ -> [ve]
    (*           assert false (* ougth to be dead code (guarded by to_be_broken...) *) *)
    let tl = ve.ve_typ
    and cl = ve.ve_clk in
    assert (List.length ve.ve_typ = List.length nvel);
    let nvel = List.map2 (fun nve t -> { nve with ve_typ = [t]; ve_clk=cl } ) nvel ve.ve_typ in   
    assert(ve.ve_typ = tl);
    nvel

let rec (break_it : val_exp -> val_exp list) =
  fun ve -> 
    let vel = break_it_do ve in
    if List.length vel = 1 then [ve] else 
      (* fixpoint *)
       (List.flatten (List.map break_it vel))
let (split_tuples:Lic.eq_info Lxm.srcflagged list -> Lic.eq_info Lxm.srcflagged list) =
  fun eql -> 
    let split_one_eq eq = 
      let { src = lxm_eq ; it = (lhs, n_rhs) } = eq in
        if List.length lhs > 1 && (to_be_broken n_rhs.ve_core) then
          let vel = break_it n_rhs in
          let eqs = 
            try List.map2 (fun lhs ve -> [lhs], ve) lhs vel 
            with _ -> 
              assert false
          in
          let eqs = List.map (fun eq -> Lxm.flagit eq lxm_eq) eqs in
            eqs
        else
          [eq]
    in
    List.fold_left (fun acc eq -> List.rev_append (split_one_eq eq) acc) [] eql 
(********************************************************************************)
(* The functions below accumulate 
   (1) the new equations 
   (2) the fresh variables. 
*)
type split_acc = (Lic.eq_info srcflagged) list * Lic.var_info list
let rec (eq : Lic.eq_info Lxm.srcflagged -> split_acc) =
  fun { src = lxm_eq ; it = (lhs, rhs) } ->
    let n_rhs, (neqs, nlocs) = split_val_exp false true rhs in
    { src = lxm_eq ; it = (lhs, n_rhs) }::neqs, nlocs
and (split_eq_acc : split_acc -> Lic.eq_info srcflagged -> split_acc) =
  fun  (eqs, locs) equation ->
    let (neqs, nlocs) = eq equation in 
    let neqs = split_tuples neqs in
    List.rev_append neqs eqs, List.rev_append nlocs locs

and (split_val_exp : bool -> bool -> Lic.val_exp -> Lic.val_exp * split_acc) =
  fun when_flag top_level ve -> 
    (* [when_flag] is true is the call is made from a "when" statement.
       We need this flag in order to know if it is necessary to add
       a when on constants. Indeed, in Lustre V6, it is not necessary
       to write " 1 when clk + x " if x in on clk (it's more sweet). 
       So we need to add it  (because if we split "1+1+x", then it
       is hard to know the "1" are on the clock of x ; moreover, lustre
       v4 (and the other backends) cannot infer such clock).

       But is is not forbidden either! so we need to make sure that there
       is no "when"...
    *)
    match ve.ve_core with
        let ce,(eql1, vl1) = split_val_exp false false ce in
        let const_l, vel = List.split cl in
        let vel,(eql2, vl2) = split_val_exp_list false false vel in
        let cl = List.combine const_l vel in
        let rhs = { ve with ve_core = Merge(ce,cl)} in
	     if top_level then rhs, (eql, vl) else
          (* create the var for the current call *)
          let clk_l = ve.ve_clk in 
          let typ_l = ve.ve_typ in  
          assert (List.length typ_l = List.length clk_l);
          let nv_l = List.map2 new_var typ_l clk_l  in
          let vi2val_exp nv = 
            let _,clk = nv.var_clock_eff in
            { 
              ve_core = CallByPosLic(Lxm.flagit (Lic.VAR_REF (nv.var_name_eff)) lxm,[]);
              ve_typ = [nv.var_type_eff];
              ve_clk = [clk];
            }
          in
            | [] -> assert false (* SNO *)
            | [nv] -> vi2val_exp nv 
            | _ -> { ve with ve_core =
                CallByPosLic(Lxm.flagit Lic.TUPLE lxm, List.map vi2val_exp nv_l)
                   }
          in
          let lpl = List.map (fun nv -> LeftVarLic(nv, lxm)) nv_l in
          let eq = Lxm.flagit (lpl, rhs) lxm in
		    nve, (eql@[eq], vl@nv_l)
      )   
      | CallByPosLic({it=Lic.VAR_REF _}, _) -> ve, ([],[])
      | CallByPosLic({it=Lic.CONST_REF _}, _) -> ve, ([],[])
      | CallByPosLic({src=lxm;it=Lic.CONST _}, _)
            let clk = ve.ve_clk in
            match (clk) with
              | On(clock,clk)::_ -> 
                { ve with ve_core = 
                    CallByPosLic({src=lxm;it=Lic.WHEN(On(clock,clk))},[ve])},
                ([],[])                  
              | (ClockVar _)::_ (* should not occur *)
              | BaseLic::_  -> ve, ([],[])
              | [] -> assert false (* should not occur *)
          else
            ve, ([],[])
      | CallByNameLic (by_name_op_eff, fl) -> (
        let lxm = by_name_op_eff.src in 
        let fl, eql, vl = 
          List.fold_left
            (fun (fl_acc, eql_acc, vl_acc) (fn, fv) ->
              let fv, (eql, vl) = split_val_exp false false fv in
              ((fn,fv)::fl_acc, eql@eql_acc, vl@vl_acc)
            )
            ([],[],[])
            fl
        in
        let rhs = { ve with ve_core = CallByNameLic (by_name_op_eff, List.rev fl) } in
	     if top_level then
          rhs, (eql, vl)
        else
          (* create the var for the current call *)
          let clk_l = ve.ve_clk in 
          let typ_l = ve.ve_typ in  
          assert (List.length typ_l = List.length clk_l);
          let nv_l = List.map2 new_var typ_l clk_l  in
          let nve = match nv_l with
            | [nv] -> { ve with ve_core = 
                CallByPosLic(
                  Lxm.flagit (Lic.VAR_REF (nv.var_name_eff)) lxm,
                )}
            | _ -> assert false
          in
          let lpl = List.map (fun nv -> LeftVarLic(nv, lxm)) nv_l in
          let eq = Lxm.flagit (lpl, rhs) lxm in
		    nve, (eql@[eq], vl@nv_l)
      | CallByPosLic(by_pos_op_eff, vel) -> (
        (* recursively split the arguments *) 
        let lxm = by_pos_op_eff.src in
        let (rhs, (eql,vl)) =
          match by_pos_op_eff.it with 
            | Lic.HAT(i) ->
              let vel, (eql, vl) = split_val_exp_list false false vel in
              let by_pos_op_eff = Lxm.flagit (Lic.HAT(i)) lxm in
              let rhs = CallByPosLic(by_pos_op_eff, vel) in
              rhs, (eql, vl)
            | Lic.WHEN ve -> (* should we create a var for the clock? *)
              let vel,(eql, vl) = split_val_exp_list true false vel in
              let by_pos_op_eff = Lxm.flagit (Lic.WHEN(ve)) lxm in
              let rhs = CallByPosLic(by_pos_op_eff, vel) in
              rhs, (eql, vl)
            | _ -> 
              let vel, (eql, vl) = split_val_exp_list false false vel in
              let rhs = CallByPosLic(by_pos_op_eff, vel) in
              rhs, (eql, vl)
        in
        let rhs = { ve with ve_core = rhs } in
	     if top_level || by_pos_op_eff.it = TUPLE then 
          rhs, (eql, vl) 
        else
          (* create the var for the current call *)
          let clk_l = ve.ve_clk in 
          let typ_l = ve.ve_typ in
          assert (List.length typ_l = List.length clk_l);
          let nv_l = List.map2 new_var typ_l clk_l  in
          let nve = 
            match nv_l with
              | [nv] -> {
                ve_typ = [nv.var_type_eff];
                ve_clk = clk_l; 
                ve_core = CallByPosLic(
                  Lxm.flagit (Lic.VAR_REF (nv.var_name_eff)) lxm,
	           }
              | _ -> {
                ve_typ = List.map (fun v -> v.var_type_eff) nv_l;
                ve_clk = clk_l;
		          ve_core = CallByPosLic(
                  Lxm.flagit Lic.TUPLE lxm, 
                  (List.map 
                     (fun nv -> 
                       let nnv = {
                         ve_core = CallByPosLic 
                           (Lxm.flagit (Lic.VAR_REF (nv.var_name_eff)) lxm, []);
                         ve_typ = [nv.var_type_eff];
                         ve_clk = [snd nv.var_clock_eff]
                       }
			              in
			              nnv
			            )
                     nv_l
			         )
                )
              }
	       in
	       let lpl = List.map (fun nv -> LeftVarLic(nv, lxm)) nv_l in
          let eq = Lxm.flagit (lpl, rhs) lxm in
		    nve, (eql@[eq], vl@nv_l)
      )
and (split_val_exp_list : bool -> bool -> Lic.val_exp list -> Lic.val_exp list * split_acc) =
  fun when_flag top_level vel ->
      List.split (List.map (split_val_exp when_flag top_level) vel) 
    let eqll,vll = List.split accl in
    let eql, vl = List.flatten eqll, List.flatten vll in
    (vel,(eql,vl))
and split_node (opt:Lv6MainArgs.t) (n: Lic.node_exp) : Lic.node_exp =
    Printf.eprintf "*** Splitting node  %s\n"
      (LicDump.string_of_node_key_iter n.node_key_eff);
    flush stderr);
  let res = match n.def_eff with
    | ExternLic 
    | AbstractLic None -> n
    | AbstractLic (Some pn) -> 
      { n with def_eff = AbstractLic (Some (split_node opt pn)) }
    | BodyLic b -> 
Pascal Raymond's avatar
Pascal Raymond committed
      let loc = match n.loclist_eff with None -> [] | Some l -> l in
      let (neqs, nv) = List.fold_left (split_eq_acc) ([], loc) b.eqs_eff in
      info (Printf.sprintf "Split %i equations into %i ones\n"
              (List.length b.eqs_eff)(List.length  neqs));

Erwan Jahier's avatar
Erwan Jahier committed
        if opt.Lv6MainArgs.gen_autotest then
          (* do not split assertions when generating lutin because we
             would handle less assertions *)
          (b.asserts_eff,neqs, nv) 
        else
          let asserts = List.map (fun x -> x.it) b.asserts_eff in 
          let lxm_asserts = List.map (fun x -> x.src) b.asserts_eff in 
          let nasserts,(neqs_asserts,nv_asserts) = 
            split_val_exp_list false true asserts 
          in
          assert (List.length nasserts = List.length lxm_asserts); 
          let nasserts = List.map2 Lxm.flagit nasserts lxm_asserts in 
          (nasserts,neqs@neqs_asserts, nv@nv_asserts) 
      in 
      let neqs = List.map remove_tuple_from_eq neqs in
Erwan Jahier's avatar
Erwan Jahier committed
      let nb = { eqs_eff = neqs ; asserts_eff = nasserts  } in
      { n with loclist_eff = Some nv; def_eff = BodyLic nb }
  in
  res
Pascal Raymond's avatar
Pascal Raymond committed

let rec doit (opt:Lv6MainArgs.t) (inprg : LicPrg.t) : LicPrg.t =
Pascal Raymond's avatar
Pascal Raymond committed
   (* n.b. on fait un minumum d'effet de bord pour
      pas avoir trop d'acummulateur ... *)
   let res = ref LicPrg.empty in

   (** TRAITE LES TYPES *)
   let do_type k (te:Lic.type_) =
Pascal Raymond's avatar
Pascal Raymond committed
      res := LicPrg.add_type k te !res
   in
   LicPrg.iter_types do_type inprg;

   (** TRAITE LES CONSTANTES *)
   let do_const k (ec: Lic.const) =
Pascal Raymond's avatar
Pascal Raymond committed
      res := LicPrg.add_const k ec !res
   in 
   LicPrg.iter_consts do_const inprg ;

   (** TRAITE LES NOEUDS : *)
   let rec do_node k (ne:Lic.node_exp) =
Pascal Raymond's avatar
Pascal Raymond committed
      (* On passe en parametre un constructeur de nouvelle variable locale *)
     info (Printf.sprintf  "#DBG: split equations of '%s'\n" (Lic.string_of_node_key k));
      let ne' = split_node opt ne in
Pascal Raymond's avatar
Pascal Raymond committed
      res := LicPrg.add_node k ne' !res
   in
   (*LET's GO *)
   LicPrg.iter_nodes do_node inprg;
   !res