summaryrefslogblamecommitdiff
path: root/minijazz/src/analysis/callgraph.ml
blob: d1fab8f3511301938a4e0a432fa2520f74bf9954 (plain) (tree)





































































































































































































                                                                                           
open Ast
open Mapfold
open Static
open Static_utils
open Location
open Errors

(** Inlines all nodes with static paramaters. *)

let expect_bool env se =
  let se = simplify env se in
  match se.se_desc with
    | SBool v -> v
    | _ -> Format.eprintf "Expected a boolean@."; raise Error

let expect_int env se =
  let se = simplify env se in
  match se.se_desc with
    | SInt v -> v
    | _ -> Format.eprintf "Expected an integer@."; raise Error

let simplify_ty env ty = match ty with
  | TBitArray se -> TBitArray (simplify env se)
  | _ -> ty

(** Find a node by name*)
let nodes_list = ref []
let find_node f =
  List.find (fun n -> f = n.n_name) !nodes_list

let vars_of_pat env pat =
  let exp_of_ident id =
    try
      let ty = IdentEnv.find id env in
      mk_exp ~ty:ty (Evar id)
    with
      | Not_found -> Format.eprintf "Not in env: %a@." Ident.print_ident id; assert false
  in
  let rec _vars_of_pat acc pat = match pat with
    | Evarpat id -> (exp_of_ident id)::acc
    | Etuplepat l -> List.fold_left (fun acc id -> (exp_of_ident id)::acc) acc l
  in
    _vars_of_pat [] pat

let ident_of_exp e = match e.e_desc with
  | Evar x -> x
  | _ -> assert false

let rename env vd =
  let e = mk_exp ~ty:vd.v_ty (Evar (Ident.copy vd.v_ident)) in
  IdentEnv.add vd.v_ident e env

let build_params m names values =
  List.fold_left2 (fun m { p_name = n } v -> NameEnv.add n v m) m names values

let build_exp m vds values =
  List.fold_left2 (fun m { v_ident = n } e -> IdentEnv.add n e m) m vds values

let build_env env vds =
  List.fold_left (fun env vd -> IdentEnv.add vd.v_ident vd.v_ty env) env vds

let rec find_local_vars b = match b with
  | BEqs (_, vds) -> vds
  | BIf (_, trueb, falseb) -> (find_local_vars trueb) @ (find_local_vars falseb)

(** Substitutes idents with new names, static params with their values *)
let do_subst_block m subst b  =
  let translate_ident subst id =
    try
      ident_of_exp (IdentEnv.find id subst)
    with
      | Not_found -> id
  in
  let static_exp funs (subst, m) se =
    simplify m se, (subst, m)
  in
  let exp funs (subst, m) e =
    let e, _ = Mapfold.exp funs (subst, m) e in
    match e.e_desc with
    | Evar x ->
        let e = if IdentEnv.mem x subst then IdentEnv.find x subst else e in
        e, (subst, m)
    | _ -> Mapfold.exp funs (subst, m) e
  in
  let pat funs (subst, m) pat = match pat with
    | Evarpat id -> Evarpat (translate_ident subst id), (subst, m)
    | Etuplepat ids -> Etuplepat (List.map (translate_ident subst) ids), (subst, m)
  in
  let var_dec funs (subst, m) vd =
    (* iterate on the type *)
    let vd, _ = Mapfold.var_dec funs (subst, m) vd in
    { vd with v_ident = translate_ident subst vd.v_ident }, (subst, m)
  in
  let funs =
    { Mapfold.defaults with static_exp = static_exp; exp = exp;
      pat = pat; var_dec = var_dec }
  in
  let b, _ = Mapfold.block_it funs (subst, m) b in
  b

let check_params loc m param_names params cl =
  let env = build_params NameEnv.empty param_names params in
  let cl = List.map (simplify env) cl in
  try
    check_true m cl
  with Unsatisfiable(c) ->
    Format.eprintf "%aThe following constraint is not satisfied: %a@."
      print_location loc  Printer.print_static_exp c;
    raise Error

let rec inline_node loc env m call_stack f params args pat =
  (* Check that the definition is sound *)
  if List.mem (f, params) call_stack then (
    Format.eprintf "The definition of %s is circular.@." f;
    raise Error
  );
  let call_stack = (f, params)::call_stack in

  (* do the actual work *)
  let n = find_node f in
  check_params loc m n.n_params params n.n_constraints;
  let m = build_params m n.n_params params in
  let subst = build_exp IdentEnv.empty n.n_inputs args in
  let subst = build_exp subst n.n_outputs (List.rev (vars_of_pat env pat)) in
  let locals = find_local_vars n.n_body in
  let subst = List.fold_left rename subst locals in
  let b = do_subst_block m subst n.n_body in
  let b = Normalize.block b in
  b, call_stack

and translate_eq env m subst call_stack (eqs, vds) ((pat, e) as eq) =
  match e.e_desc with
    (* Inline all nodes  or only those with params or declared inline
       if no_inline_all = true *)
    | Ecall(f, params, args) ->
        (try
            let n = find_node f in
            if not !Cli_options.no_inline_all
              || not (Misc.is_empty params)
              || n.n_inlined = Inlined then
              let params = List.map (simplify m) params in
              let b, call_stack = inline_node e.e_loc env m call_stack f params args pat in
              let new_eqs, new_vds = translate_block env m subst call_stack b in
              new_eqs@eqs, new_vds@vds
            else
              eq::eqs, vds
          with
            | Not_found -> eq::eqs, vds (* Predefined function*)
        )
    | _ -> eq::eqs, vds

and translate_eqs env m subst call_stack acc eqs =
  List.fold_left (translate_eq env m subst call_stack) acc eqs

and translate_block env m subst call_stack b =
  match b with
    | BEqs (eqs, vds) ->
        let vds = List.map (fun vd -> { vd with v_ty = simplify_ty m vd.v_ty }) vds in
        let env = build_env env vds in
        translate_eqs env m subst call_stack ([], vds) eqs
    | BIf(se, trueb, elseb) ->
        if expect_bool m se then
          translate_block env m subst call_stack trueb
        else
          translate_block env m subst call_stack elseb

let node m n =
  (*Init state*)
  let call_stack = [(n.n_name, [])] in
  (*Do the translation*)
  let env = build_env IdentEnv.empty n.n_inputs in
  let env = build_env env n.n_outputs in
  let eqs, vds = translate_block env m IdentEnv.empty call_stack n.n_body in
    { n with n_body = BEqs (eqs, vds) }

let build_cd env cd =
  NameEnv.add cd.c_name cd.c_value env

let program p =
  nodes_list := p.p_nodes;
  let m = List.fold_left build_cd NameEnv.empty p.p_consts in
  if !Cli_options.no_inline_all then (
    (* Find the nodes without static parameters *)
    let nodes = List.filter (fun n -> Misc.is_empty n.n_params) p.p_nodes in
    let nodes = List.map (fun n -> node m n) nodes in
    { p with p_nodes = nodes }
  ) else (
    try
      let n = List.find (fun n -> n.n_name = !Cli_options.main_node) p.p_nodes in
      if n.n_params <> [] then (
        Format.eprintf "The main node '%s' cannot have static parameters@." n.n_name;
        raise Error
      );
      { p with p_nodes = [node m n] }
    with Not_found ->
      Format.eprintf "Cannot find the main node '%s'@." !Cli_options.main_node;
      raise Error
  )