You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

379 lines
15 KiB
OCaml

(***********************************************************************)
(* *)
(* Heptagon *)
(* *)
(* Gwenael Delaval, LIG/INRIA, UJF *)
(* Leonard Gerard, Parkas, ENS *)
(* Adrien Guatto, Parkas, ENS *)
(* Cedric Pasteur, Parkas, ENS *)
(* *)
(* Copyright 2012 ENS, INRIA, UJF *)
(* *)
(* This file is part of the Heptagon compiler. *)
(* *)
(* Heptagon is free software: you can redistribute it and/or modify it *)
(* under the terms of the GNU General Public License as published by *)
(* the Free Software Foundation, either version 3 of the License, or *)
(* (at your option) any later version. *)
(* *)
(* Heptagon is distributed in the hope that it will be useful, *)
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
(* GNU General Public License for more details. *)
(* *)
(* You should have received a copy of the GNU General Public License *)
(* along with Heptagon. If not, see <http://www.gnu.org/licenses/> *)
(* *)
(***********************************************************************)
open Misc
open Names
open Idents
open Location
open Heptagon
open Hept_utils
open Hept_mapfold
open Types
open Clocks
open Linearity
open Format
(** Normalization pass
The normal form of the language is given in the manual *)
module Error =
struct
type error =
| Eunsupported_language_construct
let message loc kind =
begin match kind with
| Eunsupported_language_construct ->
eprintf "%aThis construct is not supported by MiniLS.@."
print_location loc
end;
raise Errors.Error
end
let exp_list_of_static_exp_list se_list =
let mk_one_const se =
mk_exp (Econst se) se.se_ty ~linearity:Ltop
in
List.map mk_one_const se_list
let is_list e = match e.e_desc with
| Eapp({ a_op = Etuple }, _, _)
| Econst { se_desc = Stuple _ } -> true
| _ -> false
let e_to_e_list e = match e.e_desc with
| Eapp({ a_op = Etuple }, e_list, _) -> e_list
| Econst { se_desc = Stuple se_list } ->
exp_list_of_static_exp_list se_list
| _ -> assert false
let flatten_e_list l =
let flatten = function
| { e_desc = Eapp({ a_op = Etuple }, l, _) } -> l
| e -> [e]
in
List.flatten (List.map flatten l)
(** Creates a new equation x = e, adds x to d_list
and the equation to eq_list. *)
let equation (d_list, eq_list) e =
let add_one_var ty lin d_list =
let n = Idents.gen_var "normalize" "v" in
let d_list = (mk_var_dec n ty lin) :: d_list in
n, d_list
in
match e.e_ty with
| Tprod ty_list ->
let lin_list =
(match e.e_linearity with
| Ltuple l -> l
| Ltop -> Misc.repeat_list Ltop (List.length ty_list)
| _ -> assert false)
in
let var_list, d_list =
mapfold2 (fun d_list ty lin -> add_one_var ty lin d_list) d_list ty_list lin_list in
let pat_list = List.map (fun n -> Evarpat n) var_list in
let eq_list = (mk_equation (Eeq (Etuplepat pat_list, e))) :: eq_list in
let e_list = Misc.map3
(fun n ty lin -> mk_exp (Evar n) ty lin) var_list ty_list lin_list in
let e = Eapp(mk_app Etuple, e_list, None) in
(d_list, eq_list), e
| _ ->
let n, d_list = add_one_var e.e_ty e.e_linearity d_list in
let eq_list = (mk_equation (Eeq (Evarpat n, e))) :: eq_list in
(d_list, eq_list), Evar n
(* [(e1,...,ek) when C(n) = (e1 when C(n),...,ek when C(n))] *)
let rec whenc context e c n e_orig =
let when_on_c c n context e =
(* If memalloc is activated, there cannot be a stateful exp inside a when. Indeed,
the expression inside the when will be called on a fast rhythm and write its result
in a variable that is slow because of the when. Although this value won't be used,
we have to be careful not to share this variable with another on the same clock as
the value of the latter will be overwritten. *)
let context, e =
if !Compiler_options.do_mem_alloc && Stateful.exp_is_stateful e then
let context, n = equation context e in
context, { e with e_desc = n }
else
context, e
in
{ e_orig with e_desc = Ewhen(e, c, n) }, context
in
if is_list e then (
let e_list, context = Misc.mapfold (when_on_c c n) context (e_to_e_list e) in
context, { e_orig with e_desc = Eapp(mk_app Etuple, e_list, None) }
) else
let e, context = when_on_c c n context e in
context, e
type kind = ExtValue | Any
(** Creates an equation and add it to the context if necessary. *)
let add context expected_kind e =
let up = match e.e_desc, expected_kind with
(* static arrays should be normalized to simplify code generation *)
| Econst { se_desc = Sarray _ | Sarray_power _ }, ExtValue -> true
| (Evar _ | Eapp ({ a_op = Efield | Etuple | Ereinit }, _, _) | Ewhen _
| Econst _) , ExtValue -> false
| _ , ExtValue -> true
| _ -> false in
if up then
let context, n = equation context e in
context, { e with e_desc = n }
else
context, e
let add_list context expected_kind e_list =
let aux context e =
let context, e = add context expected_kind e in
e, context
in
mapfold aux context e_list
let rec translate kind context e =
let context, e' = match e.e_desc with
| Econst _
| Evar _ -> context, e
| Epre(v, e1) -> fby kind context e v e1
| Efby({ e_desc = Econst v }, e1) -> fby kind context e (Some v) e1
| Estruct l ->
let translate_field context (f, e) =
let context, e = translate ExtValue context e in
(f, e), context
in
let l, context = mapfold translate_field context l in
context, { e with e_desc = Estruct l }
| Ewhen(e1, c, n) ->
let context, e1 = translate kind context e1 in
whenc context e1 c n e
| Emerge(n, tag_e_list) ->
merge context e n tag_e_list
| Eapp({ a_op = Eifthenelse }, [e1; e2; e3], _) ->
ifthenelse context e e1 e2 e3
| Eapp(app, e_list, r) ->
let context, e_list = translate_list ExtValue context e_list in
context, { e with e_desc = Eapp(app, flatten_e_list e_list, r) }
| Eiterator (it, app, n, pe_list, e_list, reset) ->
let context, pe_list = translate_list ExtValue context pe_list in
let context, e_list = translate_list ExtValue context e_list in
context, { e with e_desc = Eiterator(it, app, n, flatten_e_list pe_list,
flatten_e_list e_list, reset) }
| Esplit (x, e1) ->
let context, e1 = translate ExtValue context e1 in
let context, x = translate ExtValue context x in
let id = match x.e_desc with Evar x -> x | _ -> assert false in
let mk_when c = mk_exp ~linearity:e1.e_linearity (Ewhen (e1, c, id)) e1.e_ty in
(match x.e_ty with
| Tid t ->
(match Modules.find_type t with
| Signature.Tenum cl ->
let el = List.map mk_when cl in
context, { e with e_desc = Eapp(mk_app Etuple, el, None) }
| _ -> Misc.internal_error "normalize split")
| _ -> Misc.internal_error "normalize split")
| Elast _ | Efby _ ->
Error.message e.e_loc Error.Eunsupported_language_construct
in add context kind e'
and translate_list kind context e_list =
match e_list with
| [] -> context, []
| e :: e_list ->
let context, e = translate kind context e in
let context, e_list = translate_list kind context e_list in
context, e :: e_list
and fby kind context e v e1 =
let mk_fby c e =
mk_exp ~loc:e.e_loc (Epre(Some c, e)) e.e_ty ~linearity:Ltop in
let mk_pre e =
mk_exp ~loc:e.e_loc (Epre(None, e)) e.e_ty ~linearity:Ltop in
let context, e1 = translate ExtValue context e1 in
match e1.e_desc, v with
| Eapp({ a_op = Etuple } as app, e_list, r),
Some { se_desc = Stuple se_list } ->
let e_list = List.map2 mk_fby se_list e_list in
let e = { e with e_desc = Eapp(app, e_list, r) } in
translate kind context e
| Econst { se_desc = Stuple se_list },
Some { se_desc = Stuple v_list } ->
let e_list = List.map2 mk_fby v_list
(exp_list_of_static_exp_list se_list) in
let e = { e with e_desc = Eapp(mk_app Etuple, e_list, None) } in
translate kind context e
| Eapp({ a_op = Etuple } as app, e_list, r), None ->
let e_list = List.map mk_pre e_list in
let e = { e with e_desc = Eapp(app, e_list, r) } in
translate kind context e
| Econst { se_desc = Stuple se_list }, None ->
let e_list = List.map mk_pre (exp_list_of_static_exp_list se_list) in
let e = { e with e_desc = Eapp(mk_app Etuple, e_list, None) } in
translate kind context e
| _ -> context, { e with e_desc = Epre(v, e1) }
(** transforms [if x then e1, ..., en else e'1,..., e'n]
into [if x then e1 else e'1, ..., if x then en else e'n] *)
and ifthenelse context e e1 e2 e3 =
let context, e1 = translate ExtValue context e1 in
let context, e2 = translate ExtValue context e2 in
let context, e3 = translate ExtValue context e3 in
let mk_ite_list e2_list e3_list =
let mk_ite e'2 e'3 =
mk_exp ~loc:e.e_loc
(Eapp (mk_app Eifthenelse, [e1; e'2; e'3], None)) e'2.e_ty ~linearity:e'2.e_linearity
in
let e_list = List.map2 mk_ite e2_list e3_list in
{ e with e_desc = Eapp(mk_app Etuple, e_list, None) }
in
if is_list e2 then (
let e2_list, context = add_list context ExtValue (e_to_e_list e2) in
let e3_list, context = add_list context ExtValue (e_to_e_list e3) in
context, mk_ite_list e2_list e3_list
) else
context, { e with e_desc = Eapp (mk_app Eifthenelse, [e1; e2; e3], None) }
(** transforms [merge x (c1, (e11,...,e1n));...;(ck, (ek1,...,ekn))] into
[merge x (c1, e11)...(ck, ek1),..., merge x (c1, e1n)...(ck, ekn)] *)
and merge context e x c_e_list =
let translate_tag context (tag, e) =
let context, e = translate ExtValue context e in
(tag, e), context
in
let rec mk_merge x c_list e_lists =
let ty = (List.hd (List.hd e_lists)).e_ty in
let lin = (List.hd (List.hd e_lists)).e_linearity in
let rec build_c_e_list c_list e_lists =
match c_list, e_lists with
| [], [] -> [], []
| c::c_l, (e::e_l)::e_ls ->
let c_e_list, e_lists = build_c_e_list c_l e_ls in
(c,e)::c_e_list, e_l::e_lists
| _ -> assert false in
let rec build_merge_list c_list e_lists =
match e_lists with
[] -> assert false
| []::_ -> []
| _ ::_ ->
let c_e_list, e_lists = build_c_e_list c_list e_lists in
let e_merge = mk_exp ~loc:e.e_loc (Emerge(x, c_e_list)) ty ~linearity:lin in
let e_merge_list = build_merge_list c_list e_lists in
e_merge::e_merge_list in
build_merge_list c_list e_lists
in
let c_e_list, context = mapfold translate_tag context c_e_list in
match c_e_list with
| [] -> assert false
| (_,e1)::_ ->
if is_list e1 then (
let c_list = List.map (fun (t,_) -> t) c_e_list in
let e_lists = List.map (fun (_,e) -> e_to_e_list e) c_e_list in
let e_lists, context =
mapfold
(fun context e_list -> add_list context ExtValue e_list)
context e_lists in
let e_list = mk_merge x c_list e_lists in
context, { e with
e_desc = Eapp(mk_app Etuple, e_list, None) }
) else
context, { e with
e_desc = Emerge(x, c_e_list) }
(* applies distribution rules *)
(* [(p1,...,pn) = (e1,...,en)] into [p1 = e1;...;pn = en] *)
and distribute ((d_list, eq_list) as context) eq pat e =
let dist_e_list pat_list e_list =
let mk_eq pat e =
mk_equation (Eeq (pat, e))
in
let dis context eq = match eq.eq_desc with
| Eeq (pat, e) -> distribute context eq pat e
| _ -> assert false
in
let eqs = List.map2 mk_eq pat_list e_list in
List.fold_left dis context eqs
in
match pat, e.e_desc with
| Etuplepat(pat_list), Eapp({ a_op = Etuple }, e_list, _) ->
dist_e_list pat_list e_list
| Etuplepat(pat_list), Econst { se_desc = Stuple se_list } ->
dist_e_list pat_list (exp_list_of_static_exp_list se_list)
| _ ->
let eq = mk_equation ~loc:eq.eq_loc (Eeq(pat, e)) in
d_list, eq :: eq_list
and translate_eq ((d_list, eq_list) as context) eq = match eq.eq_desc with
| Eeq (pat, e) ->
let context, e = translate Any context e in
distribute context eq pat e
| Eblock b ->
let v, eqs = translate_eq_list [] b.b_equs in
let eq =
mk_equation ~loc:eq.eq_loc (Eblock { b with b_local = v @ b.b_local; b_equs = eqs})
in
d_list, eq :: eq_list
| _ -> Misc.internal_error "normalize"
and translate_eq_list d_list eq_list =
List.fold_left
(fun context eq -> translate_eq context eq)
(d_list, []) eq_list
let eq funs context eq =
let context = translate_eq context eq in
eq, context
let block funs _ b =
let _, (v_acc, eq_acc) = Hept_mapfold.block funs ([],[]) b in
{ b with b_local = v_acc@b.b_local; b_equs = eq_acc}, ([], [])
let contract funs context c =
let ({ c_block = b } as c), void_context =
Hept_mapfold.contract funs context c in
(* Non-void context could mean lost equations *)
assert (void_context=([],[]));
let context, e_a = translate ExtValue ([],[]) c.c_assume in
let context, e_a_loc = translate ExtValue context c.c_assume_loc in
let context, e_e = translate ExtValue context c.c_enforce in
let context, e_e_loc = translate ExtValue context c.c_enforce_loc in
let (d_list, eq_list) = context in
{ c with
c_assume = e_a;
c_enforce = e_e;
c_assume_loc = e_a_loc;
c_enforce_loc = e_e_loc;
c_block = { b with
b_local = d_list@b.b_local;
b_equs = eq_list@b.b_equs; }
}, void_context
let program p =
let funs = { defaults with block = block; eq = eq; contract = contract } in
let p, _ = Hept_mapfold.program funs ([], []) p in
p