Project

General

Profile

Download (35.6 KB) Statistics
| Branch: | Tag: | Revision:
1
(********************************************************************)
2
(*                                                                  *)
3
(*  The LustreC compiler toolset   /  The LustreC Development Team  *)
4
(*  Copyright 2012 -    --   ONERA - CNRS - INPT                    *)
5
(*                                                                  *)
6
(*  LustreC is free software, distributed WITHOUT ANY WARRANTY      *)
7
(*  under the terms of the GNU Lesser General Public License        *)
8
(*  version 2.1.                                                    *)
9
(*                                                                  *)
10
(********************************************************************)
11

    
12
open Utils
13
open Lustre_types
14
open Spec_types
15
open Machine_code_types
16
open Corelang
17
open Causality
18
open Machine_code_common
19
module Mpfr = Lustrec_mpfr
20

    
21
let pp_elim m fmt elim =
22
  IMap.pp ~comment:"/* elim table: */" (pp_val m) fmt elim
23

    
24
let eliminate_var_decl elim m v f a =
25
  if is_memory m v then a
26
  else try
27
      f (IMap.find v.var_id elim)
28
    with Not_found -> a
29

    
30
let rec eliminate_val m elim expr =
31
  let eliminate_val = eliminate_val m in
32
  match expr.value_desc with
33
  | Var v ->
34
    eliminate_var_decl elim m v (fun x -> x) expr
35
  | Fun (id, vl) ->
36
    { expr with value_desc = Fun (id, List.map (eliminate_val elim) vl) }
37
  | Array vl ->
38
    { expr with value_desc = Array (List.map (eliminate_val elim) vl) }
39
  | Access (v1, v2) ->
40
    {
41
      expr with
42
      value_desc = Access (eliminate_val elim v1, eliminate_val elim v2);
43
    }
44
  | Power (v1, v2) ->
45
    {
46
      expr with
47
      value_desc = Power (eliminate_val elim v1, eliminate_val elim v2);
48
    }
49
  | Cst _ | ResetFlag ->
50
    expr
51

    
52
let eliminate_expr m elim e =
53
  let e_val = eliminate_val m elim in
54
  match e with
55
  | Val v -> Val (e_val v)
56
  | Var v -> eliminate_var_decl elim m v (fun x -> Val x) e
57
  | _ -> e
58

    
59
let eliminate_pred m elim = function
60
  (* the transition is local to the machine *)
61
  | Transition (s, f, inst, Some i, vars, r, mems, insts) ->
62
    let vars = List.filter_map (function
63
        | Spec_types.Val v ->
64
          begin match v.value_desc with
65
            | Var vd when IMap.mem vd.var_id elim -> None
66
            | _ -> Some (Val v)
67
          end
68
        | Spec_types.Var vd when IMap.mem vd.var_id elim -> None
69
        | e -> Some (eliminate_expr m elim e)) vars
70
    in
71
    Transition (s, f, inst, Some i, vars, r, mems, insts)
72

    
73
  (* the transition is not local to the machine *)
74
  | Transition (s, f, inst, None, vars, r, mems, insts) ->
75
    let vars = List.map (eliminate_expr m elim) vars in
76
    Transition (s, f, inst, None, vars, r, mems, insts)
77

    
78
  | p ->
79
    p
80

    
81
let rec eliminate_formula m elim =
82
  let e_expr = eliminate_expr m elim in
83
  let e_instr i = eliminate_formula m elim i in
84
  let e_pred = eliminate_pred m elim in
85
  function
86
  | Equal (e1, e2) ->
87
    Equal (e_expr e1, e_expr e2)
88
  | GEqual (e1, e2) ->
89
    GEqual (e_expr e1, e_expr e2)
90
  | And f ->
91
    And (List.map e_instr f)
92
  | Or f ->
93
    Or (List.map e_instr f)
94
  | Imply (a, b) ->
95
    Imply (e_instr a, e_instr b)
96
  | Exists (xs, a) ->
97
    let xs = List.filter (fun vd -> not (IMap.mem vd.var_id elim)) xs in
98
    Exists (xs, eliminate_formula m elim a)
99
  | Forall (xs, a) ->
100
    let xs = List.filter (fun vd -> not (IMap.mem vd.var_id elim)) xs in
101
    Forall (xs, eliminate_formula m elim a)
102
  | Ternary (e, a, b) ->
103
    Ternary (e_expr e, e_instr a, e_instr b)
104
  | Predicate p ->
105
    Predicate (e_pred p)
106
  | ExistsMem (f, a, b) ->
107
    ExistsMem (f, e_instr a, e_instr b)
108
  | Value v ->
109
    Value (eliminate_val m elim v)
110
  | f ->
111
    f
112

    
113
let rec eliminate m elim instr =
114
  let e_val = eliminate_val m elim in
115
  let instr = { instr with instr_spec = List.map (eliminate_formula m elim) instr.instr_spec } in
116
  match get_instr_desc instr with
117
  | MLocalAssign (i, v) ->
118
    update_instr_desc instr (MLocalAssign (i, e_val v))
119
  | MStateAssign (i, v) ->
120
    update_instr_desc instr (MStateAssign (i, e_val v))
121
  | MSetReset _
122
  | MNoReset _
123
  | MClearReset
124
  | MResetAssign _
125
  | MSpec _
126
  | MComment _ ->
127
    instr
128
  | MStep (il, i, vl) ->
129
    update_instr_desc instr (MStep (il, i, List.map e_val vl))
130
  | MBranch (g, hl) ->
131
    update_instr_desc
132
      instr
133
      (MBranch
134
         ( e_val g,
135
           List.map (fun (l, il) -> l, List.map (eliminate m elim) il) hl ))
136

    
137
let rec fv_value m s v =
138
  match v.value_desc with
139
  | Var v ->
140
    if is_memory m v then s else VSet.add v s
141
  | Fun (_, vl)
142
  | Array vl ->
143
    List.fold_left (fv_value m) s vl
144
  | Access (v1, v2)
145
  | Power (v1, v2) ->
146
    fv_value m (fv_value m s v1) v2
147
  | _ -> s
148

    
149
let fv_expr m s = function
150
  | Val v -> fv_value m s v
151
  | Var v ->
152
    if is_memory m v then s else VSet.add v s
153
  | _ -> s
154

    
155
let fv_predicate m s = function
156
  | Transition (_, _, _, _, vars, _, _, _) ->
157
    List.fold_left (fv_expr m) s vars
158
  | _ -> s
159

    
160
let rec fv_formula m s = function
161
  | Equal (e1, e2)
162
  | GEqual (e1, e2) ->
163
    fv_expr m (fv_expr m s e1) e2
164
  | And f
165
  | Or f ->
166
    List.fold_left (fv_formula m) s f
167
  | Imply (a, b)
168
  | ExistsMem (_, a, b) ->
169
    fv_formula m (fv_formula m s a) b
170
  | Exists (xs, a)
171
  | Forall (xs, a) ->
172
    VSet.filter (fun v -> not (List.mem v xs)) (fv_formula m s a)
173
  | Ternary (e, a, b) ->
174
    fv_expr m (fv_formula m (fv_formula m s a) b) e
175
  | Predicate p ->
176
    fv_predicate m s p
177
  | Value v ->
178
    fv_value m s v
179
  | _ -> s
180

    
181
let eliminate_transition m elim t =
182
  let tvars = List.filter (fun vd -> not (IMap.mem vd.var_id elim)) t.tvars in
183
  let tformula = eliminate_formula m elim t.tformula in
184
  let fv = VSet.(elements (diff (fv_formula m empty tformula) (of_list tvars))) in
185
  let tformula = Exists (fv, tformula) in
186
  { t with
187
    tvars;
188
    tformula
189
  }
190

    
191
(* XXX: UNUSED *)
192
(* let eliminate_dim elim dim =
193
 *   Dimension.expr_replace_expr
194
 *     (fun v ->
195
 *       try dimension_of_value (IMap.find v elim)
196
 *       with Not_found -> mkdim_ident dim.dim_loc v)
197
 *     dim *)
198

    
199
(* 8th Jan 2016: issues when merging salsa with horn_encoding: The following
200
   functions seem unsused. They have to be adapted to the new type for expr *)
201

    
202
let unfold_expr_offset m offset expr =
203
  List.fold_left
204
    (fun res -> function
205
      | Index i ->
206
        mk_val
207
          (Access (res, value_of_dimension m i))
208
          (Types.array_element_type res.value_type)
209
      | Field _ ->
210
        Format.eprintf "internal error: not yet implemented !";
211
        assert false)
212
    expr
213
    offset
214

    
215
let rec simplify_cst_expr m offset typ cst =
216
  match offset, cst with
217
  | [], _ ->
218
    mk_val (Cst cst) typ
219
  | Index i :: q, Const_array cl when Dimension.is_const i ->
220
    let elt_typ = Types.array_element_type typ in
221
    simplify_cst_expr m q elt_typ (List.nth cl (Dimension.size_const i))
222
  | Index i :: q, Const_array cl ->
223
    let elt_typ = Types.array_element_type typ in
224
    unfold_expr_offset
225
      m
226
      [ Index i ]
227
      (mk_val (Array (List.map (simplify_cst_expr m q elt_typ) cl)) typ)
228
  | Field f :: q, Const_struct fl ->
229
    let fld_typ = Types.struct_field_type typ f in
230
    simplify_cst_expr m q fld_typ (List.assoc f fl)
231
  | _ ->
232
    Format.eprintf
233
      "internal error: Optimize_machine.simplify_cst_expr %a@."
234
      Printers.pp_const
235
      cst;
236
    assert false
237

    
238
let simplify_expr_offset m expr =
239
  let rec simplify offset expr =
240
    match offset, expr.value_desc with
241
    | Field _ :: _, _ ->
242
      failwith "not yet implemented"
243
    | _, Fun (id, vl) when Basic_library.is_value_internal_fun expr ->
244
      mk_val (Fun (id, List.map (simplify offset) vl)) expr.value_type
245
    | _, Fun _ | _, Var _ ->
246
      unfold_expr_offset m offset expr
247
    | _, Cst cst ->
248
      simplify_cst_expr m offset expr.value_type cst
249
    | _, Access (expr, i) ->
250
      simplify (Index (dimension_of_value i) :: offset) expr
251
    | _, ResetFlag ->
252
      expr
253
    | [], _ ->
254
      expr
255
    | Index _ :: q, Power (expr, _) ->
256
      simplify q expr
257
    | Index i :: q, Array vl when Dimension.is_const i ->
258
      simplify q (List.nth vl (Dimension.size_const i))
259
    | Index i :: q, Array vl ->
260
      unfold_expr_offset
261
        m
262
        [ Index i ]
263
        (mk_val (Array (List.map (simplify q) vl)) expr.value_type)
264
    (*Format.eprintf "simplify_expr %a %a = %a@." pp_val expr
265
      (Utils.fprintf_list ~sep:"" Printers.pp_offset) offset pp_val res; res)
266
      with e -> (Format.eprintf "simplify_expr %a %a = <FAIL>@." pp_val expr
267
      (Utils.fprintf_list ~sep:"" Printers.pp_offset) offset; raise e*)
268
  in
269
  simplify [] expr
270

    
271
let rec simplify_instr_offset m instr =
272
  match get_instr_desc instr with
273
  | MLocalAssign (v, expr) ->
274
    update_instr_desc instr (MLocalAssign (v, simplify_expr_offset m expr))
275
  | MStateAssign (v, expr) ->
276
    update_instr_desc instr (MStateAssign (v, simplify_expr_offset m expr))
277
  | MSetReset _
278
  | MNoReset _
279
  | MClearReset
280
  | MResetAssign _
281
  | MSpec _
282
  | MComment _ ->
283
    instr
284
  | MStep (outputs, id, inputs) ->
285
    update_instr_desc
286
      instr
287
      (MStep (outputs, id, List.map (simplify_expr_offset m) inputs))
288
  | MBranch (cond, brl) ->
289
    update_instr_desc
290
      instr
291
      (MBranch
292
         ( simplify_expr_offset m cond,
293
           List.map (fun (l, il) -> l, simplify_instrs_offset m il) brl ))
294

    
295
and simplify_instrs_offset m instrs = List.map (simplify_instr_offset m) instrs
296

    
297
(* XXX: UNUSED *)
298
(* let is_scalar_const c =
299
 *   match c with Const_real _ | Const_int _ | Const_tag _ -> true | _ -> false *)
300

    
301
(* An instruction v = expr may (and will) be unfolded iff: - either expr is
302
   atomic (no complex expressions, only const, vars and array/struct accesses) -
303
   or v has a fanin <= 1 (used at most once) *)
304
let is_unfoldable_expr fanin expr =
305
  let rec unfold_const offset cst =
306
    match offset, cst with
307
    | _, Const_int _ | _, Const_real _ | _, Const_tag _ ->
308
      true
309
    | Field f :: q, Const_struct fl ->
310
      unfold_const q (List.assoc f fl)
311
    | [], Const_struct _ ->
312
      false
313
    | Index i :: q, Const_array cl when Dimension.is_const i ->
314
      unfold_const q (List.nth cl (Dimension.size_const i))
315
    | _, Const_array _ ->
316
      false
317
    | _ ->
318
      assert false
319
  in
320
  let rec unfold offset expr =
321
    match offset, expr.value_desc with
322
    | _, Cst cst ->
323
      unfold_const offset cst
324
    | _, Var _ ->
325
      true
326
    | [], Power _ | [], Array _ ->
327
      false
328
    | Index _ :: q, Power (v, _) ->
329
      unfold q v
330
    | Index i :: q, Array vl when Dimension.is_const i ->
331
      unfold q (List.nth vl (Dimension.size_const i))
332
    | _, Array _ ->
333
      false
334
    | _, Access (v, i) ->
335
      unfold (Index (dimension_of_value i) :: offset) v
336
    | _, Fun (_, vl) when fanin < 2 && Basic_library.is_value_internal_fun expr
337
      ->
338
      List.for_all (unfold offset) vl
339
    | _, Fun _ ->
340
      false
341
    | _ ->
342
      assert false
343
  in
344
  unfold [] expr
345

    
346
let basic_unfoldable_assign fanin v expr =
347
  try
348
    let d = Hashtbl.find fanin v.var_id in
349
    is_unfoldable_expr d expr
350
  with Not_found -> false
351

    
352
let unfoldable_assign fanin v expr =
353
  (if !Options.mpfr then Mpfr.unfoldable_value expr else true)
354
  && basic_unfoldable_assign fanin v expr
355

    
356
let merge_elim elim1 elim2 =
357
  let merge _ e1 e2 =
358
    match e1, e2 with
359
    | Some e1, Some e2 ->
360
      if e1 = e2 then Some e1 else None
361
    | _, Some e2 ->
362
      Some e2
363
    | Some e1, _ ->
364
      Some e1
365
    | _ ->
366
      None
367
  in
368
  IMap.merge merge elim1 elim2
369

    
370
(* see if elim has to take in account the provided instr: if so, update elim and
371
   return the remove flag, otherwise, the expression should be kept and elim is
372
   left untouched *)
373
let rec instrs_unfold m fanin elim instrs =
374
  let elim, rev_instrs =
375
    List.fold_left
376
      (fun (elim, instrs) instr ->
377
        (* each subexpression in instr that could be rewritten by the elim set
378
           is rewritten *)
379
        let instr = eliminate m (IMap.map fst elim) instr in
380
        (* if instr is a simple local assign, then (a) elim is simplified with
381
           it (b) it is stored as the elim set *)
382
        instr_unfold m fanin instrs elim instr)
383
      (elim, [])
384
      instrs
385
  in
386
  elim, List.rev rev_instrs
387

    
388
and instr_unfold m fanin instrs (elim : (value_t * eq) IMap.t) instr =
389
  (* Format.eprintf "SHOULD WE STORE THE EXPRESSION IN INSTR %a TO ELIMINATE
390
     IT@." pp_instr instr;*)
391
  match get_instr_desc instr with
392
  (* Simple cases*)
393
  | MStep ([ v ], id, vl)
394
    when Basic_library.is_value_internal_fun (mk_val (Fun (id, vl)) v.var_type)
395
    ->
396
    instr_unfold
397
      m
398
      fanin
399
      instrs
400
      elim
401
      (update_instr_desc
402
         instr
403
         (MLocalAssign (v, mk_val (Fun (id, vl)) v.var_type)))
404
  | MLocalAssign (v, expr)
405
    when (not (is_clock_dec_type v.var_dec_type.ty_dec_desc))
406
         && unfoldable_assign fanin v expr ->
407
    (* we don't eliminate clock definitions *)
408
    let new_eq =
409
      Corelang.mkeq
410
        (desome instr.lustre_eq).eq_loc
411
        ([ v.var_id ], (desome instr.lustre_eq).eq_rhs)
412
    in
413
    IMap.add v.var_id (expr, new_eq) elim, instrs
414
  | MBranch (g, hl) when false ->
415
    let elim_branches =
416
      List.map (fun (h, l) -> h, instrs_unfold m fanin elim l) hl
417
    in
418
    let elim, branches =
419
      List.fold_right
420
        (fun (h, (e, l)) (elim, branches) ->
421
          merge_elim elim e, (h, l) :: branches)
422
        elim_branches
423
        (elim, [])
424
    in
425
    elim, update_instr_desc instr (MBranch (g, branches)) :: instrs
426
  | _ ->
427
    elim, instr :: instrs
428
(* default case, we keep the instruction and do not modify elim *)
429

    
430
(** We iterate in the order, recording simple local assigns in an accumulator 1.
431
    each expression is rewritten according to the accumulator 2. local assigns
432
    then rewrite occurrences of the lhs in the computed accumulator *)
433

    
434
let static_call_unfold elim (inst, (n, args)) =
435
  let replace v =
436
    try dimension_of_value (IMap.find v elim)
437
    with Not_found -> Dimension.mkdim_ident Location.dummy v
438
  in
439
  inst, (n, List.map (Dimension.expr_replace_expr replace) args)
440

    
441
(** Perform optimization on machine code: - iterate through step instructions
442
    and remove simple local assigns *)
443
let machine_unfold fanin elim machine =
444
  Log.report ~level:3 (fun fmt ->
445
      Format.fprintf
446
        fmt
447
        "machine_unfold %s %a@ "
448
        machine.mname.node_id
449
        (pp_elim machine)
450
        (IMap.map fst elim));
451
  let elim_consts, mconst = instrs_unfold machine fanin elim machine.mconst in
452
  let elim_vars, instrs =
453
    instrs_unfold machine fanin elim_consts machine.mstep.step_instrs
454
  in
455
  let step_instrs = simplify_instrs_offset machine instrs in
456
  let step_checks =
457
    List.map
458
      (fun (loc, check) ->
459
        loc, eliminate_val machine (IMap.map fst elim_vars) check)
460
      machine.mstep.step_checks
461
  in
462
  let step_locals =
463
    List.filter
464
      (fun v -> not (IMap.mem v.var_id elim_vars))
465
      machine.mstep.step_locals
466
  in
467
  let mtransitions = List.map (eliminate_transition machine (IMap.map fst elim_vars)) machine.mspec.mtransitions in
468
  let elim_consts = IMap.map fst elim_consts in
469
  let minstances =
470
    List.map (static_call_unfold elim_consts) machine.minstances
471
  in
472
  let mcalls = List.map (static_call_unfold elim_consts) machine.mcalls in
473
  ( {
474
      machine with
475
      mstep =
476
        {
477
          machine.mstep with
478
          step_locals;
479
          step_instrs;
480
          step_checks;
481
        };
482
      mspec =
483
        {
484
          machine.mspec with
485
          mtransitions
486
        };
487
      mconst;
488
      minstances;
489
      mcalls;
490
    },
491
    elim_vars )
492

    
493
let instr_of_const top_const =
494
  let const = const_of_top top_const in
495
  let loc = const.const_loc in
496
  let id = const.const_id in
497
  let vdecl =
498
    mkvar_decl
499
      loc
500
      ( id,
501
        mktyp Location.dummy Tydec_any,
502
        mkclock loc Ckdec_any,
503
        true,
504
        None,
505
        None )
506
  in
507
  let vdecl = { vdecl with var_type = const.const_type } in
508
  let lustre_eq =
509
    mkeq loc ([ const.const_id ], mkexpr loc (Expr_const const.const_value))
510
  in
511
  mkinstr
512
    ~lustre_eq
513
    (MLocalAssign (vdecl, mk_val (Cst const.const_value) vdecl.var_type))
514

    
515
(* We do not perform this optimization on contract nodes since there is not
516
   explicit dependence btw variables and their use in contracts. *)
517
let machines_unfold consts node_schs machines =
518
  List.fold_right
519
    (fun m (machines, removed) ->
520
      let is_contract =
521
        match m.mspec.mnode_spec with Some (Contract _) -> true | _ -> false
522
      in
523
      if is_contract then m :: machines, removed
524
      else
525
        let fanin =
526
          (IMap.find m.mname.node_id node_schs).Scheduling_type.fanin_table
527
        in
528
        let elim_consts, _ =
529
          instrs_unfold m fanin IMap.empty (List.map instr_of_const consts)
530
        in
531
        let m, removed_m = machine_unfold fanin elim_consts m in
532
        m :: machines, IMap.add m.mname.node_id removed_m removed)
533
    machines
534
    ([], IMap.empty)
535

    
536
let get_assign_lhs instr =
537
  match get_instr_desc instr with
538
  | MLocalAssign (v, e) ->
539
    mk_val (Var v) e.value_type
540
  | MStateAssign (v, e) ->
541
    mk_val (Var v) e.value_type
542
  | _ ->
543
    assert false
544

    
545
let get_assign_rhs instr =
546
  match get_instr_desc instr with
547
  | MLocalAssign (_, e) | MStateAssign (_, e) ->
548
    e
549
  | _ ->
550
    assert false
551

    
552
let is_assign instr =
553
  match get_instr_desc instr with
554
  | MLocalAssign _ | MStateAssign _ ->
555
    true
556
  | _ ->
557
    false
558

    
559
let mk_assign m v e =
560
  match v.value_desc with
561
  | Var v ->
562
    if is_memory m v then MStateAssign (v, e) else MLocalAssign (v, e)
563
  | _ ->
564
    assert false
565

    
566
let rec assigns_instr instr assign =
567
  match get_instr_desc instr with
568
  | MLocalAssign (i, _) | MStateAssign (i, _) ->
569
    VSet.add i assign
570
  | MStep (ol, _, _) ->
571
    List.fold_right VSet.add ol assign
572
  | MBranch (_, hl) ->
573
    List.fold_right (fun (_, il) -> assigns_instrs il) hl assign
574
  | _ ->
575
    assign
576

    
577
and assigns_instrs instrs assign =
578
  List.fold_left (fun assign instr -> assigns_instr instr assign) assign instrs
579

    
580
(* and substitute_expr subst expr = match expr with | Var v -> (try IMap.find
581
   expr subst with Not_found -> expr) | Fun (id, vl) -> Fun (id, List.map
582
   (substitute_expr subst) vl) | Array(vl) -> Array(List.map (substitute_expr
583
   subst) vl) | Access(v1, v2) -> Access(substitute_expr subst v1,
584
   substitute_expr subst v2) | Power(v1, v2) -> Power(substitute_expr subst v1,
585
   substitute_expr subst v2) | Cst _ -> expr *)
586

    
587
(** Finds a substitute for [instr] in [instrs], i.e. another instr' with the
588
    same rhs expression. Then substitute this expression with the first assigned
589
    var *)
590
let subst_instr m subst instrs instr =
591
  (* Format.eprintf "subst instr: %a@." (pp_instr m) instr; *)
592
  let instr = eliminate m subst instr in
593
  let instr_v = get_assign_lhs instr in
594
  let instr_e = get_assign_rhs instr in
595
  try
596
    (* Searching for equivalent asssign *)
597
    let instr' =
598
      List.find
599
        (fun instr' -> is_assign instr' && get_assign_rhs instr' = instr_e)
600
        instrs
601
    in
602
    (* Registering the instr_v as instr'_v while replacing *)
603
    match instr_v.value_desc with
604
    | Var v -> (
605
      let instr'_v = get_assign_lhs instr' in
606
      if not (is_memory m v) then
607
        (* The current instruction defines a local variables, ie not memory, we
608
           can just record the relationship and continue *)
609
        IMap.add v.var_id instr'_v subst, instrs
610
      else
611
        (* The current instruction defines a memory. We need to keep the
612
           definition, simplified *)
613
        match instr'_v.value_desc with
614
        | Var v' ->
615
          if not (is_memory m v') then
616
            (* We define v' = v. Don't need to update the records. *)
617
            let instr =
618
              eliminate
619
                m
620
                subst
621
                (update_instr_desc instr (mk_assign m instr_v instr'_v))
622
            in
623
            subst, instr :: instrs
624
          else
625
            (* Last case, v', the lhs of the previous similar definition is,
626
               itself, a memory *)
627

    
628
            (* TODO regarder avec X. Il me semble qu'on peut faire plus
629
               simple: *)
630
            (* Filtering out the list of instructions: - we copy in the same
631
               order the list of instr in instrs (fold_right) - if the current
632
               instr is this instr' then apply the elimination with v' -> v on
633
               instr' before recording it as an instruction. *)
634
            let subst_v' = IMap.add v'.var_id instr_v IMap.empty in
635
            let instrs' =
636
              snd
637
                (List.fold_right
638
                   (fun instr (ok, instrs) ->
639
                     ( ok || instr = instr',
640
                       if ok then instr :: instrs
641
                       else if instr = instr' then instrs
642
                       else eliminate m subst_v' instr :: instrs ))
643
                   instrs
644
                   (false, []))
645
            in
646
            IMap.add v'.var_id instr_v subst, instr :: instrs'
647
        | _ ->
648
          assert false)
649
    | _ ->
650
      assert false
651
  with Not_found ->
652
    (* No such equivalent expr: keeping the definition *)
653
    subst, instr :: instrs
654

    
655
(* - [subst] : hashtable from ident to (simple) definition it is an equivalence
656
   table - [elim] : set of eliminated variables - [instrs] : previous
657
   instructions, which [instr] is compared against - [instr] : current
658
   instruction, normalized by [subst] *)
659

    
660
(** Common sub-expression elimination for machine instructions *)
661
let rec instr_cse m (subst, instrs) instr =
662
  match get_instr_desc instr with
663
  (* Simple cases*)
664
  | MStep ([ v ], id, vl)
665
    when Basic_library.is_internal_fun id (List.map (fun v -> v.value_type) vl)
666
    ->
667
    instr_cse
668
      m
669
      (subst, instrs)
670
      (update_instr_desc
671
         instr
672
         (MLocalAssign (v, mk_val (Fun (id, vl)) v.var_type)))
673
  | MLocalAssign (v, expr) when is_unfoldable_expr 2 expr ->
674
    IMap.add v.var_id expr subst, instr :: instrs
675
  | _ when is_assign instr ->
676
    subst_instr m subst instrs instr
677
  | _ ->
678
    subst, instr :: instrs
679

    
680
(** Apply common sub-expression elimination to a sequence of instrs *)
681
let instrs_cse m subst instrs =
682
  let subst, rev_instrs = List.fold_left (instr_cse m) (subst, []) instrs in
683
  subst, List.rev rev_instrs
684

    
685
(** Apply common sub-expression elimination to a machine - iterate through step
686
    instructions and remove simple local assigns *)
687
let machine_cse subst machine =
688
  (*Log.report ~level:1 (fun fmt -> Format.fprintf fmt "machine_cse %a@."
689
    pp_elim subst);*)
690
  let _, instrs = instrs_cse machine subst machine.mstep.step_instrs in
691
  let assigned = assigns_instrs instrs VSet.empty in
692
  {
693
    machine with
694
    mmemory = List.filter (fun vdecl -> VSet.mem vdecl assigned) machine.mmemory;
695
    mstep =
696
      {
697
        machine.mstep with
698
        step_locals =
699
          List.filter
700
            (fun vdecl -> VSet.mem vdecl assigned)
701
            machine.mstep.step_locals;
702
        step_instrs = instrs;
703
      };
704
  }
705

    
706
let machines_cse machines = List.map (machine_cse IMap.empty) machines
707

    
708
(* variable substitution for optimizing purposes *)
709

    
710
(* checks whether an [instr] is skip and can be removed from program *)
711
let rec instr_is_skip instr =
712
  match get_instr_desc instr with
713
  | MLocalAssign (i, { value_desc = Var v; _ }) when i = v ->
714
    true
715
  | MStateAssign (i, { value_desc = Var v; _ }) when i = v ->
716
    true
717
  | MBranch (_, hl) ->
718
    List.for_all (fun (_, il) -> instrs_are_skip il) hl
719
  | _ ->
720
    false
721

    
722
and instrs_are_skip instrs = List.for_all instr_is_skip instrs
723

    
724
let instr_cons instr cont = if instr_is_skip instr then cont else instr :: cont
725

    
726
(* XXX: UNUSED *)
727
(* let rec instr_remove_skip instr cont =
728
 *   match get_instr_desc instr with
729
 *   | MLocalAssign (i, { value_desc = Var v; _ }) when i = v ->
730
 *     cont
731
 *   | MStateAssign (i, { value_desc = Var v; _ }) when i = v ->
732
 *     cont
733
 *   | MBranch (g, hl) ->
734
 *     update_instr_desc instr
735
 *       (MBranch (g, List.map (fun (h, il) -> h, instrs_remove_skip il []) hl))
736
 *     :: cont
737
 *   | _ ->
738
 *     instr :: cont
739
 *
740
 * and instrs_remove_skip instrs cont =
741
 *   List.fold_right instr_remove_skip instrs cont *)
742

    
743
let rec value_replace_var fvar value =
744
  match value.value_desc with
745
  | Cst _ | ResetFlag ->
746
    value
747
  | Var v ->
748
    { value with value_desc = Var (fvar v) }
749
  | Fun (id, args) ->
750
    { value with value_desc = Fun (id, List.map (value_replace_var fvar) args) }
751
  | Array vl ->
752
    { value with value_desc = Array (List.map (value_replace_var fvar) vl) }
753
  | Access (t, i) ->
754
    { value with value_desc = Access (value_replace_var fvar t, i) }
755
  | Power (v, n) ->
756
    { value with value_desc = Power (value_replace_var fvar v, n) }
757

    
758
let expr_spec_replace fvar = function
759
  | Val v ->
760
    Val (value_replace_var fvar v)
761
  | Var v ->
762
    Var (fvar v)
763
  | e ->
764
    e
765

    
766
let predicate_spec_replace fvar = function
767
  | Transition (s, f, inst, i, vars, r, mems, insts) ->
768
    Transition
769
      (s, f, inst, i, List.map (expr_spec_replace fvar) vars, r, mems, insts)
770
  | p ->
771
    p
772

    
773
let rec instr_spec_replace fvar =
774
  let aux instr = instr_spec_replace fvar instr in
775
  function
776
  | Equal (e1, e2) ->
777
    Equal (expr_spec_replace fvar e1, expr_spec_replace fvar e2)
778
  | GEqual (e1, e2) ->
779
    GEqual (expr_spec_replace fvar e1, expr_spec_replace fvar e2)
780
  | And f ->
781
    And (List.map aux f)
782
  | Or f ->
783
    Or (List.map aux f)
784
  | Imply (a, b) ->
785
    Imply (aux a, aux b)
786
  | Exists (xs, a) ->
787
    let fvar v = if List.mem v xs then v else fvar v in
788
    Exists (xs, instr_spec_replace fvar a)
789
  | Forall (xs, a) ->
790
    let fvar v = if List.mem v xs then v else fvar v in
791
    Forall (xs, instr_spec_replace fvar a)
792
  | Ternary (e, a, b) ->
793
    Ternary (expr_spec_replace fvar e, aux a, aux b)
794
  | Predicate p ->
795
    Predicate (predicate_spec_replace fvar p)
796
  | ExistsMem (f, a, b) ->
797
    ExistsMem (f, aux a, aux b)
798
  | f ->
799
    f
800

    
801
let rec instr_replace_var fvar instr =
802
  let instr_desc =
803
    match instr.instr_desc with
804
    | MLocalAssign (i, v) ->
805
      MLocalAssign (fvar i, value_replace_var fvar v)
806
    | MStateAssign (i, v) ->
807
      MStateAssign (i, value_replace_var fvar v)
808
    | MStep (il, i, vl) ->
809
      MStep (List.map fvar il, i, List.map (value_replace_var fvar) vl)
810
    | MBranch (g, hl) ->
811
      MBranch
812
        ( value_replace_var fvar g,
813
          List.map (fun (h, il) -> h, instrs_replace_var fvar il []) hl )
814
    | MSetReset _
815
    | MNoReset _
816
    | MClearReset
817
    | MResetAssign _
818
    | MSpec _
819
    | MComment _ ->
820
      instr.instr_desc
821
  in
822
  let instr_spec = List.map (instr_spec_replace fvar) instr.instr_spec in
823
  instr_cons { instr with instr_desc; instr_spec }
824

    
825
and instrs_replace_var fvar instrs cont =
826
  List.fold_right (instr_replace_var fvar) instrs cont
827

    
828
let step_replace_var fvar step =
829
  (* Some outputs may have been replaced by locals. We then need to rename those
830
     outputs without changing their clocks, etc *)
831
  let outputs' =
832
    List.map (fun o -> { o with var_id = (fvar o).var_id }) step.step_outputs
833
  in
834
  let locals' =
835
    List.fold_left
836
      (fun res l ->
837
        let l' = fvar l in
838
        if List.exists (fun o -> o.var_id = l'.var_id) outputs' then res
839
        else Utils.add_cons l' res)
840
      []
841
      step.step_locals
842
  in
843
  {
844
    step with
845
    step_checks =
846
      List.map (fun (l, v) -> l, value_replace_var fvar v) step.step_checks;
847
    step_outputs = outputs';
848
    step_locals = locals';
849
    step_instrs = instrs_replace_var fvar step.step_instrs [];
850
  }
851

    
852
let machine_replace_variables fvar m =
853
  { m with mstep = step_replace_var fvar m.mstep }
854

    
855
let machine_reuse_variables reuse m =
856
  let fvar v = try Hashtbl.find reuse v.var_id with Not_found -> v in
857
  machine_replace_variables fvar m
858

    
859
let machines_reuse_variables reuse_tables =
860
  List.map (fun m ->
861
      machine_reuse_variables (Utils.IMap.find m.mname.node_id reuse_tables) m)
862

    
863
let rec instr_assign res instr =
864
  match get_instr_desc instr with
865
  | MLocalAssign (i, _) ->
866
    Disjunction.CISet.add i res
867
  | MStateAssign (i, _) ->
868
    Disjunction.CISet.add i res
869
  | MBranch (_, hl) ->
870
    List.fold_left (fun res (_, b) -> instrs_assign res b) res hl
871
  | MStep (il, _, _) ->
872
    List.fold_right Disjunction.CISet.add il res
873
  | _ ->
874
    res
875

    
876
and instrs_assign res instrs = List.fold_left instr_assign res instrs
877

    
878
let rec instr_constant_assign var instr =
879
  match get_instr_desc instr with
880
  | MLocalAssign (i, { value_desc = Cst (Const_tag _); _ })
881
  | MStateAssign (i, { value_desc = Cst (Const_tag _); _ }) ->
882
    i = var
883
  | MBranch (_, hl) ->
884
    List.for_all (fun (_, b) -> instrs_constant_assign var b) hl
885
  | _ ->
886
    false
887

    
888
and instrs_constant_assign var instrs =
889
  List.fold_left
890
    (fun res i ->
891
      if Disjunction.CISet.mem var (instr_assign Disjunction.CISet.empty i) then
892
        instr_constant_assign var i
893
      else res)
894
    false
895
    instrs
896

    
897
let rec instr_reduce branches instr1 cont =
898
  match get_instr_desc instr1 with
899
  | MLocalAssign (_, { value_desc = Cst (Const_tag c); _ }) ->
900
    instr1 :: (List.assoc c branches @ cont)
901
  | MStateAssign (_, { value_desc = Cst (Const_tag c); _ }) ->
902
    instr1 :: (List.assoc c branches @ cont)
903
  | MBranch (g, hl) ->
904
    update_instr_desc
905
      instr1
906
      (MBranch (g, List.map (fun (h, b) -> h, instrs_reduce branches b []) hl))
907
    :: cont
908
  | _ ->
909
    instr1 :: cont
910

    
911
and instrs_reduce branches instrs cont =
912
  match instrs with
913
  | [] ->
914
    cont
915
  | [ i ] ->
916
    instr_reduce branches i cont
917
  | i1 :: i2 :: q ->
918
    i1 :: instrs_reduce branches (i2 :: q) cont
919

    
920
let rec instrs_fusion instrs =
921
  match instrs with
922
  | [] | [ _ ] ->
923
    instrs
924
  | i1 :: i2 :: q -> (
925
    match i2.instr_desc with
926
    | MBranch ({ value_desc = Var v; _ }, hl) when instr_constant_assign v i1 ->
927
      instr_reduce
928
        (List.map (fun (h, b) -> h, instrs_fusion b) hl)
929
        { i1 with instr_spec = i1.instr_spec @ i2.instr_spec }
930
        (instrs_fusion q)
931
    | _ ->
932
      i1 :: instrs_fusion (i2 :: q))
933

    
934
let step_fusion step =
935
  { step with step_instrs = instrs_fusion step.step_instrs }
936

    
937
let machine_fusion m = { m with mstep = step_fusion m.mstep }
938

    
939
let machines_fusion prog = List.map machine_fusion prog
940

    
941
(* Additional function to modify the prog according to removed variables map *)
942

    
943
let elim_prog_variables prog removed_table =
944
  List.map
945
    (fun t ->
946
      match t.top_decl_desc with
947
      | Node nd -> (
948
        match IMap.find_opt nd.node_id removed_table with
949
        | Some nd_elim_map ->
950
          (* Iterating through the elim map to compute - the list of variables
951
             to remove - the associated list of lustre definitions x = expr to
952
             be used when removing these variables *)
953
          let vars_to_replace, defs =
954
            (* Recovering vid from node locals *)
955
            IMap.fold
956
              (fun v (_, eq) (accu_locals, accu_defs) ->
957
                let locals =
958
                  try
959
                    List.find (fun v' -> v'.var_id = v) nd.node_locals
960
                    :: accu_locals
961
                  with Not_found -> accu_locals
962
                  (* Variable v shall be a global constant, we do no need to
963
                     eliminate it from the locals *)
964
                in
965
                (* xxx let new_eq = { eq_lhs = [v]; eq_rhs = e; eq_loc =
966
                   e.expr_loc } in *)
967
                let defs = eq :: accu_defs in
968
                locals, defs)
969
              nd_elim_map
970
              ([], [])
971
          in
972
          let node_locals, node_stmts =
973
            List.fold_right
974
              (fun stmt (locals, res_stmts) ->
975
                match stmt with
976
                | Aut _ ->
977
                  assert false (* should be processed by now *)
978
                | Eq eq -> (
979
                  match eq.eq_lhs with
980
                  | [] ->
981
                    assert false (* shall not happen *)
982
                  | [lhs] when List.exists (fun v -> v.var_id = lhs) vars_to_replace ->
983
                    (* We remove the def *)
984
                    List.filter (fun v -> v.var_id <> lhs) locals, res_stmts
985
                  | _ ->
986
                    (* When more than one lhs we just keep the equation and do
987
                       not delete it *)
988
                    let eq_rhs =
989
                      substitute_expr vars_to_replace defs eq.eq_rhs
990
                    in
991
                    locals, Eq { eq with eq_rhs } :: res_stmts))
992
              nd.node_stmts
993
              (nd.node_locals, [])
994
          in
995
          let nd' = { nd with node_locals; node_stmts } in
996
          { t with top_decl_desc = Node nd' }
997
        | None ->
998
          t)
999
      | _ ->
1000
        t)
1001
    prog
1002

    
1003
(*** Main function ***)
1004

    
1005
(* This functions produces an optimzed prog * machines It 1- eliminates common
1006
   sub-expressions (TODO how is this different from normalization?) 2- inline
1007
   constants and eliminate duplicated variables 3- try to reuse variables
1008
   whenever possible
1009

    
1010
   When item (2) identified eliminated variables, the initial prog is modified,
1011
   its normalized recomputed, as well as its scheduling, before regenerating the
1012
   machines.
1013

    
1014
   The function returns both the (possibly updated) prog as well as the
1015
   machines *)
1016
let optimize params prog node_schs machine_code =
1017
  let machine_code =
1018
    if !Options.optimization >= 4 (* && !Options.output <> "horn" *) then (
1019
      Log.report ~level:1 (fun fmt ->
1020
          Format.fprintf
1021
            fmt
1022
            "@ @[<v 2>.. machines optimization: sub-expression elimination@ ");
1023
      let machine_code = machines_cse machine_code in
1024
      Log.report ~level:3 (fun fmt ->
1025
          Format.fprintf
1026
            fmt
1027
            "@[<v 2>.. generated machines (sub-expr elim):@ %a@]@ "
1028
            pp_machines
1029
            machine_code);
1030
      Log.report ~level:1 (fun fmt -> Format.fprintf fmt "@]");
1031
      machine_code)
1032
    else machine_code
1033
  in
1034
  (* Optimize machine code *)
1035
  let prog, machine_code, removed_table =
1036
    if
1037
      !Options.optimization >= 2 && !Options.output <> Options.OutEMF
1038
      (*&& !Options.output <> "horn"*)
1039
    then (
1040
      Log.report ~level:1 (fun fmt ->
1041
          Format.fprintf
1042
            fmt
1043
            "@ @[<v 2>.. machines optimization: const. inlining (partial eval. \
1044
             with const)@ ");
1045
      let machine_code, removed_table =
1046
        machines_unfold (Corelang.get_consts prog) node_schs machine_code
1047
      in
1048
      Log.report ~level:3 (fun fmt ->
1049
          Format.fprintf
1050
            fmt
1051
            "@ Eliminated flows: %a@ "
1052
            (IMap.pp (fun fmt m -> pp_elim empty_machine fmt (IMap.map fst m)))
1053
            removed_table);
1054
      (* If variables were eliminated, relaunch the normalization/machine
1055
         generation *)
1056
      let prog, machine_code, removed_table =
1057
        if IMap.is_empty removed_table then
1058
          (* stopping here, no need to reupdate the prog *)
1059
          prog, machine_code, removed_table
1060
        else
1061
          let prog = elim_prog_variables prog removed_table in
1062
          (* Mini stage1 *)
1063
          let prog = Normalization.normalize_prog ~first:false params prog in
1064
          let prog = SortProg.sort_nodes_locals prog in
1065
          (* Mini stage2: note that we do not protect against alg. loop since
1066
             this should have been handled before *)
1067
          let prog, node_schs = Scheduling.schedule_prog prog in
1068
          let machine_code = Machine_code.translate_prog prog node_schs in
1069
          (* Mini stage2 machine optimiation *)
1070
          let machine_code, removed_table =
1071
            machines_unfold (Corelang.get_consts prog) node_schs machine_code
1072
          in
1073
          prog, machine_code, removed_table
1074
      in
1075
      Log.report ~level:3 (fun fmt ->
1076
          Format.fprintf
1077
            fmt
1078
            "@ @[<v 2>.. generated machines (const inlining):@ %a@]@ "
1079
            pp_machines
1080
            machine_code);
1081
      Log.report ~level:1 (fun fmt -> Format.fprintf fmt "@]");
1082
      prog, machine_code, removed_table)
1083
    else prog, machine_code, IMap.empty
1084
  in
1085
  (* Optimize machine code *)
1086
  let machine_code =
1087
    if !Options.optimization >= 3 && not (Backends.is_functional ()) then (
1088
      Log.report ~level:1 (fun fmt ->
1089
          Format.fprintf
1090
            fmt
1091
            ".. machines optimization: minimize stack usage by reusing \
1092
             variables@,");
1093
      let node_schs =
1094
        Scheduling.remove_prog_inlined_locals removed_table node_schs
1095
      in
1096
      let reuse_tables = Scheduling.compute_prog_reuse_table node_schs in
1097
      machines_fusion (machines_reuse_variables reuse_tables machine_code))
1098
    else machine_code
1099
  in
1100

    
1101
  prog, machine_code
1102

    
1103
(* Local Variables: *)
1104
(* compile-command:"make -C .." *)
1105
(* End: *)
(60-60/99)