From 264a4844995eb33f236f8b642b79138883cc96d2 Mon Sep 17 00:00:00 2001
From: ploc <ploc@garoche.net>
Date: Thu, 20 Jul 2017 13:20:04 -0700
Subject: [PATCH] First working version of algebraic loop resolution. Disabled
 by default.

---
 .gitignore                  |   6 +-
 src/algebraicLoop.ml        | 354 +++++++++++++++++++++++-------------
 src/corelang.ml             |   3 +-
 src/error.ml                |   5 +-
 src/inliner.ml              |  67 +++++--
 src/machine_code.ml         |   2 +-
 src/main_lustre_compiler.ml |   6 +-
 src/main_lustre_testgen.ml  |   2 +-
 src/options.ml              |   3 +-
 src/options_management.ml   |   2 +
 10 files changed, 292 insertions(+), 158 deletions(-)

diff --git a/.gitignore b/.gitignore
index c1292d11..5acf3044 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
-/_build/
-/main_lustre_compiler.native
+*~
+/src/_build/
 /setup.data
 /setup.log
 /src/version.ml
-/myocamlbuild.ml
\ No newline at end of file
+/myocamlbuild.ml
diff --git a/src/algebraicLoop.ml b/src/algebraicLoop.ml
index 53633eeb..f863bb7e 100644
--- a/src/algebraicLoop.ml
+++ b/src/algebraicLoop.ml
@@ -14,7 +14,6 @@ When done, a report is generated.
 open LustreSpec
 open Corelang
 open Utils
-open Format
 
 (* An single algebraic loop is defined (partition, node calls, inlined, status)
    ie 
@@ -30,8 +29,9 @@ open Format
 type call = ident * expr * eq (* fun id, expression, and containing equation *)
   
 type algebraic_loop = ident list * (call * bool) list * bool
-  
-exception Error of (node_desc * (algebraic_loop list)) list
+type report = (node_desc * algebraic_loop list) list
+exception Error of report
+
 
 (* Module that extract from the DataCycle the set of node that could be inlined
    to solve the problem. *)
@@ -76,7 +76,7 @@ let pp_resolution fmt resolution =
     Format.fprintf fmt "inlining: %a" Printers.pp_node_eq eq
   ) fmt resolution
   
-let al_is_solved (_, (vars, calls, status)) = status
+let al_is_solved (_, als) = List.for_all (fun (vars, calls, status) -> status) als
   
 (**********************************************************************)
 (* Functions to access or toggle the local inlining feature of a call *)
@@ -89,124 +89,181 @@ let inline_annotation loc =
     (Corelang.mkexpr loc
        (Expr_const (Const_tag tag_true) ))
 
-let is_expr_inlined expr =
+let is_inlining_annot (key, status) =
+key = Inliner.keyword && (
+  match status.eexpr_qfexpr.expr_desc with
+  | Expr_const (Const_tag tag) when tag = tag_true ->
+     true
+  | Expr_const (Const_tag tag) when tag = tag_false ->
+     false
+  | _ -> assert false (* expecting true or false value *)	 
+)
+  
+let is_expr_inlined nd expr =
   match expr.expr_annot with
     None -> false
-  | Some anns -> if List.mem_assoc Inliner.keyword anns.annots then
-      let status = List.assoc Inliner.keyword anns.annots in
-      match status.eexpr_qfexpr.expr_desc with
-      | Expr_const (Const_tag tag) when tag = tag_true ->
-	 true
-      | Expr_const (Const_tag tag) when tag = tag_false ->
-	 false
-      | _ -> assert false (* expecting true or false value *)	 
-    else false
-      
+  | Some anns -> (
+     (* Sanity check: expr should have the annotation AND the annotation should be declared *)
+     let local_ann = List.exists is_inlining_annot anns.annots in
+     let all_expr_inlined = Hashtbl.find_all Annotations.expr_annotations Inliner.keyword in
+     let registered =
+       List.exists
+	 (fun (nd_id, expr_tag) -> nd_id = nd.node_id && expr_tag = expr.expr_tag)
+	 all_expr_inlined
+     in
+     match local_ann, registered with
+     | true, true -> true (* Everythin' all righ' ! *)
+     | false, false -> false (* idem *)
+     | _ -> assert false 
+  )
+
+let pp_calls nd fmt calls = Format.fprintf fmt "@[<v 0>%a@]"
+  (fprintf_list ~sep:"@ " (fun fmt (funid,expr, _) -> Format.fprintf fmt "%s: %i (inlined:%b)"
+    funid
+    expr.expr_tag
+    (is_expr_inlined nd expr)
+   ))
+  calls
+
 (* Inline the provided expression *)
-let inline_expr expr =
+let inline_expr node expr =
+  (* Format.eprintf "inlining %a@ @?" Printers.pp_expr expr; *)
   let ann = inline_annotation expr.expr_loc in
-    match expr.expr_annot with
-    | None -> expr.expr_annot = Some {annots = [ann]; annot_loc = expr.expr_loc} 
-    | Some anns -> expr.expr_annot = Some {anns with annots = ann::anns.annots}
-  
-(* Inline the call tagged tag in expr: should not be used anymore, thanks to a direct call to inline_expr *)
-let rec inline_call tag expr =
-  if expr.expr_tag = tag then
-    inline_expr expr
+  let ann = {annots = [ann]; annot_loc = expr.expr_loc} in
+  let res = update_expr_annot node.node_id expr ann in
+  (* assert (is_expr_inlined node res); *)
+  (* Format.eprintf "Is expression inlined? %b@." (is_expr_inlined node res); *)
+  res
+
+(* Perform the steps of stage1/stage2 to revalidate the schedulability of the program *)
+let fast_stages_processing prog =
+  Log.report ~level:3
+    (fun fmt -> Format.fprintf fmt "@[<v 2>Fast revalidation: normalization + schedulability@ ");
+  Options.verbose_level := !Options.verbose_level - 2;
+
+  (* Mini stage 1 *)
+  (* Extracting dependencies *)
+  let dependencies, type_env, clock_env = Compiler_common.import_dependencies prog in
+  (* Local inlining *)
+  let prog = Inliner.local_inline prog (* type_env clock_env *) in
+  (* Checking stateless/stateful status *)
+  if Plugins.check_force_stateful () then
+    Compiler_common.force_stateful_decls prog
   else
-    let _in = inline_call tag in
-    let _ins = List.exists _in
-    in
-    match expr.expr_desc with
-    | Expr_const _
-    | Expr_ident _ -> false
-    | Expr_tuple el -> _ins el
-    | Expr_ite (g,t,e) -> _ins [g;t;e]
-    | Expr_arrow (e1, e2) 
-    | Expr_fby (e1, e2) -> _ins [e1; e2]
-    | Expr_array el -> _ins el
-    | Expr_access (e, _)
-    | Expr_power (e, _)
-    | Expr_pre e
-    | Expr_when (e, _, _) -> _in e
-    | Expr_merge (_, hl) -> _ins (List.map snd hl)
-    | Expr_appl (_, arg, Some r) -> _ins [arg; r]
-    | Expr_appl (_, arg, None) -> _in arg
+    Compiler_common.check_stateless_decls prog;
+  (* Typing *)
+  let _ (*computed_types_env*) = Compiler_common.type_decls type_env prog in
+  (* Clock calculus *)
+  let _ (*computed_clocks_env*) = Compiler_common.clock_decls clock_env prog in
+  (* Normalization *)
+  let prog = Normalization.normalize_prog ~backend:!Options.output prog in
+  (* Mini stage 2 : Scheduling *)
+  let res = Scheduling.schedule_prog prog in
+  Options.verbose_level := !Options.verbose_level + 2;
 
+  Log.report ~level:3
+    (fun fmt -> Format.fprintf fmt "@]@ ");
+  res
 
 (**********************)
 (* Returns a modified node, if possible, and an algebraic_loop report *)
-let rec solving_node nd existing_al partitions =
+let rec solving_node max_inlines prog nd existing_al partitions =
+  (* let pp_calls = pp_calls nd in *)
   (* For each partition, we identify the original one *)
-  let rerun, al = List.fold_left (fun (rerun, al) part ->
+  let rerun, max_inlines, al = List.fold_left (fun (rerun, inlines, al) part ->
     let part_vars = ISet.of_list part in 
+    (* Useful functions to filter list of elements *)
     let match_al (vars, calls, status) =
-      not (ISet.is_empty (ISet.inter (ISet.of_list vars) part_vars))  in
+      not (ISet.is_empty (ISet.inter (ISet.of_list vars) part_vars)) in
+    (* Identifying previous alarms that could be associated to current conflict *)
     let matched, non_matched = List.partition match_al existing_al in
-    match matched, non_matched with
-    | [], _ -> ((* A new conflict partition *)
-      let calls = CycleResolution.resolve nd part in
-      (* Filter out already inlined calls *)
-      let already_inlined, possible_resolution =
-	List.partition (fun (_, expr, _) -> is_expr_inlined expr) calls in
-      (* Inlining the first uninlined call *)
-      match possible_resolution with
-      | (eq, expr, fun_id)::_ -> ((* One could inline expr *)
-	(* Forcing the expr to be inlined *)
-	let _ = inline_expr expr in
-	true, (* we have to rerun to see if the inlined expression solves the issue *)
-	(
-	  part,
-	  List.map (fun (eq, expr2, fcn_name) ->  fcn_name, expr2, (expr2.expr_tag = expr.expr_tag)) calls,
-	  false (* Status is nok, LA is unsolved yet *)
-	    
-	)::non_matched
-      )	 
-      | [] -> (* No more calls to inline: algebraic loop is not solvable *)
-	 rerun, (* we don't force rerun since the current node is not solvable *)
-	(
-	  part, (* initial list of troublesome variables *)
-	  List.map (fun (eq, expr, fcn_name) ->  fcn_name, expr, false) calls,
-	  false (* Status is nok, LA is unsolved *)
-	)::non_matched 
-	  
-    )
-    | [vars, calls, status], _ ->
-       assert false (* Existing partitions were already addressed:
-		       on peut regarder si la partition courante concerne xxx
-		       else (* New cycle ??? *)
-		       
-		       
-
-
-		       Que faut il faire?
-
-		       dans un premiertemps on peut ramasser les contraintes et s'arreter là.
-		       pas d'appel recursif donc on a toujours des nouvelles partitions
-		       on calcule les apples, on les associe à faux (on a pas inliné encore)
-		       et le statue global est à faux (on a rien resolu)
-
-		       comment resoud-on ?
-		       on ne doit se focaliser que sur les partitions courantes. C'est elle qui sont en conflit
-		       pour chaque, on pick un call et on l'inlinee
-		       apres on appel stage1/stage2 pour faire le scheudling du node
-
-
-		    *)
+    let previous_calls =
+      match matched with
+      | [] -> []
+      | [_ (*vars*), calls, _ (*status*)] -> List.map fst calls (* we just keep the calls *)
+      | _ -> (* variable should not belong to two different algrebraic loops. At least I
+		hope so! *)
+	 assert false
+    in
+    let match_previous (eq, expr, fun_id) =
+      List.exists
+	(fun (_, expr', _) -> expr'.expr_tag = expr.expr_tag)
+	previous_calls
+    in
+    (* let match_inlined (_, expr, _) = is_expr_inlined nd expr in *)
+    
+    (* let previous_inlined, previous_no_inlined = List.partition match_inlined previous_calls in *)
+    (* Format.eprintf "Previous calls: @[<v 0>inlined: {%a}@ no inlined: {%a}@ @]@ " *)
+    (*   pp_calls previous_inlined *)
+    (*   pp_calls previous_no_inlined *)
 
-	 
-  ) (false, existing_al) partitions
+    (* ; *)
+    
+    let current_calls = CycleResolution.resolve nd part in
+    (* Format.eprintf "Current calls: %a" pp_calls current_calls; *)
+    (* Filter out calls from current_calls that were not already in previous calls *)
+    let current_calls = List.filter (fun c -> not (match_previous c)) current_calls
+    in
+    (* Format.eprintf "Current new calls (no old ones): %a" pp_calls current_calls; *)
+    let calls = previous_calls @ current_calls in
+    (* Format.eprintf "All calls (previous + new): %a" pp_calls calls; *)
+    
+    (* Filter out already inlined calls: actually they should not appear
+       ... since they were inlined. We keep it for safety. *)
+    let _ (* already_inlined *), possible_resolution =
+      List.partition (fun (_, expr, _) -> is_expr_inlined nd expr) calls in
+    (* Inlining the first uninlined call *)
+    match possible_resolution with
+    | (fun_id, expr, eq)::_ -> ((* One could inline expr *)
+      Log.report ~level:2 (fun fmt-> Format.fprintf fmt "inlining call to %s@ " fun_id); 
+      (* Forcing the expr to be inlined *)
+      let _ = inline_expr nd expr in
+      (* Format.eprintf "Making sure that the inline list evolved: inlined = {%a}" *)
+      (* 	pp_calls  *)
+      (* ; *)
+      true, (* we have to rerun to see if the inlined expression solves the issue *)
+      max_inlines - 1,
+      (
+	part,
+	List.map (fun ((eq, expr2, fcn_name) as call)->  call, (expr2.expr_tag = expr.expr_tag)) calls,
+	true (* TODO was false. Should be put it true and expect a final
+		scheduling to change it to false in case of failure ? *) (*
+									   Status is nok, LA is unsolved yet *)
+	  
+      )::non_matched
+    )	 
+    | [] -> (* No more calls to inline: algebraic loop is not solvable *)
+       rerun, (* we don't force rerun since the current node is not solvable *)
+      max_inlines,
+      (
+	part, (* initial list of troublesogme variables *)
+	List.map (fun ((eq, expr, fcn_name) as call) ->  call, false) calls,
+	false (* Status is nok, LA is unsolved *)
+      )::non_matched 
+	
+  ) (false, max_inlines, existing_al) partitions
   in
   (* if partition an already identified al ? *)
   (* if ISet.of_list partition *)
-
-  (* if rerun then *)
-  (*   assert false (\* the missing part, redo stage 1, ... with the updated inline flags *\) *)
-  (* ; *)
-  (* TODO xxx il faut ici ajouter le inline sur le 1er appel,
-     puis reafaire le stage1 et scheduling du noeud *)
-  Some(nd, al)
-
+  if rerun && max_inlines > 0 then
+    (* At least one partition could be improved: we try to inline the calls and reschedule the node. *)
+    try
+      Log.report ~level:2 (fun fmt -> Format.fprintf fmt "rescheduling node with new inlined call@ ");
+      let _ = fast_stages_processing prog in
+      (* If everything went up to here, the problem is solved! All associated
+	 alarms are mapped to valid status. *)
+      let al = List.map (fun (v,c,_) -> v,c,true) al in
+      Log.report ~level:2 (fun fmt -> Format.fprintf fmt "AL solved@ ");
+      Some(nd, al), max_inlines
+    with Causality.Error (Causality.DataCycle partitions2) -> (
+      Log.report ~level:3 (fun fmt -> Format.fprintf fmt "AL not solved yet. Further processing.@ ");
+      solving_node max_inlines prog nd al partitions2
+    )
+  else ((* No rerun, we return the current node and computed alarms *)
+    Log.report ~level:3 (fun fmt -> Format.fprintf fmt "AL not solved yet. Stopping.@ ");
+    Some(nd, al), max_inlines
+  )
+      
 (** This function takes a prog and returns (prog', status, alarms)
     where prog' is a modified prog with some locally inlined calls
     status is true is the final prog' is schedulable, ie no algebraic loop
@@ -214,42 +271,51 @@ let rec solving_node nd existing_al partitions =
     Alarms contain the list of inlining performed or advised for each node. 
     This could be provided as a feedback to the user.
 *)
-let clean_al prog =
+let clean_al prog : program * bool * report =
+  let max_inlines = !Options.al_nb_max in
 (* We iterate over each node *)
-  let prog, al_list =
+  let _, prog, al_list =
     List.fold_right (
-      fun top (prog_accu, al_list) ->
+      fun top (max_inlines, prog_accu, al_list) ->
 	match top.top_decl_desc with
 	| Node nd -> (
-	  let error =
+	  let error, max_inlines =
 	    try (* without exception the node is schedulable; nothing to declare *)
 	      let _ = Scheduling.schedule_node nd in
-	      None
-	    with Causality.Error (Causality.DataCycle partitions) -> solving_node nd [] partitions
+	      None, max_inlines
+	    with Causality.Error (Causality.DataCycle partitions) -> (
+	      Log.report ~level:2 (fun fmt -> Format.fprintf fmt "@[<v 2>AL in node %s@ " nd.node_id);
+	      let error, max_inlines = solving_node max_inlines prog nd [] partitions in
+	      Log.report ~level:2 (fun fmt -> Format.fprintf fmt "@ @]");
+	      error, max_inlines
+	    )
 	  in
 	  match error with
-	  | None -> top::prog_accu, al_list (* keep it as is *)
+	  | None -> max_inlines, top::prog_accu, al_list (* keep it as is *)
 	  | Some (nd, al) ->
 	     (* returning the updated node, possible solved, as well as the
 		generated alarms *)
-	     {top with top_decl_desc = Node nd}::prog_accu, (nd, al)::al_list 
+	     max_inlines,
+	     {top with top_decl_desc = Node nd}::prog_accu,
+	    (nd, al)::al_list 
 	)	   
-	| _ -> top::prog_accu, al_list
-    ) prog ([], []) 
+	| _ -> max_inlines, top::prog_accu, al_list
+    ) prog (max_inlines, [], []) 
   in
   prog, List.for_all al_is_solved al_list, al_list
 
 
 (* (ident list * (ident * expr* bool) list * bool) *)
-let pp_al fmt (partition, calls, status) =
+let pp_al nd fmt (partition, calls, status) =
+  let open Format in
   fprintf fmt "@[<v 0>";
   fprintf fmt "variables in the alg. loop: @[<hov 0>%a@]@ "
     (fprintf_list ~sep:",@ " pp_print_string) partition;
   fprintf fmt "@ involved node calls: @[<v 0>%a@]@ "
     (fprintf_list ~sep:",@ "
-       (fun fmt (funid, expr, status) ->
+       (fun fmt ((funid, expr, eq), status) ->
 	 fprintf fmt "%s" funid;
-	 if status && is_expr_inlined expr then fprintf fmt " (inlining it solves the alg. loop)";
+	 if status && is_expr_inlined nd expr then fprintf fmt " (inlining it solves the alg. loop)";
        )
     ) calls;
   fprintf fmt "@]"
@@ -265,26 +331,54 @@ let pp_al fmt (partition, calls, status) =
     
     
 let pp_report fmt report =
-  fprintf fmt "@.Algebraic loops in nodes@.";
+  let open Format in
   fprintf_list ~sep:"@."
     (fun fmt (nd, als) ->
       let top = Corelang.node_from_name (nd.node_id) in
-      Error.pp_error top.top_decl_loc
+      let pp =
+	if not !Options.solve_al || List.exists (fun (_,_,valid) -> not valid) als then
+	  Error.pp_error (* at least one failure: erroneous node *)
+	else
+	  Error.pp_warning (* solvable cases: warning only *)
+      in
+      pp top.top_decl_loc
 	(fun fmt -> 
-	  fprintf fmt "node %s: {@[<v 0>%a@]}"
+	  fprintf fmt "algebraic loop in node %s: {@[<v 0>%a@]}"
 	    nd.node_id
-	    (fprintf_list ~sep:"@ " pp_al) als
+	    (fprintf_list ~sep:"@ " (pp_al nd)) als
 	)
     ) fmt report;
   fprintf fmt "@."
     
 
-let analyze prog =
+
+let analyze cpt prog =
+  Log.report ~level:1 (fun fmt ->
+    Format.fprintf fmt "@[<v 2>Algebraic loop detected: ";
+    if !Options.solve_al then Format.fprintf fmt "solving mode actived";
+    Format.fprintf fmt "@ ";    
+  );
   let prog, status_ok, report = clean_al prog in
-  if !Options.solve_al && status_ok then
-    Scheduling.schedule_prog prog
-  else (
-    (* TODO create a report *)
-    Format.eprintf "%a" pp_report report;
-    raise (Corelang.Error (Location.dummy_loc, Error.AlgebraicLoop))
-  )
+  
+  let res =
+    if cpt > 0 && !Options.solve_al && status_ok then (
+      try
+	fast_stages_processing prog
+      with _ -> assert false (* Should not happen since the error has been
+				catched already *)
+    )
+    else (
+      (* We stop with unresolved AL *)(* TODO create a report *)
+      (* Printing the report on stderr *)
+      Format.eprintf "%a" pp_report report;
+      raise (Corelang.Error (Location.dummy_loc, Error.AlgebraicLoop))
+    )
+  in
+  (* Printing the report on stderr *)
+  Format.eprintf "%a" pp_report report;
+  res
+    
+let analyze prog =
+  analyze !Options.al_nb_max prog
+    
+    
diff --git a/src/corelang.ml b/src/corelang.ml
index 67c7030a..694ba1b5 100755
--- a/src/corelang.ml
+++ b/src/corelang.ml
@@ -178,7 +178,8 @@ let update_expr_annot node_id e annot =
   List.iter (fun (key, _) -> 
     Annotations.add_expr_ann node_id e.expr_tag key
   ) annot.annots;
-  { e with expr_annot = merge_expr_annot e.expr_annot (Some annot) }
+  e.expr_annot <- merge_expr_annot e.expr_annot (Some annot);
+  e
 
 
 let mkinstr ?lustre_expr ?lustre_eq i =
diff --git a/src/error.ml b/src/error.ml
index 1d4fe3cd..b5a67432 100644
--- a/src/error.ml
+++ b/src/error.ml
@@ -50,10 +50,9 @@ let return_code kind =
       "library %s.lusic has a different version number and may crash compiler.@.Please recompile the corresponding interface or source file.@."
       sym
 
-let pp_warning loc warning_code pp_msg =
-  Format.eprintf "%a@.Warning %i: %t@."
+let pp_warning loc pp_msg =
+  Format.eprintf "%a@.Warning: %t@."
     Location.pp_loc loc
-    warning_code
     pp_msg
 
 let pp_error loc pp_msg =
diff --git a/src/inliner.ml b/src/inliner.ml
index 05f1394e..0aab3f3d 100644
--- a/src/inliner.ml
+++ b/src/inliner.ml
@@ -229,7 +229,8 @@ let rec inline_expr ?(selection_on_annotation=false) expr locals node nodes =
 						    as arguments nodes *)
       (not selection_on_annotation || is_inline_expr expr) (* and if selection on annotation is activated, 
 							      it is explicitely inlined here *)
-    then 
+    then (
+      (* Format.eprintf "Inlining call to %s in expression %a@." id Printers.pp_expr expr; *)
       (* The node should be inlined *)
       (* let _ =     Format.eprintf "Inlining call to %s@." id in *)
       let called = try List.find (check_node_name id) nodes 
@@ -239,6 +240,7 @@ let rec inline_expr ?(selection_on_annotation=false) expr locals node nodes =
       let expr, locals', eqs'', asserts'', annots'' = 
 	inline_call called' expr.expr_loc expr.expr_tag args' reset locals' node in
       expr, locals', eqs'@eqs'', asserts'@asserts'', annots'@annots''
+    )
     else 
       (* let _ =     Format.eprintf "Not inlining call to %s@." id in *)
       { expr with expr_desc = Expr_appl(id, args', reset)}, 
@@ -458,23 +460,58 @@ let global_inline basename prog type_env clock_env =
 *)
   res
 
-let local_inline basename prog type_env clock_env =
+let pp_inline_calls fmt prog =
   let local_anns = Annotations.get_expr_annotations keyword in
-  if local_anns != [] then (
-    let nodes_with_anns = List.fold_left (fun accu (k, _) -> ISet.add k accu) ISet.empty local_anns in
-    ISet.iter (fun node_id -> Format.eprintf "Node %s has local expression annotations@." node_id) nodes_with_anns;
-    List.fold_right (fun top accu -> 
-      ( match top.top_decl_desc with
-      | Node nd when ISet.mem nd.node_id nodes_with_anns ->
-	{ top with top_decl_desc = Node (inline_node ~selection_on_annotation:true nd prog) }
-      | _ -> top
-      )::accu) prog []
-    
-)
- else
+  let nodes_with_anns = List.fold_left (fun accu (k, _) -> ISet.add k accu) ISet.empty local_anns in
+  Format.fprintf fmt "@[<v 0>Inlined expresssions in node (by tags):@ %a@]"
+    (fprintf_list ~sep:""
+       (fun fmt top ->
+	 match top.top_decl_desc with
+	 | Node nd when ISet.mem nd.node_id nodes_with_anns ->
+	    Format.fprintf fmt "%s: {@[<v 0>%a}@]@ "
+	      nd.node_id
+	      (fprintf_list ~sep:"@ " (fun fmt tag -> Format.fprintf fmt "%i" tag))
+	      (List.fold_left
+		 (fun accu (id, tag) -> if id = nd.node_id then tag::accu else accu)
+		 []
+		 local_anns
+	      )	      
+	 (* | Node nd -> Format.fprintf fmt "%s: no inline annotations" nd.node_id *)
+	 | _ -> ()
+     ))
+    prog
+  
+  
+let local_inline prog (* type_env clock_env *) =
+  Log.report ~level:2 (fun fmt -> Format.fprintf fmt ".. @[<v 2>Inlining@,");
+  let local_anns = Annotations.get_expr_annotations keyword in
+  let prog = 
+    if local_anns != [] then (
+      let nodes_with_anns = List.fold_left (fun accu (k, _) -> ISet.add k accu) ISet.empty local_anns in
+      ISet.iter (fun node_id -> Log.report ~level:2 (fun fmt -> Format.fprintf fmt "Node %s has local expression annotations@ " node_id))
+	nodes_with_anns;
+      List.fold_right (fun top accu -> 
+	( match top.top_decl_desc with
+	| Node nd when ISet.mem nd.node_id nodes_with_anns ->
+	   Log.report ~level:2 (fun fmt -> Format.fprintf fmt "[local inline] Processing node %s@ " nd.node_id);
+	  let inlined_node = inline_node ~selection_on_annotation:true nd prog in
+	  (* Format.eprintf "Before inline@.%a@.After:@.%a@." *)
+	  (*   Printers.pp_node nd *)
+	  (*   Printers.pp_node inlined_node; *)
+	  { top with top_decl_desc = Node inlined_node }
+	    
+	| _ -> top
+	)::accu) prog []
+	
+    )
+    else (
+      Log.report ~level:2 (fun fmt -> Format.fprintf fmt "No local inline information!@ ");
+      prog
+    )
+  in
+  Log.report ~level:2 (fun fmt -> Format.fprintf fmt "@]@,");
   prog
 
-
 (* Local Variables: *)
 (* compile-command:"make -C .." *)
 (* End: *)
diff --git a/src/machine_code.ml b/src/machine_code.ml
index 0e8a5305..eca08aec 100644
--- a/src/machine_code.ml
+++ b/src/machine_code.ml
@@ -156,7 +156,7 @@ let rec is_const_value v =
 
 (* Returns the declared stateless status and the computed one. *)
 let get_stateless_status m =
- (m.mname.node_dec_stateless, Utils.desome m.mname.node_stateless)
+ (m.mname.node_dec_stateless, try Utils.desome m.mname.node_stateless with _ -> failwith ("stateless status of machine " ^ m.mname.node_id ^ " not computed"))
 
 let is_input m id =
   List.exists (fun o -> o.var_id = id.var_id) m.mstep.step_inputs
diff --git a/src/main_lustre_compiler.ml b/src/main_lustre_compiler.ml
index 3b78406e..ba623572 100644
--- a/src/main_lustre_compiler.ml
+++ b/src/main_lustre_compiler.ml
@@ -116,7 +116,7 @@ let stage1 prog dirname basename =
       Inliner.global_inline basename prog type_env clock_env
     else (* if !Option.has_local_inline *)
       [],
-      Inliner.local_inline basename prog type_env clock_env
+      Inliner.local_inline prog (* type_env clock_env *)
   in
 
   (* Checking stateless/stateful status *)
@@ -241,7 +241,7 @@ let stage1 prog dirname basename =
 let stage2 prog =    
   (* Computation of node equation scheduling. It also breaks dependency cycles
      and warns about unused input or memory variables *)
-  Log.report ~level:1 (fun fmt -> fprintf fmt ".. scheduling@,");
+  Log.report ~level:1 (fun fmt -> fprintf fmt ".. @[<v 2>scheduling@ ");
   let prog, node_schs =
     try 
       Scheduling.schedule_prog prog
@@ -254,7 +254,7 @@ let stage2 prog =
   Log.report ~level:3 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Scheduling.pp_fanin_table node_schs);
   Log.report ~level:5 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Scheduling.pp_dep_graph node_schs);
   Log.report ~level:3 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Printers.pp_prog prog);
-
+  Log.report ~level:1 (fun fmt -> fprintf fmt "@]@ ");
 
   (* TODO Salsa optimize prog: 
      - emits warning for programs with pre inside expressions
diff --git a/src/main_lustre_testgen.ml b/src/main_lustre_testgen.ml
index 798e5477..4db9362a 100644
--- a/src/main_lustre_testgen.ml
+++ b/src/main_lustre_testgen.ml
@@ -45,7 +45,7 @@ let stage1 prog dirname basename =
       Inliner.global_inline basename prog type_env clock_env
     else (* if !Option.has_local_inline *)
       [],
-      Inliner.local_inline basename prog type_env clock_env
+      Inliner.local_inline prog (* type_env clock_env *)
   in
 
   check_stateless_decls prog;
diff --git a/src/options.ml b/src/options.ml
index 91a2261f..f70e0b24 100755
--- a/src/options.ml
+++ b/src/options.ml
@@ -53,7 +53,8 @@ let gen_mcdc = ref false
 let no_mutation_suffix = ref false
 
 let solve_al = ref false
-
+let al_nb_max = ref 15
+  
 (* Local Variables: *)
 (* compile-command:"make -C .." *)
 (* End: *)
diff --git a/src/options_management.ml b/src/options_management.ml
index 33d74b8a..1a052fb4 100644
--- a/src/options_management.ml
+++ b/src/options_management.ml
@@ -81,6 +81,8 @@ let common_options =
     "-node", Arg.Set_string main_node, "specifies the \x1b[4mmain\x1b[0m node";
     "-print-types", Arg.Set print_types, "prints node types";
     "-print-clocks", Arg.Set print_clocks, "prints node clocks";
+    "-algebraic-loop-solve", Arg.Set solve_al, "try to solve algebraic loops"; 
+    "-algebraic-loop-max", Arg.Set_int al_nb_max, "try to solve \x1b[4mnb\x1b[0m number of algebraic loops  <default: 15>"; 
     "-verbose", Arg.Set_int verbose_level, "changes verbose \x1b[4mlevel\x1b[0m <default: 1>";
     "-version", Arg.Unit print_version, " displays the version";
   ]
-- 
GitLab