From 532069087ed393ccb07688cf827ab6f837a73192 Mon Sep 17 00:00:00 2001 From: xthirioux <xthirioux@041b043f-8d7c-46b2-b46e-ef0dd855326e> Date: Thu, 26 Nov 2015 15:45:16 +0000 Subject: [PATCH] major branche merging salsa/mpfr with trunk --- Makefile.in | 34 +- configure.ac | 3 +- setup.ml | 5884 ------------------------ src/Makefile.in | 2 +- src/_tags | 11 +- src/backends/C/c_backend.ml | 20 +- src/backends/C/c_backend_common.ml | 209 +- src/backends/C/c_backend_header.ml | 29 +- src/backends/C/c_backend_main.ml | 172 +- src/backends/C/c_backend_makefile.ml | 8 +- src/backends/C/c_backend_src.ml | 379 +- src/backends/Horn/horn_backend.ml | 52 +- src/basic_library.ml | 73 +- src/causality.ml | 4 +- src/clock_calculus.ml | 2 +- src/compiler_common.ml | 17 + src/corelang.ml | 54 +- src/corelang.mli | 5 +- src/dimension.ml | 9 +- src/env.ml | 1 + src/inliner.ml | 101 +- src/lexerLustreSpec.mll | 10 +- src/lexer_lustre.mll | 202 +- src/liveness.ml | 14 +- src/lustreSpec.ml | 37 +- src/machine_code.ml | 225 +- src/main_lustre_compiler.ml | 836 ++-- src/normalization.ml | 70 +- src/optimize_machine.ml | 221 +- src/options.ml | 41 +- src/parser_lustre.mly | 91 +- src/plugins/salsa/machine_salsa_opt.ml | 572 +++ src/plugins/salsa/salsaDatatypes.ml | 337 ++ src/plugins/scopes/scopes.ml | 319 ++ src/printers.ml | 13 +- src/scheduling.ml | 76 +- src/splitting.ml | 2 +- src/stateless.ml | 13 +- src/type_predef.ml | 1 - src/types.ml | 29 +- src/typing.ml | 35 +- src/utils.ml | 3 +- test/tests_ok_dev.list | 32 - 43 files changed, 3002 insertions(+), 7246 deletions(-) delete mode 100644 setup.ml create mode 100644 src/plugins/salsa/machine_salsa_opt.ml create mode 100644 src/plugins/salsa/salsaDatatypes.ml create mode 100644 src/plugins/scopes/scopes.ml delete mode 100644 test/tests_ok_dev.list diff --git a/Makefile.in b/Makefile.in index 749e3bb0..a204a135 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3,52 +3,44 @@ OCAMLBUILD=@OCAMLBUILD@ -classic-display -no-links prefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ -datadir = ${prefix}/share +datarootdir = ${prefix}/share includedir = ${prefix}/include -LUSI_LIBS=include/math.lusi include/conv.lusi +LUSI_LIBS=include/math.lusi include/conv.lusi include/mpfr_lustre.lusi + LOCAL_BINDIR=bin LOCAL_DOCDIR=doc/manual lustrec: @echo Compiling binary lustrec - @$(OCAMLBUILD) -cflags -I,@OCAMLGRAPH_PATH@ -lflag @OCAMLGRAPH_PATH@/graph.cmxa -I src -I src/backends/C src/main_lustre_compiler.native - @mkdir -p $(LOCAL_BINDIR) - @mv _build/src/main_lustre_compiler.native $(LOCAL_BINDIR)/lustrec + @make -C src lustrec doc: @echo Generating doc - @$(OCAMLBUILD) lustrec.docdir/index.html - @rm -rf $(LOCAL_DOCDIR) - @cp -rf _build/lustrec.docdir $(LOCAL_DOCDIR) + @make -C src doc dot: doc - $(OCAMLBUILD) lustrec.docdir/lustrec.dot - dot -T ps -o lustrec.dot _build/lustrec.docdir/lustrec.dot - mv _build/lustrec.docdir/lustrec.dot $(LOCAL_DOCDIR) + @make -C src dot clean: - $(OCAMLBUILD) -clean + @make -C src clean + @rm -f $(LUSI_LIBS:%.lusi=%.lusic) $(LUSI_LIBS:%.lusi=%.h) dist-clean: clean - @rm -f Makefile myocamlbuild.ml config.log config.status configure include/*.lusic include/math.h include/conv.h -%.lusic: %.lusi +%.lusic: %.lusi @echo Compiling $< - @$(LOCAL_BINDIR)/lustrec -verbose 0 -d include $< + @$(LOCAL_BINDIR)/lustrec $(OPTION_LUSIC) -verbose 0 -d include $< -clean-lusic: - @rm -f $(LUSI_LIBS:%.lusi=%.lusic) +include/mpfr_lustre.lusic: OPTION_LUSIC=-mpfr 1 -compile-lusi: $(LUSI_LIBS:%.lusi=%.lusic) +compile-lusi: lustrec $(LUSI_LIBS:%.lusi=%.lusic) -install: clean-lusic compile-lusi +install: compile-lusi mkdir -p ${bindir} install -m 0755 $(LOCAL_BINDIR)/* ${bindir} mkdir -p ${includedir}/lustrec cp include/* ${includedir}/lustrec - mkdir -p ${datadir} - install -m 0655 share/FindLustre.cmake ${datadir} .PHONY: compile-lusi doc dot lustrec lustrec.odocl clean install dist-clean diff --git a/configure.ac b/configure.ac index 8ef82bad..e7141ed1 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -define([svnversion], esyscmd([sh -c "svnversion|sed "s/:.*//"|tr -d '\n'"]))dnl +dnl define([svnversion], esyscmd([sh -c "svnversion|sed "s/:.*//"|tr -d '\n'"])) AC_INIT([lustrec], [1.1-svnversion], [ploc@garoche.net]) @@ -86,7 +86,6 @@ AC_DEFINE_DIR([abs_datadir], [datadir]) # Instanciation AC_CONFIG_FILES([Makefile src/Makefile - src/myocamlbuild.ml src/version.ml]) AC_OUTPUT diff --git a/setup.ml b/setup.ml deleted file mode 100644 index f192265d..00000000 --- a/setup.ml +++ /dev/null @@ -1,5884 +0,0 @@ -(* setup.ml generated for the first time by OASIS v0.2.0 *) - -(* OASIS_START *) -(* DO NOT EDIT (digest: cbef9780a942e499729218b6c22c21f0) *) -(* - Regenerated by OASIS v0.3.0 - Visit http://oasis.forge.ocamlcore.org for more information and - documentation about functions used in this file. -*) -module OASISGettext = struct -(* # 21 "src/oasis/OASISGettext.ml" *) - - let ns_ str = - str - - let s_ str = - str - - let f_ (str : ('a, 'b, 'c, 'd) format4) = - str - - let fn_ fmt1 fmt2 n = - if n = 1 then - fmt1^^"" - else - fmt2^^"" - - let init = - [] - -end - -module OASISContext = struct -(* # 21 "src/oasis/OASISContext.ml" *) - - open OASISGettext - - type level = - [ `Debug - | `Info - | `Warning - | `Error] - - type t = - { - quiet: bool; - info: bool; - debug: bool; - ignore_plugins: bool; - ignore_unknown_fields: bool; - printf: level -> string -> unit; - } - - let printf lvl str = - let beg = - match lvl with - | `Error -> s_ "E: " - | `Warning -> s_ "W: " - | `Info -> s_ "I: " - | `Debug -> s_ "D: " - in - prerr_endline (beg^str) - - let default = - ref - { - quiet = false; - info = false; - debug = false; - ignore_plugins = false; - ignore_unknown_fields = false; - printf = printf; - } - - let quiet = - {!default with quiet = true} - - - let args () = - ["-quiet", - Arg.Unit (fun () -> default := {!default with quiet = true}), - (s_ " Run quietly"); - - "-info", - Arg.Unit (fun () -> default := {!default with info = true}), - (s_ " Display information message"); - - - "-debug", - Arg.Unit (fun () -> default := {!default with debug = true}), - (s_ " Output debug message")] -end - -module OASISString = struct -(* # 1 "src/oasis/OASISString.ml" *) - - - - (** Various string utilities. - - Mostly inspired by extlib and batteries ExtString and BatString libraries. - - @author Sylvain Le Gall - *) - - let nsplitf str f = - if str = "" then - [] - else - let buf = Buffer.create 13 in - let lst = ref [] in - let push () = - lst := Buffer.contents buf :: !lst; - Buffer.clear buf - in - let str_len = String.length str in - for i = 0 to str_len - 1 do - if f str.[i] then - push () - else - Buffer.add_char buf str.[i] - done; - push (); - List.rev !lst - - (** [nsplit c s] Split the string [s] at char [c]. It doesn't include the - separator. - *) - let nsplit str c = - nsplitf str ((=) c) - - let find ~what ?(offset=0) str = - let what_idx = ref 0 in - let str_idx = ref offset in - while !str_idx < String.length str && - !what_idx < String.length what do - if str.[!str_idx] = what.[!what_idx] then - incr what_idx - else - what_idx := 0; - incr str_idx - done; - if !what_idx <> String.length what then - raise Not_found - else - !str_idx - !what_idx - - let sub_start str len = - let str_len = String.length str in - if len >= str_len then - "" - else - String.sub str len (str_len - len) - - let sub_end ?(offset=0) str len = - let str_len = String.length str in - if len >= str_len then - "" - else - String.sub str 0 (str_len - len) - - let starts_with ~what ?(offset=0) str = - let what_idx = ref 0 in - let str_idx = ref offset in - let ok = ref true in - while !ok && - !str_idx < String.length str && - !what_idx < String.length what do - if str.[!str_idx] = what.[!what_idx] then - incr what_idx - else - ok := false; - incr str_idx - done; - if !what_idx = String.length what then - true - else - false - - let strip_starts_with ~what str = - if starts_with ~what str then - sub_start str (String.length what) - else - raise Not_found - - let ends_with ~what ?(offset=0) str = - let what_idx = ref ((String.length what) - 1) in - let str_idx = ref ((String.length str) - 1) in - let ok = ref true in - while !ok && - offset <= !str_idx && - 0 <= !what_idx do - if str.[!str_idx] = what.[!what_idx] then - decr what_idx - else - ok := false; - decr str_idx - done; - if !what_idx = -1 then - true - else - false - - let strip_ends_with ~what str = - if ends_with ~what str then - sub_end str (String.length what) - else - raise Not_found - - let replace_chars f s = - let buf = String.make (String.length s) 'X' in - for i = 0 to String.length s - 1 do - buf.[i] <- f s.[i] - done; - buf - -end - -module OASISUtils = struct -(* # 21 "src/oasis/OASISUtils.ml" *) - - open OASISGettext - - module MapString = Map.Make(String) - - let map_string_of_assoc assoc = - List.fold_left - (fun acc (k, v) -> MapString.add k v acc) - MapString.empty - assoc - - module SetString = Set.Make(String) - - let set_string_add_list st lst = - List.fold_left - (fun acc e -> SetString.add e acc) - st - lst - - let set_string_of_list = - set_string_add_list - SetString.empty - - - let compare_csl s1 s2 = - String.compare (String.lowercase s1) (String.lowercase s2) - - module HashStringCsl = - Hashtbl.Make - (struct - type t = string - - let equal s1 s2 = - (String.lowercase s1) = (String.lowercase s2) - - let hash s = - Hashtbl.hash (String.lowercase s) - end) - - let varname_of_string ?(hyphen='_') s = - if String.length s = 0 then - begin - invalid_arg "varname_of_string" - end - else - begin - let buf = - OASISString.replace_chars - (fun c -> - if ('a' <= c && c <= 'z') - || - ('A' <= c && c <= 'Z') - || - ('0' <= c && c <= '9') then - c - else - hyphen) - s; - in - let buf = - (* Start with a _ if digit *) - if '0' <= s.[0] && s.[0] <= '9' then - "_"^buf - else - buf - in - String.lowercase buf - end - - let varname_concat ?(hyphen='_') p s = - let what = String.make 1 hyphen in - let p = - try - OASISString.strip_ends_with ~what p - with Not_found -> - p - in - let s = - try - OASISString.strip_starts_with ~what s - with Not_found -> - s - in - p^what^s - - - let is_varname str = - str = varname_of_string str - - let failwithf fmt = Printf.ksprintf failwith fmt - -end - -module PropList = struct -(* # 21 "src/oasis/PropList.ml" *) - - open OASISGettext - - type name = string - - exception Not_set of name * string option - exception No_printer of name - exception Unknown_field of name * name - - let () = - Printexc.register_printer - (function - | Not_set (nm, Some rsn) -> - Some - (Printf.sprintf (f_ "Field '%s' is not set: %s") nm rsn) - | Not_set (nm, None) -> - Some - (Printf.sprintf (f_ "Field '%s' is not set") nm) - | No_printer nm -> - Some - (Printf.sprintf (f_ "No default printer for value %s") nm) - | Unknown_field (nm, schm) -> - Some - (Printf.sprintf (f_ "Field %s is not defined in schema %s") nm schm) - | _ -> - None) - - module Data = - struct - - type t = - (name, unit -> unit) Hashtbl.t - - let create () = - Hashtbl.create 13 - - let clear t = - Hashtbl.clear t - -(* # 71 "src/oasis/PropList.ml" *) - end - - module Schema = - struct - - type ('ctxt, 'extra) value = - { - get: Data.t -> string; - set: Data.t -> ?context:'ctxt -> string -> unit; - help: (unit -> string) option; - extra: 'extra; - } - - type ('ctxt, 'extra) t = - { - name: name; - fields: (name, ('ctxt, 'extra) value) Hashtbl.t; - order: name Queue.t; - name_norm: string -> string; - } - - let create ?(case_insensitive=false) nm = - { - name = nm; - fields = Hashtbl.create 13; - order = Queue.create (); - name_norm = - (if case_insensitive then - String.lowercase - else - fun s -> s); - } - - let add t nm set get extra help = - let key = - t.name_norm nm - in - - if Hashtbl.mem t.fields key then - failwith - (Printf.sprintf - (f_ "Field '%s' is already defined in schema '%s'") - nm t.name); - Hashtbl.add - t.fields - key - { - set = set; - get = get; - help = help; - extra = extra; - }; - Queue.add nm t.order - - let mem t nm = - Hashtbl.mem t.fields nm - - let find t nm = - try - Hashtbl.find t.fields (t.name_norm nm) - with Not_found -> - raise (Unknown_field (nm, t.name)) - - let get t data nm = - (find t nm).get data - - let set t data nm ?context x = - (find t nm).set - data - ?context - x - - let fold f acc t = - Queue.fold - (fun acc k -> - let v = - find t k - in - f acc k v.extra v.help) - acc - t.order - - let iter f t = - fold - (fun () -> f) - () - t - - let name t = - t.name - end - - module Field = - struct - - type ('ctxt, 'value, 'extra) t = - { - set: Data.t -> ?context:'ctxt -> 'value -> unit; - get: Data.t -> 'value; - sets: Data.t -> ?context:'ctxt -> string -> unit; - gets: Data.t -> string; - help: (unit -> string) option; - extra: 'extra; - } - - let new_id = - let last_id = - ref 0 - in - fun () -> incr last_id; !last_id - - let create ?schema ?name ?parse ?print ?default ?update ?help extra = - (* Default value container *) - let v = - ref None - in - - (* If name is not given, create unique one *) - let nm = - match name with - | Some s -> s - | None -> Printf.sprintf "_anon_%d" (new_id ()) - in - - (* Last chance to get a value: the default *) - let default () = - match default with - | Some d -> d - | None -> raise (Not_set (nm, Some (s_ "no default value"))) - in - - (* Get data *) - let get data = - (* Get value *) - try - (Hashtbl.find data nm) (); - match !v with - | Some x -> x - | None -> default () - with Not_found -> - default () - in - - (* Set data *) - let set data ?context x = - let x = - match update with - | Some f -> - begin - try - f ?context (get data) x - with Not_set _ -> - x - end - | None -> - x - in - Hashtbl.replace - data - nm - (fun () -> v := Some x) - in - - (* Parse string value, if possible *) - let parse = - match parse with - | Some f -> - f - | None -> - fun ?context s -> - failwith - (Printf.sprintf - (f_ "Cannot parse field '%s' when setting value %S") - nm - s) - in - - (* Set data, from string *) - let sets data ?context s = - set ?context data (parse ?context s) - in - - (* Output value as string, if possible *) - let print = - match print with - | Some f -> - f - | None -> - fun _ -> raise (No_printer nm) - in - - (* Get data, as a string *) - let gets data = - print (get data) - in - - begin - match schema with - | Some t -> - Schema.add t nm sets gets extra help - | None -> - () - end; - - { - set = set; - get = get; - sets = sets; - gets = gets; - help = help; - extra = extra; - } - - let fset data t ?context x = - t.set data ?context x - - let fget data t = - t.get data - - let fsets data t ?context s = - t.sets data ?context s - - let fgets data t = - t.gets data - - end - - module FieldRO = - struct - - let create ?schema ?name ?parse ?print ?default ?update ?help extra = - let fld = - Field.create ?schema ?name ?parse ?print ?default ?update ?help extra - in - fun data -> Field.fget data fld - - end -end - -module OASISMessage = struct -(* # 21 "src/oasis/OASISMessage.ml" *) - - - open OASISGettext - open OASISContext - - let generic_message ~ctxt lvl fmt = - let cond = - if ctxt.quiet then - false - else - match lvl with - | `Debug -> ctxt.debug - | `Info -> ctxt.info - | _ -> true - in - Printf.ksprintf - (fun str -> - if cond then - begin - ctxt.printf lvl str - end) - fmt - - let debug ~ctxt fmt = - generic_message ~ctxt `Debug fmt - - let info ~ctxt fmt = - generic_message ~ctxt `Info fmt - - let warning ~ctxt fmt = - generic_message ~ctxt `Warning fmt - - let error ~ctxt fmt = - generic_message ~ctxt `Error fmt - -end - -module OASISVersion = struct -(* # 21 "src/oasis/OASISVersion.ml" *) - - open OASISGettext - - - - type s = string - - type t = string - - type comparator = - | VGreater of t - | VGreaterEqual of t - | VEqual of t - | VLesser of t - | VLesserEqual of t - | VOr of comparator * comparator - | VAnd of comparator * comparator - - - (* Range of allowed characters *) - let is_digit c = - '0' <= c && c <= '9' - - let is_alpha c = - ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') - - let is_special = - function - | '.' | '+' | '-' | '~' -> true - | _ -> false - - let rec version_compare v1 v2 = - if v1 <> "" || v2 <> "" then - begin - (* Compare ascii string, using special meaning for version - * related char - *) - let val_ascii c = - if c = '~' then -1 - else if is_digit c then 0 - else if c = '\000' then 0 - else if is_alpha c then Char.code c - else (Char.code c) + 256 - in - - let len1 = String.length v1 in - let len2 = String.length v2 in - - let p = ref 0 in - - (** Compare ascii part *) - let compare_vascii () = - let cmp = ref 0 in - while !cmp = 0 && - !p < len1 && !p < len2 && - not (is_digit v1.[!p] && is_digit v2.[!p]) do - cmp := (val_ascii v1.[!p]) - (val_ascii v2.[!p]); - incr p - done; - if !cmp = 0 && !p < len1 && !p = len2 then - val_ascii v1.[!p] - else if !cmp = 0 && !p = len1 && !p < len2 then - - (val_ascii v2.[!p]) - else - !cmp - in - - (** Compare digit part *) - let compare_digit () = - let extract_int v p = - let start_p = !p in - while !p < String.length v && is_digit v.[!p] do - incr p - done; - let substr = - String.sub v !p ((String.length v) - !p) - in - let res = - match String.sub v start_p (!p - start_p) with - | "" -> 0 - | s -> int_of_string s - in - res, substr - in - let i1, tl1 = extract_int v1 (ref !p) in - let i2, tl2 = extract_int v2 (ref !p) in - i1 - i2, tl1, tl2 - in - - match compare_vascii () with - | 0 -> - begin - match compare_digit () with - | 0, tl1, tl2 -> - if tl1 <> "" && is_digit tl1.[0] then - 1 - else if tl2 <> "" && is_digit tl2.[0] then - -1 - else - version_compare tl1 tl2 - | n, _, _ -> - n - end - | n -> - n - end - else - begin - 0 - end - - - let version_of_string str = str - - let string_of_version t = t - - let chop t = - try - let pos = - String.rindex t '.' - in - String.sub t 0 pos - with Not_found -> - t - - let rec comparator_apply v op = - match op with - | VGreater cv -> - (version_compare v cv) > 0 - | VGreaterEqual cv -> - (version_compare v cv) >= 0 - | VLesser cv -> - (version_compare v cv) < 0 - | VLesserEqual cv -> - (version_compare v cv) <= 0 - | VEqual cv -> - (version_compare v cv) = 0 - | VOr (op1, op2) -> - (comparator_apply v op1) || (comparator_apply v op2) - | VAnd (op1, op2) -> - (comparator_apply v op1) && (comparator_apply v op2) - - let rec string_of_comparator = - function - | VGreater v -> "> "^(string_of_version v) - | VEqual v -> "= "^(string_of_version v) - | VLesser v -> "< "^(string_of_version v) - | VGreaterEqual v -> ">= "^(string_of_version v) - | VLesserEqual v -> "<= "^(string_of_version v) - | VOr (c1, c2) -> - (string_of_comparator c1)^" || "^(string_of_comparator c2) - | VAnd (c1, c2) -> - (string_of_comparator c1)^" && "^(string_of_comparator c2) - - let rec varname_of_comparator = - let concat p v = - OASISUtils.varname_concat - p - (OASISUtils.varname_of_string - (string_of_version v)) - in - function - | VGreater v -> concat "gt" v - | VLesser v -> concat "lt" v - | VEqual v -> concat "eq" v - | VGreaterEqual v -> concat "ge" v - | VLesserEqual v -> concat "le" v - | VOr (c1, c2) -> - (varname_of_comparator c1)^"_or_"^(varname_of_comparator c2) - | VAnd (c1, c2) -> - (varname_of_comparator c1)^"_and_"^(varname_of_comparator c2) - - let version_0_3_or_after t = - comparator_apply t (VGreaterEqual (string_of_version "0.3")) - -end - -module OASISLicense = struct -(* # 21 "src/oasis/OASISLicense.ml" *) - - (** License for _oasis fields - @author Sylvain Le Gall - *) - - - - type license = string - - type license_exception = string - - type license_version = - | Version of OASISVersion.t - | VersionOrLater of OASISVersion.t - | NoVersion - - - type license_dep_5_unit = - { - license: license; - excption: license_exception option; - version: license_version; - } - - - type license_dep_5 = - | DEP5Unit of license_dep_5_unit - | DEP5Or of license_dep_5 list - | DEP5And of license_dep_5 list - - - type t = - | DEP5License of license_dep_5 - | OtherLicense of string (* URL *) - - -end - -module OASISExpr = struct -(* # 21 "src/oasis/OASISExpr.ml" *) - - - - open OASISGettext - - type test = string - - type flag = string - - type t = - | EBool of bool - | ENot of t - | EAnd of t * t - | EOr of t * t - | EFlag of flag - | ETest of test * string - - - type 'a choices = (t * 'a) list - - let eval var_get t = - let rec eval' = - function - | EBool b -> - b - - | ENot e -> - not (eval' e) - - | EAnd (e1, e2) -> - (eval' e1) && (eval' e2) - - | EOr (e1, e2) -> - (eval' e1) || (eval' e2) - - | EFlag nm -> - let v = - var_get nm - in - assert(v = "true" || v = "false"); - (v = "true") - - | ETest (nm, vl) -> - let v = - var_get nm - in - (v = vl) - in - eval' t - - let choose ?printer ?name var_get lst = - let rec choose_aux = - function - | (cond, vl) :: tl -> - if eval var_get cond then - vl - else - choose_aux tl - | [] -> - let str_lst = - if lst = [] then - s_ "<empty>" - else - String.concat - (s_ ", ") - (List.map - (fun (cond, vl) -> - match printer with - | Some p -> p vl - | None -> s_ "<no printer>") - lst) - in - match name with - | Some nm -> - failwith - (Printf.sprintf - (f_ "No result for the choice list '%s': %s") - nm str_lst) - | None -> - failwith - (Printf.sprintf - (f_ "No result for a choice list: %s") - str_lst) - in - choose_aux (List.rev lst) - -end - -module OASISTypes = struct -(* # 21 "src/oasis/OASISTypes.ml" *) - - - - - type name = string - type package_name = string - type url = string - type unix_dirname = string - type unix_filename = string - type host_dirname = string - type host_filename = string - type prog = string - type arg = string - type args = string list - type command_line = (prog * arg list) - - type findlib_name = string - type findlib_full = string - - type compiled_object = - | Byte - | Native - | Best - - - type dependency = - | FindlibPackage of findlib_full * OASISVersion.comparator option - | InternalLibrary of name - - - type tool = - | ExternalTool of name - | InternalExecutable of name - - - type vcs = - | Darcs - | Git - | Svn - | Cvs - | Hg - | Bzr - | Arch - | Monotone - | OtherVCS of url - - - type plugin_kind = - [ `Configure - | `Build - | `Doc - | `Test - | `Install - | `Extra - ] - - type plugin_data_purpose = - [ `Configure - | `Build - | `Install - | `Clean - | `Distclean - | `Install - | `Uninstall - | `Test - | `Doc - | `Extra - | `Other of string - ] - - type 'a plugin = 'a * name * OASISVersion.t option - - type all_plugin = plugin_kind plugin - - type plugin_data = (all_plugin * plugin_data_purpose * (unit -> unit)) list - -(* # 102 "src/oasis/OASISTypes.ml" *) - - type 'a conditional = 'a OASISExpr.choices - - type custom = - { - pre_command: (command_line option) conditional; - post_command: (command_line option) conditional; - } - - - type common_section = - { - cs_name: name; - cs_data: PropList.Data.t; - cs_plugin_data: plugin_data; - } - - - type build_section = - { - bs_build: bool conditional; - bs_install: bool conditional; - bs_path: unix_dirname; - bs_compiled_object: compiled_object; - bs_build_depends: dependency list; - bs_build_tools: tool list; - bs_c_sources: unix_filename list; - bs_data_files: (unix_filename * unix_filename option) list; - bs_ccopt: args conditional; - bs_cclib: args conditional; - bs_dlllib: args conditional; - bs_dllpath: args conditional; - bs_byteopt: args conditional; - bs_nativeopt: args conditional; - } - - - type library = - { - lib_modules: string list; - lib_pack: bool; - lib_internal_modules: string list; - lib_findlib_parent: findlib_name option; - lib_findlib_name: findlib_name option; - lib_findlib_containers: findlib_name list; - } - - type executable = - { - exec_custom: bool; - exec_main_is: unix_filename; - } - - type flag = - { - flag_description: string option; - flag_default: bool conditional; - } - - type source_repository = - { - src_repo_type: vcs; - src_repo_location: url; - src_repo_browser: url option; - src_repo_module: string option; - src_repo_branch: string option; - src_repo_tag: string option; - src_repo_subdir: unix_filename option; - } - - type test = - { - test_type: [`Test] plugin; - test_command: command_line conditional; - test_custom: custom; - test_working_directory: unix_filename option; - test_run: bool conditional; - test_tools: tool list; - } - - type doc_format = - | HTML of unix_filename - | DocText - | PDF - | PostScript - | Info of unix_filename - | DVI - | OtherDoc - - - type doc = - { - doc_type: [`Doc] plugin; - doc_custom: custom; - doc_build: bool conditional; - doc_install: bool conditional; - doc_install_dir: unix_filename; - doc_title: string; - doc_authors: string list; - doc_abstract: string option; - doc_format: doc_format; - doc_data_files: (unix_filename * unix_filename option) list; - doc_build_tools: tool list; - } - - type section = - | Library of common_section * build_section * library - | Executable of common_section * build_section * executable - | Flag of common_section * flag - | SrcRepo of common_section * source_repository - | Test of common_section * test - | Doc of common_section * doc - - - type section_kind = - [ `Library | `Executable | `Flag | `SrcRepo | `Test | `Doc ] - - type package = - { - oasis_version: OASISVersion.t; - ocaml_version: OASISVersion.comparator option; - findlib_version: OASISVersion.comparator option; - name: package_name; - version: OASISVersion.t; - license: OASISLicense.t; - license_file: unix_filename option; - copyrights: string list; - maintainers: string list; - authors: string list; - homepage: url option; - synopsis: string; - description: string option; - categories: url list; - - conf_type: [`Configure] plugin; - conf_custom: custom; - - build_type: [`Build] plugin; - build_custom: custom; - - install_type: [`Install] plugin; - install_custom: custom; - uninstall_custom: custom; - - clean_custom: custom; - distclean_custom: custom; - - files_ab: unix_filename list; - sections: section list; - plugins: [`Extra] plugin list; - schema_data: PropList.Data.t; - plugin_data: plugin_data; - } - -end - -module OASISUnixPath = struct -(* # 21 "src/oasis/OASISUnixPath.ml" *) - - type unix_filename = string - type unix_dirname = string - - type host_filename = string - type host_dirname = string - - let current_dir_name = "." - - let parent_dir_name = ".." - - let is_current_dir fn = - fn = current_dir_name || fn = "" - - let concat f1 f2 = - if is_current_dir f1 then - f2 - else - let f1' = - try OASISString.strip_ends_with ~what:"/" f1 with Not_found -> f1 - in - f1'^"/"^f2 - - let make = - function - | hd :: tl -> - List.fold_left - (fun f p -> concat f p) - hd - tl - | [] -> - invalid_arg "OASISUnixPath.make" - - let dirname f = - try - String.sub f 0 (String.rindex f '/') - with Not_found -> - current_dir_name - - let basename f = - try - let pos_start = - (String.rindex f '/') + 1 - in - String.sub f pos_start ((String.length f) - pos_start) - with Not_found -> - f - - let chop_extension f = - try - let last_dot = - String.rindex f '.' - in - let sub = - String.sub f 0 last_dot - in - try - let last_slash = - String.rindex f '/' - in - if last_slash < last_dot then - sub - else - f - with Not_found -> - sub - - with Not_found -> - f - - let capitalize_file f = - let dir = dirname f in - let base = basename f in - concat dir (String.capitalize base) - - let uncapitalize_file f = - let dir = dirname f in - let base = basename f in - concat dir (String.uncapitalize base) - -end - -module OASISHostPath = struct -(* # 21 "src/oasis/OASISHostPath.ml" *) - - - open Filename - - module Unix = OASISUnixPath - - let make = - function - | [] -> - invalid_arg "OASISHostPath.make" - | hd :: tl -> - List.fold_left Filename.concat hd tl - - let of_unix ufn = - if Sys.os_type = "Unix" then - ufn - else - make - (List.map - (fun p -> - if p = Unix.current_dir_name then - current_dir_name - else if p = Unix.parent_dir_name then - parent_dir_name - else - p) - (OASISString.nsplit ufn '/')) - - -end - -module OASISSection = struct -(* # 21 "src/oasis/OASISSection.ml" *) - - open OASISTypes - - let section_kind_common = - function - | Library (cs, _, _) -> - `Library, cs - | Executable (cs, _, _) -> - `Executable, cs - | Flag (cs, _) -> - `Flag, cs - | SrcRepo (cs, _) -> - `SrcRepo, cs - | Test (cs, _) -> - `Test, cs - | Doc (cs, _) -> - `Doc, cs - - let section_common sct = - snd (section_kind_common sct) - - let section_common_set cs = - function - | Library (_, bs, lib) -> Library (cs, bs, lib) - | Executable (_, bs, exec) -> Executable (cs, bs, exec) - | Flag (_, flg) -> Flag (cs, flg) - | SrcRepo (_, src_repo) -> SrcRepo (cs, src_repo) - | Test (_, tst) -> Test (cs, tst) - | Doc (_, doc) -> Doc (cs, doc) - - (** Key used to identify section - *) - let section_id sct = - let k, cs = - section_kind_common sct - in - k, cs.cs_name - - let string_of_section sct = - let k, nm = - section_id sct - in - (match k with - | `Library -> "library" - | `Executable -> "executable" - | `Flag -> "flag" - | `SrcRepo -> "src repository" - | `Test -> "test" - | `Doc -> "doc") - ^" "^nm - - let section_find id scts = - List.find - (fun sct -> id = section_id sct) - scts - - module CSection = - struct - type t = section - - let id = section_id - - let compare t1 t2 = - compare (id t1) (id t2) - - let equal t1 t2 = - (id t1) = (id t2) - - let hash t = - Hashtbl.hash (id t) - end - - module MapSection = Map.Make(CSection) - module SetSection = Set.Make(CSection) - -end - -module OASISBuildSection = struct -(* # 21 "src/oasis/OASISBuildSection.ml" *) - -end - -module OASISExecutable = struct -(* # 21 "src/oasis/OASISExecutable.ml" *) - - open OASISTypes - - let unix_exec_is (cs, bs, exec) is_native ext_dll suffix_program = - let dir = - OASISUnixPath.concat - bs.bs_path - (OASISUnixPath.dirname exec.exec_main_is) - in - let is_native_exec = - match bs.bs_compiled_object with - | Native -> true - | Best -> is_native () - | Byte -> false - in - - OASISUnixPath.concat - dir - (cs.cs_name^(suffix_program ())), - - if not is_native_exec && - not exec.exec_custom && - bs.bs_c_sources <> [] then - Some (dir^"/dll"^cs.cs_name^"_stubs"^(ext_dll ())) - else - None - -end - -module OASISLibrary = struct -(* # 21 "src/oasis/OASISLibrary.ml" *) - - open OASISTypes - open OASISUtils - open OASISGettext - open OASISSection - - type library_name = name - type findlib_part_name = name - type 'a map_of_findlib_part_name = 'a OASISUtils.MapString.t - - exception InternalLibraryNotFound of library_name - exception FindlibPackageNotFound of findlib_name - - type group_t = - | Container of findlib_name * group_t list - | Package of (findlib_name * - common_section * - build_section * - library * - group_t list) - - (* Look for a module file, considering capitalization or not. *) - let find_module source_file_exists (cs, bs, lib) modul = - let possible_base_fn = - List.map - (OASISUnixPath.concat bs.bs_path) - [modul; - OASISUnixPath.uncapitalize_file modul; - OASISUnixPath.capitalize_file modul] - in - (* TODO: we should be able to be able to determine the source for every - * files. Hence we should introduce a Module(source: fn) for the fields - * Modules and InternalModules - *) - List.fold_left - (fun acc base_fn -> - match acc with - | `No_sources _ -> - begin - let file_found = - List.fold_left - (fun acc ext -> - if source_file_exists (base_fn^ext) then - (base_fn^ext) :: acc - else - acc) - [] - [".ml"; ".mli"; ".mll"; ".mly"] - in - match file_found with - | [] -> - acc - | lst -> - `Sources (base_fn, lst) - end - | `Sources _ -> - acc) - (`No_sources possible_base_fn) - possible_base_fn - - let source_unix_files ~ctxt (cs, bs, lib) source_file_exists = - List.fold_left - (fun acc modul -> - match find_module source_file_exists (cs, bs, lib) modul with - | `Sources (base_fn, lst) -> - (base_fn, lst) :: acc - | `No_sources _ -> - OASISMessage.warning - ~ctxt - (f_ "Cannot find source file matching \ - module '%s' in library %s") - modul cs.cs_name; - acc) - [] - (lib.lib_modules @ lib.lib_internal_modules) - - let generated_unix_files - ~ctxt - ~is_native - ~has_native_dynlink - ~ext_lib - ~ext_dll - ~source_file_exists - (cs, bs, lib) = - - let find_modules lst ext = - let find_module modul = - match find_module source_file_exists (cs, bs, lib) modul with - | `Sources (base_fn, _) -> - [base_fn] - | `No_sources lst -> - OASISMessage.warning - ~ctxt - (f_ "Cannot find source file matching \ - module '%s' in library %s") - modul cs.cs_name; - lst - in - List.map - (fun nm -> - List.map - (fun base_fn -> base_fn ^"."^ext) - (find_module nm)) - lst - in - - (* The headers that should be compiled along *) - let headers = - if lib.lib_pack then - [] - else - find_modules - lib.lib_modules - "cmi" - in - - (* The .cmx that be compiled along *) - let cmxs = - let should_be_built = - (not lib.lib_pack) && (* Do not install .cmx packed submodules *) - match bs.bs_compiled_object with - | Native -> true - | Best -> is_native - | Byte -> false - in - if should_be_built then - find_modules - (lib.lib_modules @ lib.lib_internal_modules) - "cmx" - else - [] - in - - let acc_nopath = - [] - in - - (* Compute what libraries should be built *) - let acc_nopath = - (* Add the packed header file if required *) - let add_pack_header acc = - if lib.lib_pack then - [cs.cs_name^".cmi"] :: acc - else - acc - in - let byte acc = - add_pack_header ([cs.cs_name^".cma"] :: acc) - in - let native acc = - let acc = - add_pack_header - (if has_native_dynlink then - [cs.cs_name^".cmxs"] :: acc - else acc) - in - [cs.cs_name^".cmxa"] :: [cs.cs_name^ext_lib] :: acc - in - match bs.bs_compiled_object with - | Native -> - byte (native acc_nopath) - | Best when is_native -> - byte (native acc_nopath) - | Byte | Best -> - byte acc_nopath - in - - (* Add C library to be built *) - let acc_nopath = - if bs.bs_c_sources <> [] then - begin - ["lib"^cs.cs_name^"_stubs"^ext_lib] - :: - ["dll"^cs.cs_name^"_stubs"^ext_dll] - :: - acc_nopath - end - else - acc_nopath - in - - (* All the files generated *) - List.rev_append - (List.rev_map - (List.rev_map - (OASISUnixPath.concat bs.bs_path)) - acc_nopath) - (headers @ cmxs) - - type data = common_section * build_section * library - type tree = - | Node of (data option) * (tree MapString.t) - | Leaf of data - - let findlib_mapping pkg = - (* Map from library name to either full findlib name or parts + parent. *) - let fndlb_parts_of_lib_name = - let fndlb_parts cs lib = - let name = - match lib.lib_findlib_name with - | Some nm -> nm - | None -> cs.cs_name - in - let name = - String.concat "." (lib.lib_findlib_containers @ [name]) - in - name - in - List.fold_left - (fun mp -> - function - | Library (cs, _, lib) -> - begin - let lib_name = cs.cs_name in - let fndlb_parts = fndlb_parts cs lib in - if MapString.mem lib_name mp then - failwithf - (f_ "The library name '%s' is used more than once.") - lib_name; - match lib.lib_findlib_parent with - | Some lib_name_parent -> - MapString.add - lib_name - (`Unsolved (lib_name_parent, fndlb_parts)) - mp - | None -> - MapString.add - lib_name - (`Solved fndlb_parts) - mp - end - - | Executable _ | Test _ | Flag _ | SrcRepo _ | Doc _ -> - mp) - MapString.empty - pkg.sections - in - - (* Solve the above graph to be only library name to full findlib name. *) - let fndlb_name_of_lib_name = - let rec solve visited mp lib_name lib_name_child = - if SetString.mem lib_name visited then - failwithf - (f_ "Library '%s' is involved in a cycle \ - with regard to findlib naming.") - lib_name; - let visited = SetString.add lib_name visited in - try - match MapString.find lib_name mp with - | `Solved fndlb_nm -> - fndlb_nm, mp - | `Unsolved (lib_nm_parent, post_fndlb_nm) -> - let pre_fndlb_nm, mp = - solve visited mp lib_nm_parent lib_name - in - let fndlb_nm = pre_fndlb_nm^"."^post_fndlb_nm in - fndlb_nm, MapString.add lib_name (`Solved fndlb_nm) mp - with Not_found -> - failwithf - (f_ "Library '%s', which is defined as the findlib parent of \ - library '%s', doesn't exist.") - lib_name lib_name_child - in - let mp = - MapString.fold - (fun lib_name status mp -> - match status with - | `Solved _ -> - (* Solved initialy, no need to go further *) - mp - | `Unsolved _ -> - let _, mp = solve SetString.empty mp lib_name "<none>" in - mp) - fndlb_parts_of_lib_name - fndlb_parts_of_lib_name - in - MapString.map - (function - | `Solved fndlb_nm -> fndlb_nm - | `Unsolved _ -> assert false) - mp - in - - (* Convert an internal library name to a findlib name. *) - let findlib_name_of_library_name lib_nm = - try - MapString.find lib_nm fndlb_name_of_lib_name - with Not_found -> - raise (InternalLibraryNotFound lib_nm) - in - - (* Add a library to the tree. - *) - let add sct mp = - let fndlb_fullname = - let cs, _, _ = sct in - let lib_name = cs.cs_name in - findlib_name_of_library_name lib_name - in - let rec add_children nm_lst (children : tree MapString.t) = - match nm_lst with - | (hd :: tl) -> - begin - let node = - try - add_node tl (MapString.find hd children) - with Not_found -> - (* New node *) - new_node tl - in - MapString.add hd node children - end - | [] -> - (* Should not have a nameless library. *) - assert false - and add_node tl node = - if tl = [] then - begin - match node with - | Node (None, children) -> - Node (Some sct, children) - | Leaf (cs', _, _) | Node (Some (cs', _, _), _) -> - (* TODO: allow to merge Package, i.e. - * archive(byte) = "foo.cma foo_init.cmo" - *) - let cs, _, _ = sct in - failwithf - (f_ "Library '%s' and '%s' have the same findlib name '%s'") - cs.cs_name cs'.cs_name fndlb_fullname - end - else - begin - match node with - | Leaf data -> - Node (Some data, add_children tl MapString.empty) - | Node (data_opt, children) -> - Node (data_opt, add_children tl children) - end - and new_node = - function - | [] -> - Leaf sct - | hd :: tl -> - Node (None, MapString.add hd (new_node tl) MapString.empty) - in - add_children (OASISString.nsplit fndlb_fullname '.') mp - in - - let rec group_of_tree mp = - MapString.fold - (fun nm node acc -> - let cur = - match node with - | Node (Some (cs, bs, lib), children) -> - Package (nm, cs, bs, lib, group_of_tree children) - | Node (None, children) -> - Container (nm, group_of_tree children) - | Leaf (cs, bs, lib) -> - Package (nm, cs, bs, lib, []) - in - cur :: acc) - mp [] - in - - let group_mp = - List.fold_left - (fun mp -> - function - | Library (cs, bs, lib) -> - add (cs, bs, lib) mp - | _ -> - mp) - MapString.empty - pkg.sections - in - - let groups = - group_of_tree group_mp - in - - let library_name_of_findlib_name = - Lazy.lazy_from_fun - (fun () -> - (* Revert findlib_name_of_library_name. *) - MapString.fold - (fun k v mp -> MapString.add v k mp) - fndlb_name_of_lib_name - MapString.empty) - in - let library_name_of_findlib_name fndlb_nm = - try - MapString.find fndlb_nm (Lazy.force library_name_of_findlib_name) - with Not_found -> - raise (FindlibPackageNotFound fndlb_nm) - in - - groups, - findlib_name_of_library_name, - library_name_of_findlib_name - - let findlib_of_group = - function - | Container (fndlb_nm, _) - | Package (fndlb_nm, _, _, _, _) -> fndlb_nm - - let root_of_group grp = - let rec root_lib_aux = - (* We do a DFS in the group. *) - function - | Container (_, children) -> - List.fold_left - (fun res grp -> - if res = None then - root_lib_aux grp - else - res) - None - children - | Package (_, cs, bs, lib, _) -> - Some (cs, bs, lib) - in - match root_lib_aux grp with - | Some res -> - res - | None -> - failwithf - (f_ "Unable to determine root library of findlib library '%s'") - (findlib_of_group grp) - -end - -module OASISFlag = struct -(* # 21 "src/oasis/OASISFlag.ml" *) - -end - -module OASISPackage = struct -(* # 21 "src/oasis/OASISPackage.ml" *) - -end - -module OASISSourceRepository = struct -(* # 21 "src/oasis/OASISSourceRepository.ml" *) - -end - -module OASISTest = struct -(* # 21 "src/oasis/OASISTest.ml" *) - -end - -module OASISDocument = struct -(* # 21 "src/oasis/OASISDocument.ml" *) - -end - -module OASISExec = struct -(* # 21 "src/oasis/OASISExec.ml" *) - - open OASISGettext - open OASISUtils - open OASISMessage - - (* TODO: I don't like this quote, it is there because $(rm) foo expands to - * 'rm -f' foo... - *) - let run ~ctxt ?f_exit_code ?(quote=true) cmd args = - let cmd = - if quote then - if Sys.os_type = "Win32" then - if String.contains cmd ' ' then - (* Double the 1st double quote... win32... sigh *) - "\""^(Filename.quote cmd) - else - cmd - else - Filename.quote cmd - else - cmd - in - let cmdline = - String.concat " " (cmd :: args) - in - info ~ctxt (f_ "Running command '%s'") cmdline; - match f_exit_code, Sys.command cmdline with - | None, 0 -> () - | None, i -> - failwithf - (f_ "Command '%s' terminated with error code %d") - cmdline i - | Some f, i -> - f i - - let run_read_output ~ctxt ?f_exit_code cmd args = - let fn = - Filename.temp_file "oasis-" ".txt" - in - try - begin - let () = - run ~ctxt ?f_exit_code cmd (args @ [">"; Filename.quote fn]) - in - let chn = - open_in fn - in - let routput = - ref [] - in - begin - try - while true do - routput := (input_line chn) :: !routput - done - with End_of_file -> - () - end; - close_in chn; - Sys.remove fn; - List.rev !routput - end - with e -> - (try Sys.remove fn with _ -> ()); - raise e - - let run_read_one_line ~ctxt ?f_exit_code cmd args = - match run_read_output ~ctxt ?f_exit_code cmd args with - | [fst] -> - fst - | lst -> - failwithf - (f_ "Command return unexpected output %S") - (String.concat "\n" lst) -end - -module OASISFileUtil = struct -(* # 21 "src/oasis/OASISFileUtil.ml" *) - - open OASISGettext - - let file_exists_case fn = - let dirname = Filename.dirname fn in - let basename = Filename.basename fn in - if Sys.file_exists dirname then - if basename = Filename.current_dir_name then - true - else - List.mem - basename - (Array.to_list (Sys.readdir dirname)) - else - false - - let find_file ?(case_sensitive=true) paths exts = - - (* Cardinal product of two list *) - let ( * ) lst1 lst2 = - List.flatten - (List.map - (fun a -> - List.map - (fun b -> a,b) - lst2) - lst1) - in - - let rec combined_paths lst = - match lst with - | p1 :: p2 :: tl -> - let acc = - (List.map - (fun (a,b) -> Filename.concat a b) - (p1 * p2)) - in - combined_paths (acc :: tl) - | [e] -> - e - | [] -> - [] - in - - let alternatives = - List.map - (fun (p,e) -> - if String.length e > 0 && e.[0] <> '.' then - p ^ "." ^ e - else - p ^ e) - ((combined_paths paths) * exts) - in - List.find - (if case_sensitive then - file_exists_case - else - Sys.file_exists) - alternatives - - let which ~ctxt prg = - let path_sep = - match Sys.os_type with - | "Win32" -> - ';' - | _ -> - ':' - in - let path_lst = OASISString.nsplit (Sys.getenv "PATH") path_sep in - let exec_ext = - match Sys.os_type with - | "Win32" -> - "" :: (OASISString.nsplit (Sys.getenv "PATHEXT") path_sep) - | _ -> - [""] - in - find_file ~case_sensitive:false [path_lst; [prg]] exec_ext - - (**/**) - let rec fix_dir dn = - (* Windows hack because Sys.file_exists "src\\" = false when - * Sys.file_exists "src" = true - *) - let ln = - String.length dn - in - if Sys.os_type = "Win32" && ln > 0 && dn.[ln - 1] = '\\' then - fix_dir (String.sub dn 0 (ln - 1)) - else - dn - - let q = Filename.quote - (**/**) - - let cp ~ctxt ?(recurse=false) src tgt = - if recurse then - match Sys.os_type with - | "Win32" -> - OASISExec.run ~ctxt - "xcopy" [q src; q tgt; "/E"] - | _ -> - OASISExec.run ~ctxt - "cp" ["-r"; q src; q tgt] - else - OASISExec.run ~ctxt - (match Sys.os_type with - | "Win32" -> "copy" - | _ -> "cp") - [q src; q tgt] - - let mkdir ~ctxt tgt = - OASISExec.run ~ctxt - (match Sys.os_type with - | "Win32" -> "md" - | _ -> "mkdir") - [q tgt] - - let rec mkdir_parent ~ctxt f tgt = - let tgt = - fix_dir tgt - in - if Sys.file_exists tgt then - begin - if not (Sys.is_directory tgt) then - OASISUtils.failwithf - (f_ "Cannot create directory '%s', a file of the same name already \ - exists") - tgt - end - else - begin - mkdir_parent ~ctxt f (Filename.dirname tgt); - if not (Sys.file_exists tgt) then - begin - f tgt; - mkdir ~ctxt tgt - end - end - - let rmdir ~ctxt tgt = - if Sys.readdir tgt = [||] then - begin - match Sys.os_type with - | "Win32" -> - OASISExec.run ~ctxt "rd" [q tgt] - | _ -> - OASISExec.run ~ctxt "rm" ["-r"; q tgt] - end - - let glob ~ctxt fn = - let basename = - Filename.basename fn - in - if String.length basename >= 2 && - basename.[0] = '*' && - basename.[1] = '.' then - begin - let ext_len = - (String.length basename) - 2 - in - let ext = - String.sub basename 2 ext_len - in - let dirname = - Filename.dirname fn - in - Array.fold_left - (fun acc fn -> - try - let fn_ext = - String.sub - fn - ((String.length fn) - ext_len) - ext_len - in - if fn_ext = ext then - (Filename.concat dirname fn) :: acc - else - acc - with Invalid_argument _ -> - acc) - [] - (Sys.readdir dirname) - end - else - begin - if file_exists_case fn then - [fn] - else - [] - end -end - - -# 2142 "setup.ml" -module BaseEnvLight = struct -(* # 21 "src/base/BaseEnvLight.ml" *) - - module MapString = Map.Make(String) - - type t = string MapString.t - - let default_filename = - Filename.concat - (Sys.getcwd ()) - "setup.data" - - let load ?(allow_empty=false) ?(filename=default_filename) () = - if Sys.file_exists filename then - begin - let chn = - open_in_bin filename - in - let st = - Stream.of_channel chn - in - let line = - ref 1 - in - let st_line = - Stream.from - (fun _ -> - try - match Stream.next st with - | '\n' -> incr line; Some '\n' - | c -> Some c - with Stream.Failure -> None) - in - let lexer = - Genlex.make_lexer ["="] st_line - in - let rec read_file mp = - match Stream.npeek 3 lexer with - | [Genlex.Ident nm; Genlex.Kwd "="; Genlex.String value] -> - Stream.junk lexer; - Stream.junk lexer; - Stream.junk lexer; - read_file (MapString.add nm value mp) - | [] -> - mp - | _ -> - failwith - (Printf.sprintf - "Malformed data file '%s' line %d" - filename !line) - in - let mp = - read_file MapString.empty - in - close_in chn; - mp - end - else if allow_empty then - begin - MapString.empty - end - else - begin - failwith - (Printf.sprintf - "Unable to load environment, the file '%s' doesn't exist." - filename) - end - - let var_get name env = - let rec var_expand str = - let buff = - Buffer.create ((String.length str) * 2) - in - Buffer.add_substitute - buff - (fun var -> - try - var_expand (MapString.find var env) - with Not_found -> - failwith - (Printf.sprintf - "No variable %s defined when trying to expand %S." - var - str)) - str; - Buffer.contents buff - in - var_expand (MapString.find name env) - - let var_choose lst env = - OASISExpr.choose - (fun nm -> var_get nm env) - lst -end - - -# 2240 "setup.ml" -module BaseContext = struct -(* # 21 "src/base/BaseContext.ml" *) - - open OASISContext - - let args = args - - let default = default - -end - -module BaseMessage = struct -(* # 21 "src/base/BaseMessage.ml" *) - - (** Message to user, overrid for Base - @author Sylvain Le Gall - *) - open OASISMessage - open BaseContext - - let debug fmt = debug ~ctxt:!default fmt - - let info fmt = info ~ctxt:!default fmt - - let warning fmt = warning ~ctxt:!default fmt - - let error fmt = error ~ctxt:!default fmt - -end - -module BaseEnv = struct -(* # 21 "src/base/BaseEnv.ml" *) - - open OASISGettext - open OASISUtils - open PropList - - module MapString = BaseEnvLight.MapString - - type origin_t = - | ODefault - | OGetEnv - | OFileLoad - | OCommandLine - - type cli_handle_t = - | CLINone - | CLIAuto - | CLIWith - | CLIEnable - | CLIUser of (Arg.key * Arg.spec * Arg.doc) list - - type definition_t = - { - hide: bool; - dump: bool; - cli: cli_handle_t; - arg_help: string option; - group: string option; - } - - let schema = - Schema.create "environment" - - (* Environment data *) - let env = - Data.create () - - (* Environment data from file *) - let env_from_file = - ref MapString.empty - - (* Lexer for var *) - let var_lxr = - Genlex.make_lexer [] - - let rec var_expand str = - let buff = - Buffer.create ((String.length str) * 2) - in - Buffer.add_substitute - buff - (fun var -> - try - (* TODO: this is a quick hack to allow calling Test.Command - * without defining executable name really. I.e. if there is - * an exec Executable toto, then $(toto) should be replace - * by its real name. It is however useful to have this function - * for other variable that depend on the host and should be - * written better than that. - *) - let st = - var_lxr (Stream.of_string var) - in - match Stream.npeek 3 st with - | [Genlex.Ident "utoh"; Genlex.Ident nm] -> - OASISHostPath.of_unix (var_get nm) - | [Genlex.Ident "utoh"; Genlex.String s] -> - OASISHostPath.of_unix s - | [Genlex.Ident "ocaml_escaped"; Genlex.Ident nm] -> - String.escaped (var_get nm) - | [Genlex.Ident "ocaml_escaped"; Genlex.String s] -> - String.escaped s - | [Genlex.Ident nm] -> - var_get nm - | _ -> - failwithf - (f_ "Unknown expression '%s' in variable expansion of %s.") - var - str - with - | Unknown_field (_, _) -> - failwithf - (f_ "No variable %s defined when trying to expand %S.") - var - str - | Stream.Error e -> - failwithf - (f_ "Syntax error when parsing '%s' when trying to \ - expand %S: %s") - var - str - e) - str; - Buffer.contents buff - - and var_get name = - let vl = - try - Schema.get schema env name - with Unknown_field _ as e -> - begin - try - MapString.find name !env_from_file - with Not_found -> - raise e - end - in - var_expand vl - - let var_choose ?printer ?name lst = - OASISExpr.choose - ?printer - ?name - var_get - lst - - let var_protect vl = - let buff = - Buffer.create (String.length vl) - in - String.iter - (function - | '$' -> Buffer.add_string buff "\\$" - | c -> Buffer.add_char buff c) - vl; - Buffer.contents buff - - let var_define - ?(hide=false) - ?(dump=true) - ?short_desc - ?(cli=CLINone) - ?arg_help - ?group - name (* TODO: type constraint on the fact that name must be a valid OCaml - id *) - dflt = - - let default = - [ - OFileLoad, (fun () -> MapString.find name !env_from_file); - ODefault, dflt; - OGetEnv, (fun () -> Sys.getenv name); - ] - in - - let extra = - { - hide = hide; - dump = dump; - cli = cli; - arg_help = arg_help; - group = group; - } - in - - (* Try to find a value that can be defined - *) - let var_get_low lst = - let errors, res = - List.fold_left - (fun (errors, res) (o, v) -> - if res = None then - begin - try - errors, Some (v ()) - with - | Not_found -> - errors, res - | Failure rsn -> - (rsn :: errors), res - | e -> - (Printexc.to_string e) :: errors, res - end - else - errors, res) - ([], None) - (List.sort - (fun (o1, _) (o2, _) -> - Pervasives.compare o2 o1) - lst) - in - match res, errors with - | Some v, _ -> - v - | None, [] -> - raise (Not_set (name, None)) - | None, lst -> - raise (Not_set (name, Some (String.concat (s_ ", ") lst))) - in - - let help = - match short_desc with - | Some fs -> Some fs - | None -> None - in - - let var_get_lst = - FieldRO.create - ~schema - ~name - ~parse:(fun ?(context=ODefault) s -> [context, fun () -> s]) - ~print:var_get_low - ~default - ~update:(fun ?context x old_x -> x @ old_x) - ?help - extra - in - - fun () -> - var_expand (var_get_low (var_get_lst env)) - - let var_redefine - ?hide - ?dump - ?short_desc - ?cli - ?arg_help - ?group - name - dflt = - if Schema.mem schema name then - begin - (* TODO: look suspsicious, we want to memorize dflt not dflt () *) - Schema.set schema env ~context:ODefault name (dflt ()); - fun () -> var_get name - end - else - begin - var_define - ?hide - ?dump - ?short_desc - ?cli - ?arg_help - ?group - name - dflt - end - - let var_ignore (e : unit -> string) = - () - - let print_hidden = - var_define - ~hide:true - ~dump:false - ~cli:CLIAuto - ~arg_help:"Print even non-printable variable. (debug)" - "print_hidden" - (fun () -> "false") - - let var_all () = - List.rev - (Schema.fold - (fun acc nm def _ -> - if not def.hide || bool_of_string (print_hidden ()) then - nm :: acc - else - acc) - [] - schema) - - let default_filename = - BaseEnvLight.default_filename - - let load ?allow_empty ?filename () = - env_from_file := BaseEnvLight.load ?allow_empty ?filename () - - let unload () = - env_from_file := MapString.empty; - Data.clear env - - let dump ?(filename=default_filename) () = - let chn = - open_out_bin filename - in - let output nm value = - Printf.fprintf chn "%s=%S\n" nm value - in - let mp_todo = - (* Dump data from schema *) - Schema.fold - (fun mp_todo nm def _ -> - if def.dump then - begin - try - let value = - Schema.get - schema - env - nm - in - output nm value - with Not_set _ -> - () - end; - MapString.remove nm mp_todo) - !env_from_file - schema - in - (* Dump data defined outside of schema *) - MapString.iter output mp_todo; - - (* End of the dump *) - close_out chn - - let print () = - let printable_vars = - Schema.fold - (fun acc nm def short_descr_opt -> - if not def.hide || bool_of_string (print_hidden ()) then - begin - try - let value = - Schema.get - schema - env - nm - in - let txt = - match short_descr_opt with - | Some s -> s () - | None -> nm - in - (txt, value) :: acc - with Not_set _ -> - acc - end - else - acc) - [] - schema - in - let max_length = - List.fold_left max 0 - (List.rev_map String.length - (List.rev_map fst printable_vars)) - in - let dot_pad str = - String.make ((max_length - (String.length str)) + 3) '.' - in - - Printf.printf "\nConfiguration: \n"; - List.iter - (fun (name,value) -> - Printf.printf "%s: %s %s\n" name (dot_pad name) value) - (List.rev printable_vars); - Printf.printf "\n%!" - - let args () = - let arg_concat = - OASISUtils.varname_concat ~hyphen:'-' - in - [ - "--override", - Arg.Tuple - ( - let rvr = ref "" - in - let rvl = ref "" - in - [ - Arg.Set_string rvr; - Arg.Set_string rvl; - Arg.Unit - (fun () -> - Schema.set - schema - env - ~context:OCommandLine - !rvr - !rvl) - ] - ), - "var+val Override any configuration variable."; - - ] - @ - List.flatten - (Schema.fold - (fun acc name def short_descr_opt -> - let var_set s = - Schema.set - schema - env - ~context:OCommandLine - name - s - in - - let arg_name = - OASISUtils.varname_of_string ~hyphen:'-' name - in - - let hlp = - match short_descr_opt with - | Some txt -> txt () - | None -> "" - in - - let arg_hlp = - match def.arg_help with - | Some s -> s - | None -> "str" - in - - let default_value = - try - Printf.sprintf - (f_ " [%s]") - (Schema.get - schema - env - name) - with Not_set _ -> - "" - in - - let args = - match def.cli with - | CLINone -> - [] - | CLIAuto -> - [ - arg_concat "--" arg_name, - Arg.String var_set, - Printf.sprintf (f_ "%s %s%s") arg_hlp hlp default_value - ] - | CLIWith -> - [ - arg_concat "--with-" arg_name, - Arg.String var_set, - Printf.sprintf (f_ "%s %s%s") arg_hlp hlp default_value - ] - | CLIEnable -> - let dflt = - if default_value = " [true]" then - s_ " [default: enabled]" - else - s_ " [default: disabled]" - in - [ - arg_concat "--enable-" arg_name, - Arg.Unit (fun () -> var_set "true"), - Printf.sprintf (f_ " %s%s") hlp dflt; - - arg_concat "--disable-" arg_name, - Arg.Unit (fun () -> var_set "false"), - Printf.sprintf (f_ " %s%s") hlp dflt - ] - | CLIUser lst -> - lst - in - args :: acc) - [] - schema) -end - -module BaseArgExt = struct -(* # 21 "src/base/BaseArgExt.ml" *) - - open OASISUtils - open OASISGettext - - let parse argv args = - (* Simulate command line for Arg *) - let current = - ref 0 - in - - try - Arg.parse_argv - ~current:current - (Array.concat [[|"none"|]; argv]) - (Arg.align args) - (failwithf (f_ "Don't know what to do with arguments: '%s'")) - (s_ "configure options:") - with - | Arg.Help txt -> - print_endline txt; - exit 0 - | Arg.Bad txt -> - prerr_endline txt; - exit 1 -end - -module BaseCheck = struct -(* # 21 "src/base/BaseCheck.ml" *) - - open BaseEnv - open BaseMessage - open OASISUtils - open OASISGettext - - let prog_best prg prg_lst = - var_redefine - prg - (fun () -> - let alternate = - List.fold_left - (fun res e -> - match res with - | Some _ -> - res - | None -> - try - Some (OASISFileUtil.which ~ctxt:!BaseContext.default e) - with Not_found -> - None) - None - prg_lst - in - match alternate with - | Some prg -> prg - | None -> raise Not_found) - - let prog prg = - prog_best prg [prg] - - let prog_opt prg = - prog_best prg [prg^".opt"; prg] - - let ocamlfind = - prog "ocamlfind" - - let version - var_prefix - cmp - fversion - () = - (* Really compare version provided *) - let var = - var_prefix^"_version_"^(OASISVersion.varname_of_comparator cmp) - in - var_redefine - ~hide:true - var - (fun () -> - let version_str = - match fversion () with - | "[Distributed with OCaml]" -> - begin - try - (var_get "ocaml_version") - with Not_found -> - warning - (f_ "Variable ocaml_version not defined, fallback \ - to default"); - Sys.ocaml_version - end - | res -> - res - in - let version = - OASISVersion.version_of_string version_str - in - if OASISVersion.comparator_apply version cmp then - version_str - else - failwithf - (f_ "Cannot satisfy version constraint on %s: %s (version: %s)") - var_prefix - (OASISVersion.string_of_comparator cmp) - version_str) - () - - let package_version pkg = - OASISExec.run_read_one_line ~ctxt:!BaseContext.default - (ocamlfind ()) - ["query"; "-format"; "%v"; pkg] - - let package ?version_comparator pkg () = - let var = - OASISUtils.varname_concat - "pkg_" - (OASISUtils.varname_of_string pkg) - in - let findlib_dir pkg = - let dir = - OASISExec.run_read_one_line ~ctxt:!BaseContext.default - (ocamlfind ()) - ["query"; "-format"; "%d"; pkg] - in - if Sys.file_exists dir && Sys.is_directory dir then - dir - else - failwithf - (f_ "When looking for findlib package %s, \ - directory %s return doesn't exist") - pkg dir - in - let vl = - var_redefine - var - (fun () -> findlib_dir pkg) - () - in - ( - match version_comparator with - | Some ver_cmp -> - ignore - (version - var - ver_cmp - (fun _ -> package_version pkg) - ()) - | None -> - () - ); - vl -end - -module BaseOCamlcConfig = struct -(* # 21 "src/base/BaseOCamlcConfig.ml" *) - - - open BaseEnv - open OASISUtils - open OASISGettext - - module SMap = Map.Make(String) - - let ocamlc = - BaseCheck.prog_opt "ocamlc" - - let ocamlc_config_map = - (* Map name to value for ocamlc -config output - (name ^": "^value) - *) - let rec split_field mp lst = - match lst with - | line :: tl -> - let mp = - try - let pos_semicolon = - String.index line ':' - in - if pos_semicolon > 1 then - ( - let name = - String.sub line 0 pos_semicolon - in - let linelen = - String.length line - in - let value = - if linelen > pos_semicolon + 2 then - String.sub - line - (pos_semicolon + 2) - (linelen - pos_semicolon - 2) - else - "" - in - SMap.add name value mp - ) - else - ( - mp - ) - with Not_found -> - ( - mp - ) - in - split_field mp tl - | [] -> - mp - in - - let cache = - lazy - (var_protect - (Marshal.to_string - (split_field - SMap.empty - (OASISExec.run_read_output - ~ctxt:!BaseContext.default - (ocamlc ()) ["-config"])) - [])) - in - var_redefine - "ocamlc_config_map" - ~hide:true - ~dump:false - (fun () -> - (* TODO: update if ocamlc change !!! *) - Lazy.force cache) - - let var_define nm = - (* Extract data from ocamlc -config *) - let avlbl_config_get () = - Marshal.from_string - (ocamlc_config_map ()) - 0 - in - let chop_version_suffix s = - try - String.sub s 0 (String.index s '+') - with _ -> - s - in - - let nm_config, value_config = - match nm with - | "ocaml_version" -> - "version", chop_version_suffix - | _ -> nm, (fun x -> x) - in - var_redefine - nm - (fun () -> - try - let map = - avlbl_config_get () - in - let value = - SMap.find nm_config map - in - value_config value - with Not_found -> - failwithf - (f_ "Cannot find field '%s' in '%s -config' output") - nm - (ocamlc ())) - -end - -module BaseStandardVar = struct -(* # 21 "src/base/BaseStandardVar.ml" *) - - - open OASISGettext - open OASISTypes - open OASISExpr - open BaseCheck - open BaseEnv - - let ocamlfind = BaseCheck.ocamlfind - let ocamlc = BaseOCamlcConfig.ocamlc - let ocamlopt = prog_opt "ocamlopt" - let ocamlbuild = prog "ocamlbuild" - - - (**/**) - let rpkg = - ref None - - let pkg_get () = - match !rpkg with - | Some pkg -> pkg - | None -> failwith (s_ "OASIS Package is not set") - - let var_cond = ref [] - - let var_define_cond ~since_version f dflt = - let holder = ref (fun () -> dflt) in - let since_version = - OASISVersion.VGreaterEqual (OASISVersion.version_of_string since_version) - in - var_cond := - (fun ver -> - if OASISVersion.comparator_apply ver since_version then - holder := f ()) :: !var_cond; - fun () -> !holder () - - (**/**) - - let pkg_name = - var_define - ~short_desc:(fun () -> s_ "Package name") - "pkg_name" - (fun () -> (pkg_get ()).name) - - let pkg_version = - var_define - ~short_desc:(fun () -> s_ "Package version") - "pkg_version" - (fun () -> - (OASISVersion.string_of_version (pkg_get ()).version)) - - let c = BaseOCamlcConfig.var_define - - let os_type = c "os_type" - let system = c "system" - let architecture = c "architecture" - let ccomp_type = c "ccomp_type" - let ocaml_version = c "ocaml_version" - - (* TODO: Check standard variable presence at runtime *) - - let standard_library_default = c "standard_library_default" - let standard_library = c "standard_library" - let standard_runtime = c "standard_runtime" - let bytecomp_c_compiler = c "bytecomp_c_compiler" - let native_c_compiler = c "native_c_compiler" - let model = c "model" - let ext_obj = c "ext_obj" - let ext_asm = c "ext_asm" - let ext_lib = c "ext_lib" - let ext_dll = c "ext_dll" - let default_executable_name = c "default_executable_name" - let systhread_supported = c "systhread_supported" - - let flexlink = - BaseCheck.prog "flexlink" - - let flexdll_version = - var_define - ~short_desc:(fun () -> "FlexDLL version (Win32)") - "flexdll_version" - (fun () -> - let lst = - OASISExec.run_read_output ~ctxt:!BaseContext.default - (flexlink ()) ["-help"] - in - match lst with - | line :: _ -> - Scanf.sscanf line "FlexDLL version %s" (fun ver -> ver) - | [] -> - raise Not_found) - - (**/**) - let p name hlp dflt = - var_define - ~short_desc:hlp - ~cli:CLIAuto - ~arg_help:"dir" - name - dflt - - let (/) a b = - if os_type () = Sys.os_type then - Filename.concat a b - else if os_type () = "Unix" then - OASISUnixPath.concat a b - else - OASISUtils.failwithf (f_ "Cannot handle os_type %s filename concat") - (os_type ()) - (**/**) - - let prefix = - p "prefix" - (fun () -> s_ "Install architecture-independent files dir") - (fun () -> - match os_type () with - | "Win32" -> - let program_files = - Sys.getenv "PROGRAMFILES" - in - program_files/(pkg_name ()) - | _ -> - "/usr/local") - - let exec_prefix = - p "exec_prefix" - (fun () -> s_ "Install architecture-dependent files in dir") - (fun () -> "$prefix") - - let bindir = - p "bindir" - (fun () -> s_ "User executables") - (fun () -> "$exec_prefix"/"bin") - - let sbindir = - p "sbindir" - (fun () -> s_ "System admin executables") - (fun () -> "$exec_prefix"/"sbin") - - let libexecdir = - p "libexecdir" - (fun () -> s_ "Program executables") - (fun () -> "$exec_prefix"/"libexec") - - let sysconfdir = - p "sysconfdir" - (fun () -> s_ "Read-only single-machine data") - (fun () -> "$prefix"/"etc") - - let sharedstatedir = - p "sharedstatedir" - (fun () -> s_ "Modifiable architecture-independent data") - (fun () -> "$prefix"/"com") - - let localstatedir = - p "localstatedir" - (fun () -> s_ "Modifiable single-machine data") - (fun () -> "$prefix"/"var") - - let libdir = - p "libdir" - (fun () -> s_ "Object code libraries") - (fun () -> "$exec_prefix"/"lib") - - let datarootdir = - p "datarootdir" - (fun () -> s_ "Read-only arch-independent data root") - (fun () -> "$prefix"/"share") - - let datadir = - p "datadir" - (fun () -> s_ "Read-only architecture-independent data") - (fun () -> "$datarootdir") - - let infodir = - p "infodir" - (fun () -> s_ "Info documentation") - (fun () -> "$datarootdir"/"info") - - let localedir = - p "localedir" - (fun () -> s_ "Locale-dependent data") - (fun () -> "$datarootdir"/"locale") - - let mandir = - p "mandir" - (fun () -> s_ "Man documentation") - (fun () -> "$datarootdir"/"man") - - let docdir = - p "docdir" - (fun () -> s_ "Documentation root") - (fun () -> "$datarootdir"/"doc"/"$pkg_name") - - let htmldir = - p "htmldir" - (fun () -> s_ "HTML documentation") - (fun () -> "$docdir") - - let dvidir = - p "dvidir" - (fun () -> s_ "DVI documentation") - (fun () -> "$docdir") - - let pdfdir = - p "pdfdir" - (fun () -> s_ "PDF documentation") - (fun () -> "$docdir") - - let psdir = - p "psdir" - (fun () -> s_ "PS documentation") - (fun () -> "$docdir") - - let destdir = - p "destdir" - (fun () -> s_ "Prepend a path when installing package") - (fun () -> - raise - (PropList.Not_set - ("destdir", - Some (s_ "undefined by construct")))) - - let findlib_version = - var_define - "findlib_version" - (fun () -> - BaseCheck.package_version "findlib") - - let is_native = - var_define - "is_native" - (fun () -> - try - let _s : string = - ocamlopt () - in - "true" - with PropList.Not_set _ -> - let _s : string = - ocamlc () - in - "false") - - let ext_program = - var_define - "suffix_program" - (fun () -> - match os_type () with - | "Win32" -> ".exe" - | _ -> "") - - let rm = - var_define - ~short_desc:(fun () -> s_ "Remove a file.") - "rm" - (fun () -> - match os_type () with - | "Win32" -> "del" - | _ -> "rm -f") - - let rmdir = - var_define - ~short_desc:(fun () -> s_ "Remove a directory.") - "rmdir" - (fun () -> - match os_type () with - | "Win32" -> "rd" - | _ -> "rm -rf") - - let debug = - var_define - ~short_desc:(fun () -> s_ "Turn ocaml debug flag on") - ~cli:CLIEnable - "debug" - (fun () -> "true") - - let profile = - var_define - ~short_desc:(fun () -> s_ "Turn ocaml profile flag on") - ~cli:CLIEnable - "profile" - (fun () -> "false") - - let tests = - var_define_cond ~since_version:"0.3" - (fun () -> - var_define - ~short_desc:(fun () -> - s_ "Compile tests executable and library and run them") - ~cli:CLIEnable - "tests" - (fun () -> "false")) - "true" - - let docs = - var_define_cond ~since_version:"0.3" - (fun () -> - var_define - ~short_desc:(fun () -> s_ "Create documentations") - ~cli:CLIEnable - "docs" - (fun () -> "true")) - "true" - - let native_dynlink = - var_define - ~short_desc:(fun () -> s_ "Compiler support generation of .cmxs.") - ~cli:CLINone - "native_dynlink" - (fun () -> - let res = - let ocaml_lt_312 () = - OASISVersion.comparator_apply - (OASISVersion.version_of_string (ocaml_version ())) - (OASISVersion.VLesser - (OASISVersion.version_of_string "3.12.0")) - in - let flexdll_lt_030 () = - OASISVersion.comparator_apply - (OASISVersion.version_of_string (flexdll_version ())) - (OASISVersion.VLesser - (OASISVersion.version_of_string "0.30")) - in - let has_native_dynlink = - let ocamlfind = ocamlfind () in - try - let fn = - OASISExec.run_read_one_line - ~ctxt:!BaseContext.default - ocamlfind - ["query"; "-predicates"; "native"; "dynlink"; - "-format"; "%d/%a"] - in - Sys.file_exists fn - with _ -> - false - in - if not has_native_dynlink then - false - else if ocaml_lt_312 () then - false - else if (os_type () = "Win32" || os_type () = "Cygwin") - && flexdll_lt_030 () then - begin - BaseMessage.warning - (f_ ".cmxs generation disabled because FlexDLL needs to be \ - at least 0.30. Please upgrade FlexDLL from %s to 0.30.") - (flexdll_version ()); - false - end - else - true - in - string_of_bool res) - - let init pkg = - rpkg := Some pkg; - List.iter (fun f -> f pkg.oasis_version) !var_cond - -end - -module BaseFileAB = struct -(* # 21 "src/base/BaseFileAB.ml" *) - - open BaseEnv - open OASISGettext - open BaseMessage - - let to_filename fn = - let fn = - OASISHostPath.of_unix fn - in - if not (Filename.check_suffix fn ".ab") then - warning - (f_ "File '%s' doesn't have '.ab' extension") - fn; - Filename.chop_extension fn - - let replace fn_lst = - let buff = - Buffer.create 13 - in - List.iter - (fun fn -> - let fn = - OASISHostPath.of_unix fn - in - let chn_in = - open_in fn - in - let chn_out = - open_out (to_filename fn) - in - ( - try - while true do - Buffer.add_string buff (var_expand (input_line chn_in)); - Buffer.add_char buff '\n' - done - with End_of_file -> - () - ); - Buffer.output_buffer chn_out buff; - Buffer.clear buff; - close_in chn_in; - close_out chn_out) - fn_lst -end - -module BaseLog = struct -(* # 21 "src/base/BaseLog.ml" *) - - open OASISUtils - - let default_filename = - Filename.concat - (Filename.dirname BaseEnv.default_filename) - "setup.log" - - module SetTupleString = - Set.Make - (struct - type t = string * string - let compare (s11, s12) (s21, s22) = - match String.compare s11 s21 with - | 0 -> String.compare s12 s22 - | n -> n - end) - - let load () = - if Sys.file_exists default_filename then - begin - let chn = - open_in default_filename - in - let scbuf = - Scanf.Scanning.from_file default_filename - in - let rec read_aux (st, lst) = - if not (Scanf.Scanning.end_of_input scbuf) then - begin - let acc = - try - Scanf.bscanf scbuf "%S %S\n" - (fun e d -> - let t = - e, d - in - if SetTupleString.mem t st then - st, lst - else - SetTupleString.add t st, - t :: lst) - with Scanf.Scan_failure _ -> - failwith - (Scanf.bscanf scbuf - "%l" - (fun line -> - Printf.sprintf - "Malformed log file '%s' at line %d" - default_filename - line)) - in - read_aux acc - end - else - begin - close_in chn; - List.rev lst - end - in - read_aux (SetTupleString.empty, []) - end - else - begin - [] - end - - let register event data = - let chn_out = - open_out_gen [Open_append; Open_creat; Open_text] 0o644 default_filename - in - Printf.fprintf chn_out "%S %S\n" event data; - close_out chn_out - - let unregister event data = - if Sys.file_exists default_filename then - begin - let lst = - load () - in - let chn_out = - open_out default_filename - in - let write_something = - ref false - in - List.iter - (fun (e, d) -> - if e <> event || d <> data then - begin - write_something := true; - Printf.fprintf chn_out "%S %S\n" e d - end) - lst; - close_out chn_out; - if not !write_something then - Sys.remove default_filename - end - - let filter events = - let st_events = - List.fold_left - (fun st e -> - SetString.add e st) - SetString.empty - events - in - List.filter - (fun (e, _) -> SetString.mem e st_events) - (load ()) - - let exists event data = - List.exists - (fun v -> (event, data) = v) - (load ()) -end - -module BaseBuilt = struct -(* # 21 "src/base/BaseBuilt.ml" *) - - open OASISTypes - open OASISGettext - open BaseStandardVar - open BaseMessage - - type t = - | BExec (* Executable *) - | BExecLib (* Library coming with executable *) - | BLib (* Library *) - | BDoc (* Document *) - - let to_log_event_file t nm = - "built_"^ - (match t with - | BExec -> "exec" - | BExecLib -> "exec_lib" - | BLib -> "lib" - | BDoc -> "doc")^ - "_"^nm - - let to_log_event_done t nm = - "is_"^(to_log_event_file t nm) - - let register t nm lst = - BaseLog.register - (to_log_event_done t nm) - "true"; - List.iter - (fun alt -> - let registered = - List.fold_left - (fun registered fn -> - if OASISFileUtil.file_exists_case fn then - begin - BaseLog.register - (to_log_event_file t nm) - (if Filename.is_relative fn then - Filename.concat (Sys.getcwd ()) fn - else - fn); - true - end - else - registered) - false - alt - in - if not registered then - warning - (f_ "Cannot find an existing alternative files among: %s") - (String.concat (s_ ", ") alt)) - lst - - let unregister t nm = - List.iter - (fun (e, d) -> - BaseLog.unregister e d) - (BaseLog.filter - [to_log_event_file t nm; - to_log_event_done t nm]) - - let fold t nm f acc = - List.fold_left - (fun acc (_, fn) -> - if OASISFileUtil.file_exists_case fn then - begin - f acc fn - end - else - begin - warning - (f_ "File '%s' has been marked as built \ - for %s but doesn't exist") - fn - (Printf.sprintf - (match t with - | BExec | BExecLib -> - (f_ "executable %s") - | BLib -> - (f_ "library %s") - | BDoc -> - (f_ "documentation %s")) - nm); - acc - end) - acc - (BaseLog.filter - [to_log_event_file t nm]) - - let is_built t nm = - List.fold_left - (fun is_built (_, d) -> - (try - bool_of_string d - with _ -> - false)) - false - (BaseLog.filter - [to_log_event_done t nm]) - - let of_executable ffn (cs, bs, exec) = - let unix_exec_is, unix_dll_opt = - OASISExecutable.unix_exec_is - (cs, bs, exec) - (fun () -> - bool_of_string - (is_native ())) - ext_dll - ext_program - in - let evs = - (BExec, cs.cs_name, [[ffn unix_exec_is]]) - :: - (match unix_dll_opt with - | Some fn -> - [BExecLib, cs.cs_name, [[ffn fn]]] - | None -> - []) - in - evs, - unix_exec_is, - unix_dll_opt - - let of_library ffn (cs, bs, lib) = - let unix_lst = - OASISLibrary.generated_unix_files - ~ctxt:!BaseContext.default - ~source_file_exists:(fun fn -> - OASISFileUtil.file_exists_case (OASISHostPath.of_unix fn)) - ~is_native:(bool_of_string (is_native ())) - ~has_native_dynlink:(bool_of_string (native_dynlink ())) - ~ext_lib:(ext_lib ()) - ~ext_dll:(ext_dll ()) - (cs, bs, lib) - in - let evs = - [BLib, - cs.cs_name, - List.map (List.map ffn) unix_lst] - in - evs, unix_lst - -end - -module BaseCustom = struct -(* # 21 "src/base/BaseCustom.ml" *) - - open BaseEnv - open BaseMessage - open OASISTypes - open OASISGettext - - let run cmd args extra_args = - OASISExec.run ~ctxt:!BaseContext.default ~quote:false - (var_expand cmd) - (List.map - var_expand - (args @ (Array.to_list extra_args))) - - let hook ?(failsafe=false) cstm f e = - let optional_command lst = - let printer = - function - | Some (cmd, args) -> String.concat " " (cmd :: args) - | None -> s_ "No command" - in - match - var_choose - ~name:(s_ "Pre/Post Command") - ~printer - lst with - | Some (cmd, args) -> - begin - try - run cmd args [||] - with e when failsafe -> - warning - (f_ "Command '%s' fail with error: %s") - (String.concat " " (cmd :: args)) - (match e with - | Failure msg -> msg - | e -> Printexc.to_string e) - end - | None -> - () - in - let res = - optional_command cstm.pre_command; - f e - in - optional_command cstm.post_command; - res -end - -module BaseDynVar = struct -(* # 21 "src/base/BaseDynVar.ml" *) - - - open OASISTypes - open OASISGettext - open BaseEnv - open BaseBuilt - - let init pkg = - (* TODO: disambiguate exec vs other variable by adding exec_VARNAME. *) - (* TODO: provide compile option for library libary_byte_args_VARNAME... *) - List.iter - (function - | Executable (cs, bs, exec) -> - if var_choose bs.bs_build then - var_ignore - (var_redefine - (* We don't save this variable *) - ~dump:false - ~short_desc:(fun () -> - Printf.sprintf - (f_ "Filename of executable '%s'") - cs.cs_name) - (OASISUtils.varname_of_string cs.cs_name) - (fun () -> - let fn_opt = - fold - BExec cs.cs_name - (fun _ fn -> Some fn) - None - in - match fn_opt with - | Some fn -> fn - | None -> - raise - (PropList.Not_set - (cs.cs_name, - Some (Printf.sprintf - (f_ "Executable '%s' not yet built.") - cs.cs_name))))) - - | Library _ | Flag _ | Test _ | SrcRepo _ | Doc _ -> - ()) - pkg.sections -end - -module BaseTest = struct -(* # 21 "src/base/BaseTest.ml" *) - - open BaseEnv - open BaseMessage - open OASISTypes - open OASISExpr - open OASISGettext - - let test lst pkg extra_args = - - let one_test (failure, n) (test_plugin, cs, test) = - if var_choose - ~name:(Printf.sprintf - (f_ "test %s run") - cs.cs_name) - ~printer:string_of_bool - test.test_run then - begin - let () = - info (f_ "Running test '%s'") cs.cs_name - in - let back_cwd = - match test.test_working_directory with - | Some dir -> - let cwd = - Sys.getcwd () - in - let chdir d = - info (f_ "Changing directory to '%s'") d; - Sys.chdir d - in - chdir dir; - fun () -> chdir cwd - - | None -> - fun () -> () - in - try - let failure_percent = - BaseCustom.hook - test.test_custom - (test_plugin pkg (cs, test)) - extra_args - in - back_cwd (); - (failure_percent +. failure, n + 1) - with e -> - begin - back_cwd (); - raise e - end - end - else - begin - info (f_ "Skipping test '%s'") cs.cs_name; - (failure, n) - end - in - let (failed, n) = - List.fold_left - one_test - (0.0, 0) - lst - in - let failure_percent = - if n = 0 then - 0.0 - else - failed /. (float_of_int n) - in - let msg = - Printf.sprintf - (f_ "Tests had a %.2f%% failure rate") - (100. *. failure_percent) - in - if failure_percent > 0.0 then - failwith msg - else - info "%s" msg; - - (* Possible explanation why the tests where not run. *) - if OASISVersion.version_0_3_or_after pkg.oasis_version && - not (bool_of_string (BaseStandardVar.tests ())) && - lst <> [] then - BaseMessage.warning - "Tests are turned off, consider enabling with \ - 'ocaml setup.ml -configure --enable-tests'" -end - -module BaseDoc = struct -(* # 21 "src/base/BaseDoc.ml" *) - - open BaseEnv - open BaseMessage - open OASISTypes - open OASISGettext - - let doc lst pkg extra_args = - - let one_doc (doc_plugin, cs, doc) = - if var_choose - ~name:(Printf.sprintf - (f_ "documentation %s build") - cs.cs_name) - ~printer:string_of_bool - doc.doc_build then - begin - info (f_ "Building documentation '%s'") cs.cs_name; - BaseCustom.hook - doc.doc_custom - (doc_plugin pkg (cs, doc)) - extra_args - end - in - List.iter one_doc lst; - - if OASISVersion.version_0_3_or_after pkg.oasis_version && - not (bool_of_string (BaseStandardVar.docs ())) && - lst <> [] then - BaseMessage.warning - "Docs are turned off, consider enabling with \ - 'ocaml setup.ml -configure --enable-docs'" -end - -module BaseSetup = struct -(* # 21 "src/base/BaseSetup.ml" *) - - open BaseEnv - open BaseMessage - open OASISTypes - open OASISSection - open OASISGettext - open OASISUtils - - type std_args_fun = - package -> string array -> unit - - type ('a, 'b) section_args_fun = - name * (package -> (common_section * 'a) -> string array -> 'b) - - type t = - { - configure: std_args_fun; - build: std_args_fun; - doc: ((doc, unit) section_args_fun) list; - test: ((test, float) section_args_fun) list; - install: std_args_fun; - uninstall: std_args_fun; - clean: std_args_fun list; - clean_doc: (doc, unit) section_args_fun list; - clean_test: (test, unit) section_args_fun list; - distclean: std_args_fun list; - distclean_doc: (doc, unit) section_args_fun list; - distclean_test: (test, unit) section_args_fun list; - package: package; - oasis_fn: string option; - oasis_version: string; - oasis_digest: Digest.t option; - oasis_exec: string option; - oasis_setup_args: string list; - setup_update: bool; - } - - (* Associate a plugin function with data from package *) - let join_plugin_sections filter_map lst = - List.rev - (List.fold_left - (fun acc sct -> - match filter_map sct with - | Some e -> - e :: acc - | None -> - acc) - [] - lst) - - (* Search for plugin data associated with a section name *) - let lookup_plugin_section plugin action nm lst = - try - List.assoc nm lst - with Not_found -> - failwithf - (f_ "Cannot find plugin %s matching section %s for %s action") - plugin - nm - action - - let configure t args = - (* Run configure *) - BaseCustom.hook - t.package.conf_custom - (fun () -> - (* Reload if preconf has changed it *) - begin - try - unload (); - load (); - with _ -> - () - end; - - (* Run plugin's configure *) - t.configure t.package args; - - (* Dump to allow postconf to change it *) - dump ()) - (); - - (* Reload environment *) - unload (); - load (); - - (* Save environment *) - print (); - - (* Replace data in file *) - BaseFileAB.replace t.package.files_ab - - let build t args = - BaseCustom.hook - t.package.build_custom - (t.build t.package) - args - - let doc t args = - BaseDoc.doc - (join_plugin_sections - (function - | Doc (cs, e) -> - Some - (lookup_plugin_section - "documentation" - (s_ "build") - cs.cs_name - t.doc, - cs, - e) - | _ -> - None) - t.package.sections) - t.package - args - - let test t args = - BaseTest.test - (join_plugin_sections - (function - | Test (cs, e) -> - Some - (lookup_plugin_section - "test" - (s_ "run") - cs.cs_name - t.test, - cs, - e) - | _ -> - None) - t.package.sections) - t.package - args - - let all t args = - let rno_doc = - ref false - in - let rno_test = - ref false - in - Arg.parse_argv - ~current:(ref 0) - (Array.of_list - ((Sys.executable_name^" all") :: - (Array.to_list args))) - [ - "-no-doc", - Arg.Set rno_doc, - s_ "Don't run doc target"; - - "-no-test", - Arg.Set rno_test, - s_ "Don't run test target"; - ] - (failwithf (f_ "Don't know what to do with '%s'")) - ""; - - info "Running configure step"; - configure t [||]; - - info "Running build step"; - build t [||]; - - (* Load setup.log dynamic variables *) - BaseDynVar.init t.package; - - if not !rno_doc then - begin - info "Running doc step"; - doc t [||]; - end - else - begin - info "Skipping doc step" - end; - - if not !rno_test then - begin - info "Running test step"; - test t [||] - end - else - begin - info "Skipping test step" - end - - let install t args = - BaseCustom.hook - t.package.install_custom - (t.install t.package) - args - - let uninstall t args = - BaseCustom.hook - t.package.uninstall_custom - (t.uninstall t.package) - args - - let reinstall t args = - uninstall t args; - install t args - - let clean, distclean = - let failsafe f a = - try - f a - with e -> - warning - (f_ "Action fail with error: %s") - (match e with - | Failure msg -> msg - | e -> Printexc.to_string e) - in - - let generic_clean t cstm mains docs tests args = - BaseCustom.hook - ~failsafe:true - cstm - (fun () -> - (* Clean section *) - List.iter - (function - | Test (cs, test) -> - let f = - try - List.assoc cs.cs_name tests - with Not_found -> - fun _ _ _ -> () - in - failsafe - (f t.package (cs, test)) - args - | Doc (cs, doc) -> - let f = - try - List.assoc cs.cs_name docs - with Not_found -> - fun _ _ _ -> () - in - failsafe - (f t.package (cs, doc)) - args - | Library _ - | Executable _ - | Flag _ - | SrcRepo _ -> - ()) - t.package.sections; - (* Clean whole package *) - List.iter - (fun f -> - failsafe - (f t.package) - args) - mains) - () - in - - let clean t args = - generic_clean - t - t.package.clean_custom - t.clean - t.clean_doc - t.clean_test - args - in - - let distclean t args = - (* Call clean *) - clean t args; - - (* Call distclean code *) - generic_clean - t - t.package.distclean_custom - t.distclean - t.distclean_doc - t.distclean_test - args; - - (* Remove generated file *) - List.iter - (fun fn -> - if Sys.file_exists fn then - begin - info (f_ "Remove '%s'") fn; - Sys.remove fn - end) - (BaseEnv.default_filename - :: - BaseLog.default_filename - :: - (List.rev_map BaseFileAB.to_filename t.package.files_ab)) - in - - clean, distclean - - let version t _ = - print_endline t.oasis_version - - let update_setup_ml, no_update_setup_ml_cli = - let b = ref true in - b, - ("-no-update-setup-ml", - Arg.Clear b, - s_ " Don't try to update setup.ml, even if _oasis has changed.") - - let update_setup_ml t = - let oasis_fn = - match t.oasis_fn with - | Some fn -> fn - | None -> "_oasis" - in - let oasis_exec = - match t.oasis_exec with - | Some fn -> fn - | None -> "oasis" - in - let ocaml = - Sys.executable_name - in - let setup_ml, args = - match Array.to_list Sys.argv with - | setup_ml :: args -> - setup_ml, args - | [] -> - failwith - (s_ "Expecting non-empty command line arguments.") - in - let ocaml, setup_ml = - if Sys.executable_name = Sys.argv.(0) then - (* We are not running in standard mode, probably the script - * is precompiled. - *) - "ocaml", "setup.ml" - else - ocaml, setup_ml - in - let no_update_setup_ml_cli, _, _ = no_update_setup_ml_cli in - let do_update () = - let oasis_exec_version = - OASISExec.run_read_one_line - ~ctxt:!BaseContext.default - ~f_exit_code: - (function - | 0 -> - () - | 1 -> - failwithf - (f_ "Executable '%s' is probably an old version \ - of oasis (< 0.3.0), please update to version \ - v%s.") - oasis_exec t.oasis_version - | 127 -> - failwithf - (f_ "Cannot find executable '%s', please install \ - oasis v%s.") - oasis_exec t.oasis_version - | n -> - failwithf - (f_ "Command '%s version' exited with code %d.") - oasis_exec n) - oasis_exec ["version"] - in - if OASISVersion.comparator_apply - (OASISVersion.version_of_string oasis_exec_version) - (OASISVersion.VGreaterEqual - (OASISVersion.version_of_string t.oasis_version)) then - begin - (* We have a version >= for the executable oasis, proceed with - * update. - *) - (* TODO: delegate this check to 'oasis setup'. *) - if Sys.os_type = "Win32" then - failwithf - (f_ "It is not possible to update the running script \ - setup.ml on Windows. Please update setup.ml by \ - running '%s'.") - (String.concat " " (oasis_exec :: "setup" :: t.oasis_setup_args)) - else - begin - OASISExec.run - ~ctxt:!BaseContext.default - ~f_exit_code: - (function - | 0 -> - () - | n -> - failwithf - (f_ "Unable to update setup.ml using '%s', \ - please fix the problem and retry.") - oasis_exec) - oasis_exec ("setup" :: t.oasis_setup_args); - OASISExec.run ~ctxt:!BaseContext.default ocaml (setup_ml :: args) - end - end - else - failwithf - (f_ "The version of '%s' (v%s) doesn't match the version of \ - oasis used to generate the %s file. Please install at \ - least oasis v%s.") - oasis_exec oasis_exec_version setup_ml t.oasis_version - in - - if !update_setup_ml then - begin - try - match t.oasis_digest with - | Some dgst -> - if Sys.file_exists oasis_fn && dgst <> Digest.file "_oasis" then - begin - do_update (); - true - end - else - false - | None -> - false - with e -> - error - (f_ "Error when updating setup.ml. If you want to avoid this error, \ - you can bypass the update of %s by running '%s %s %s %s'") - setup_ml ocaml setup_ml no_update_setup_ml_cli - (String.concat " " args); - raise e - end - else - false - - let setup t = - let catch_exn = - ref true - in - try - let act_ref = - ref (fun _ -> - failwithf - (f_ "No action defined, run '%s %s -help'") - Sys.executable_name - Sys.argv.(0)) - - in - let extra_args_ref = - ref [] - in - let allow_empty_env_ref = - ref false - in - let arg_handle ?(allow_empty_env=false) act = - Arg.Tuple - [ - Arg.Rest (fun str -> extra_args_ref := str :: !extra_args_ref); - - Arg.Unit - (fun () -> - allow_empty_env_ref := allow_empty_env; - act_ref := act); - ] - in - - Arg.parse - (Arg.align - ([ - "-configure", - arg_handle ~allow_empty_env:true configure, - s_ "[options*] Configure the whole build process."; - - "-build", - arg_handle build, - s_ "[options*] Build executables and libraries."; - - "-doc", - arg_handle doc, - s_ "[options*] Build documents."; - - "-test", - arg_handle test, - s_ "[options*] Run tests."; - - "-all", - arg_handle ~allow_empty_env:true all, - s_ "[options*] Run configure, build, doc and test targets."; - - "-install", - arg_handle install, - s_ "[options*] Install libraries, data, executables \ - and documents."; - - "-uninstall", - arg_handle uninstall, - s_ "[options*] Uninstall libraries, data, executables \ - and documents."; - - "-reinstall", - arg_handle reinstall, - s_ "[options*] Uninstall and install libraries, data, \ - executables and documents."; - - "-clean", - arg_handle ~allow_empty_env:true clean, - s_ "[options*] Clean files generated by a build."; - - "-distclean", - arg_handle ~allow_empty_env:true distclean, - s_ "[options*] Clean files generated by a build and configure."; - - "-version", - arg_handle ~allow_empty_env:true version, - s_ " Display version of OASIS used to generate this setup.ml."; - - "-no-catch-exn", - Arg.Clear catch_exn, - s_ " Don't catch exception, useful for debugging."; - ] - @ - (if t.setup_update then - [no_update_setup_ml_cli] - else - []) - @ (BaseContext.args ()))) - (failwithf (f_ "Don't know what to do with '%s'")) - (s_ "Setup and run build process current package\n"); - - (* Build initial environment *) - load ~allow_empty:!allow_empty_env_ref (); - - (** Initialize flags *) - List.iter - (function - | Flag (cs, {flag_description = hlp; - flag_default = choices}) -> - begin - let apply ?short_desc () = - var_ignore - (var_define - ~cli:CLIEnable - ?short_desc - (OASISUtils.varname_of_string cs.cs_name) - (fun () -> - string_of_bool - (var_choose - ~name:(Printf.sprintf - (f_ "default value of flag %s") - cs.cs_name) - ~printer:string_of_bool - choices))) - in - match hlp with - | Some hlp -> - apply ~short_desc:(fun () -> hlp) () - | None -> - apply () - end - | _ -> - ()) - t.package.sections; - - BaseStandardVar.init t.package; - - BaseDynVar.init t.package; - - if t.setup_update && update_setup_ml t then - () - else - !act_ref t (Array.of_list (List.rev !extra_args_ref)) - - with e when !catch_exn -> - error "%s" (Printexc.to_string e); - exit 1 - -end - - -# 4480 "setup.ml" -module InternalConfigurePlugin = struct -(* # 21 "src/plugins/internal/InternalConfigurePlugin.ml" *) - - (** Configure using internal scheme - @author Sylvain Le Gall - *) - - open BaseEnv - open OASISTypes - open OASISUtils - open OASISGettext - open BaseMessage - - (** Configure build using provided series of check to be done - * and then output corresponding file. - *) - let configure pkg argv = - let var_ignore_eval var = - let _s : string = - var () - in - () - in - - let errors = - ref SetString.empty - in - - let buff = - Buffer.create 13 - in - - let add_errors fmt = - Printf.kbprintf - (fun b -> - errors := SetString.add (Buffer.contents b) !errors; - Buffer.clear b) - buff - fmt - in - - let warn_exception e = - warning "%s" (Printexc.to_string e) - in - - (* Check tools *) - let check_tools lst = - List.iter - (function - | ExternalTool tool -> - begin - try - var_ignore_eval (BaseCheck.prog tool) - with e -> - warn_exception e; - add_errors (f_ "Cannot find external tool '%s'") tool - end - | InternalExecutable nm1 -> - (* Check that matching tool is built *) - List.iter - (function - | Executable ({cs_name = nm2}, - {bs_build = build}, - _) when nm1 = nm2 -> - if not (var_choose build) then - add_errors - (f_ "Cannot find buildable internal executable \ - '%s' when checking build depends") - nm1 - | _ -> - ()) - pkg.sections) - lst - in - - let build_checks sct bs = - if var_choose bs.bs_build then - begin - if bs.bs_compiled_object = Native then - begin - try - var_ignore_eval BaseStandardVar.ocamlopt - with e -> - warn_exception e; - add_errors - (f_ "Section %s requires native compilation") - (OASISSection.string_of_section sct) - end; - - (* Check tools *) - check_tools bs.bs_build_tools; - - (* Check depends *) - List.iter - (function - | FindlibPackage (findlib_pkg, version_comparator) -> - begin - try - var_ignore_eval - (BaseCheck.package ?version_comparator findlib_pkg) - with e -> - warn_exception e; - match version_comparator with - | None -> - add_errors - (f_ "Cannot find findlib package %s") - findlib_pkg - | Some ver_cmp -> - add_errors - (f_ "Cannot find findlib package %s (%s)") - findlib_pkg - (OASISVersion.string_of_comparator ver_cmp) - end - | InternalLibrary nm1 -> - (* Check that matching library is built *) - List.iter - (function - | Library ({cs_name = nm2}, - {bs_build = build}, - _) when nm1 = nm2 -> - if not (var_choose build) then - add_errors - (f_ "Cannot find buildable internal library \ - '%s' when checking build depends") - nm1 - | _ -> - ()) - pkg.sections) - bs.bs_build_depends - end - in - - (* Parse command line *) - BaseArgExt.parse argv (BaseEnv.args ()); - - (* OCaml version *) - begin - match pkg.ocaml_version with - | Some ver_cmp -> - begin - try - var_ignore_eval - (BaseCheck.version - "ocaml" - ver_cmp - BaseStandardVar.ocaml_version) - with e -> - warn_exception e; - add_errors - (f_ "OCaml version %s doesn't match version constraint %s") - (BaseStandardVar.ocaml_version ()) - (OASISVersion.string_of_comparator ver_cmp) - end - | None -> - () - end; - - (* Findlib version *) - begin - match pkg.findlib_version with - | Some ver_cmp -> - begin - try - var_ignore_eval - (BaseCheck.version - "findlib" - ver_cmp - BaseStandardVar.findlib_version) - with e -> - warn_exception e; - add_errors - (f_ "Findlib version %s doesn't match version constraint %s") - (BaseStandardVar.findlib_version ()) - (OASISVersion.string_of_comparator ver_cmp) - end - | None -> - () - end; - - (* FlexDLL *) - if BaseStandardVar.os_type () = "Win32" || - BaseStandardVar.os_type () = "Cygwin" then - begin - try - var_ignore_eval BaseStandardVar.flexlink - with e -> - warn_exception e; - add_errors (f_ "Cannot find 'flexlink'") - end; - - (* Check build depends *) - List.iter - (function - | Executable (_, bs, _) - | Library (_, bs, _) as sct -> - build_checks sct bs - | Doc (_, doc) -> - if var_choose doc.doc_build then - check_tools doc.doc_build_tools - | Test (_, test) -> - if var_choose test.test_run then - check_tools test.test_tools - | _ -> - ()) - pkg.sections; - - (* Check if we need native dynlink (presence of libraries that compile to - * native) - *) - begin - let has_cmxa = - List.exists - (function - | Library (_, bs, _) -> - var_choose bs.bs_build && - (bs.bs_compiled_object = Native || - (bs.bs_compiled_object = Best && - bool_of_string (BaseStandardVar.is_native ()))) - | _ -> - false) - pkg.sections - in - if has_cmxa then - var_ignore_eval BaseStandardVar.native_dynlink - end; - - (* Check errors *) - if SetString.empty != !errors then - begin - List.iter - (fun e -> error "%s" e) - (SetString.elements !errors); - failwithf - (fn_ - "%d configuration error" - "%d configuration errors" - (SetString.cardinal !errors)) - (SetString.cardinal !errors) - end - -end - -module InternalInstallPlugin = struct -(* # 21 "src/plugins/internal/InternalInstallPlugin.ml" *) - - (** Install using internal scheme - @author Sylvain Le Gall - *) - - open BaseEnv - open BaseStandardVar - open BaseMessage - open OASISTypes - open OASISLibrary - open OASISGettext - open OASISUtils - - let exec_hook = - ref (fun (cs, bs, exec) -> cs, bs, exec) - - let lib_hook = - ref (fun (cs, bs, lib) -> cs, bs, lib, []) - - let doc_hook = - ref (fun (cs, doc) -> cs, doc) - - let install_file_ev = - "install-file" - - let install_dir_ev = - "install-dir" - - let install_findlib_ev = - "install-findlib" - - let win32_max_command_line_length = 8000 - - let split_install_command ocamlfind findlib_name meta files = - if Sys.os_type = "Win32" then - (* Arguments for the first command: *) - let first_args = ["install"; findlib_name; meta] in - (* Arguments for remaining commands: *) - let other_args = ["install"; findlib_name; "-add"] in - (* Extract as much files as possible from [files], [len] is - the current command line length: *) - let rec get_files len acc files = - match files with - | [] -> - (List.rev acc, []) - | file :: rest -> - let len = len + 1 + String.length file in - if len > win32_max_command_line_length then - (List.rev acc, files) - else - get_files len (file :: acc) rest - in - (* Split the command into several commands. *) - let rec split args files = - match files with - | [] -> - [] - | _ -> - (* Length of "ocamlfind install <lib> [META|-add]" *) - let len = - List.fold_left - (fun len arg -> - len + 1 (* for the space *) + String.length arg) - (String.length ocamlfind) - args - in - match get_files len [] files with - | ([], _) -> - failwith (s_ "Command line too long.") - | (firsts, others) -> - let cmd = args @ firsts in - (* Use -add for remaining commands: *) - let () = - let findlib_ge_132 = - OASISVersion.comparator_apply - (OASISVersion.version_of_string - (BaseStandardVar.findlib_version ())) - (OASISVersion.VGreaterEqual - (OASISVersion.version_of_string "1.3.2")) - in - if not findlib_ge_132 then - failwithf - (f_ "Installing the library %s require to use the flag \ - '-add' of ocamlfind because the command line is too \ - long. This flag is only available for findlib 1.3.2. \ - Please upgrade findlib from %s to 1.3.2") - findlib_name (BaseStandardVar.findlib_version ()) - in - let cmds = split other_args others in - cmd :: cmds - in - (* The first command does not use -add: *) - split first_args files - else - ["install" :: findlib_name :: meta :: files] - - let install pkg argv = - - let in_destdir = - try - let destdir = - destdir () - in - (* Practically speaking destdir is prepended - * at the beginning of the target filename - *) - fun fn -> destdir^fn - with PropList.Not_set _ -> - fun fn -> fn - in - - let install_file ?tgt_fn src_file envdir = - let tgt_dir = - in_destdir (envdir ()) - in - let tgt_file = - Filename.concat - tgt_dir - (match tgt_fn with - | Some fn -> - fn - | None -> - Filename.basename src_file) - in - (* Create target directory if needed *) - OASISFileUtil.mkdir_parent - ~ctxt:!BaseContext.default - (fun dn -> - info (f_ "Creating directory '%s'") dn; - BaseLog.register install_dir_ev dn) - tgt_dir; - - (* Really install files *) - info (f_ "Copying file '%s' to '%s'") src_file tgt_file; - OASISFileUtil.cp ~ctxt:!BaseContext.default src_file tgt_file; - BaseLog.register install_file_ev tgt_file - in - - (* Install data into defined directory *) - let install_data srcdir lst tgtdir = - let tgtdir = - OASISHostPath.of_unix (var_expand tgtdir) - in - List.iter - (fun (src, tgt_opt) -> - let real_srcs = - OASISFileUtil.glob - ~ctxt:!BaseContext.default - (Filename.concat srcdir src) - in - if real_srcs = [] then - failwithf - (f_ "Wildcard '%s' doesn't match any files") - src; - List.iter - (fun fn -> - install_file - fn - (fun () -> - match tgt_opt with - | Some s -> - OASISHostPath.of_unix (var_expand s) - | None -> - tgtdir)) - real_srcs) - lst - in - - (** Install all libraries *) - let install_libs pkg = - - let files_of_library (f_data, acc) data_lib = - let cs, bs, lib, lib_extra = - !lib_hook data_lib - in - if var_choose bs.bs_install && - BaseBuilt.is_built BaseBuilt.BLib cs.cs_name then - begin - let acc = - (* Start with acc + lib_extra *) - List.rev_append lib_extra acc - in - let acc = - (* Add uncompiled header from the source tree *) - let path = - OASISHostPath.of_unix bs.bs_path - in - List.fold_left - (fun acc modul -> - try - List.find - OASISFileUtil.file_exists_case - (List.map - (Filename.concat path) - [modul^".mli"; - modul^".ml"; - String.uncapitalize modul^".mli"; - String.capitalize modul^".mli"; - String.uncapitalize modul^".ml"; - String.capitalize modul^".ml"]) - :: acc - with Not_found -> - begin - warning - (f_ "Cannot find source header for module %s \ - in library %s") - modul cs.cs_name; - acc - end) - acc - lib.lib_modules - in - - let acc = - (* Get generated files *) - BaseBuilt.fold - BaseBuilt.BLib - cs.cs_name - (fun acc fn -> fn :: acc) - acc - in - - let f_data () = - (* Install data associated with the library *) - install_data - bs.bs_path - bs.bs_data_files - (Filename.concat - (datarootdir ()) - pkg.name); - f_data () - in - - (f_data, acc) - end - else - begin - (f_data, acc) - end - in - - (* Install one group of library *) - let install_group_lib grp = - (* Iterate through all group nodes *) - let rec install_group_lib_aux data_and_files grp = - let data_and_files, children = - match grp with - | Container (_, children) -> - data_and_files, children - | Package (_, cs, bs, lib, children) -> - files_of_library data_and_files (cs, bs, lib), children - in - List.fold_left - install_group_lib_aux - data_and_files - children - in - - (* Findlib name of the root library *) - let findlib_name = - findlib_of_group grp - in - - (* Determine root library *) - let root_lib = - root_of_group grp - in - - (* All files to install for this library *) - let f_data, files = - install_group_lib_aux (ignore, []) grp - in - - (* Really install, if there is something to install *) - if files = [] then - begin - warning - (f_ "Nothing to install for findlib library '%s'") - findlib_name - end - else - begin - let meta = - (* Search META file *) - let (_, bs, _) = - root_lib - in - let res = - Filename.concat bs.bs_path "META" - in - if not (OASISFileUtil.file_exists_case res) then - failwithf - (f_ "Cannot find file '%s' for findlib library %s") - res - findlib_name; - res - in - let files = - (* Make filename shorter to avoid hitting command max line length - * too early, esp. on Windows. - *) - let remove_prefix p n = - let plen = String.length p in - let nlen = String.length n in - if plen <= nlen && String.sub n 0 plen = p then - begin - let fn_sep = - if Sys.os_type = "Win32" then - '\\' - else - '/' - in - let cutpoint = plen + - (if plen < nlen && n.[plen] = fn_sep then - 1 - else - 0) - in - String.sub n cutpoint (nlen - cutpoint) - end - else - n - in - List.map (remove_prefix (Sys.getcwd ())) files - in - info - (f_ "Installing findlib library '%s'") - findlib_name; - let ocamlfind = ocamlfind () in - let commands = - split_install_command - ocamlfind - findlib_name - meta - files - in - List.iter - (OASISExec.run ~ctxt:!BaseContext.default ocamlfind) - commands; - BaseLog.register install_findlib_ev findlib_name - end; - - (* Install data files *) - f_data (); - - in - - let group_libs, _, _ = - findlib_mapping pkg - in - - (* We install libraries in groups *) - List.iter install_group_lib group_libs - in - - let install_execs pkg = - let install_exec data_exec = - let (cs, bs, exec) = - !exec_hook data_exec - in - if var_choose bs.bs_install && - BaseBuilt.is_built BaseBuilt.BExec cs.cs_name then - begin - let exec_libdir () = - Filename.concat - (libdir ()) - pkg.name - in - BaseBuilt.fold - BaseBuilt.BExec - cs.cs_name - (fun () fn -> - install_file - ~tgt_fn:(cs.cs_name ^ ext_program ()) - fn - bindir) - (); - BaseBuilt.fold - BaseBuilt.BExecLib - cs.cs_name - (fun () fn -> - install_file - fn - exec_libdir) - (); - install_data - bs.bs_path - bs.bs_data_files - (Filename.concat - (datarootdir ()) - pkg.name) - end - in - List.iter - (function - | Executable (cs, bs, exec)-> - install_exec (cs, bs, exec) - | _ -> - ()) - pkg.sections - in - - let install_docs pkg = - let install_doc data = - let (cs, doc) = - !doc_hook data - in - if var_choose doc.doc_install && - BaseBuilt.is_built BaseBuilt.BDoc cs.cs_name then - begin - let tgt_dir = - OASISHostPath.of_unix (var_expand doc.doc_install_dir) - in - BaseBuilt.fold - BaseBuilt.BDoc - cs.cs_name - (fun () fn -> - install_file - fn - (fun () -> tgt_dir)) - (); - install_data - Filename.current_dir_name - doc.doc_data_files - doc.doc_install_dir - end - in - List.iter - (function - | Doc (cs, doc) -> - install_doc (cs, doc) - | _ -> - ()) - pkg.sections - in - - install_libs pkg; - install_execs pkg; - install_docs pkg - - (* Uninstall already installed data *) - let uninstall _ argv = - List.iter - (fun (ev, data) -> - if ev = install_file_ev then - begin - if OASISFileUtil.file_exists_case data then - begin - info - (f_ "Removing file '%s'") - data; - Sys.remove data - end - else - begin - warning - (f_ "File '%s' doesn't exist anymore") - data - end - end - else if ev = install_dir_ev then - begin - if Sys.file_exists data && Sys.is_directory data then - begin - if Sys.readdir data = [||] then - begin - info - (f_ "Removing directory '%s'") - data; - OASISFileUtil.rmdir ~ctxt:!BaseContext.default data - end - else - begin - warning - (f_ "Directory '%s' is not empty (%s)") - data - (String.concat - ", " - (Array.to_list - (Sys.readdir data))) - end - end - else - begin - warning - (f_ "Directory '%s' doesn't exist anymore") - data - end - end - else if ev = install_findlib_ev then - begin - info (f_ "Removing findlib library '%s'") data; - OASISExec.run ~ctxt:!BaseContext.default - (ocamlfind ()) ["remove"; data] - end - else - failwithf (f_ "Unknown log event '%s'") ev; - BaseLog.unregister ev data) - (* We process event in reverse order *) - (List.rev - (BaseLog.filter - [install_file_ev; - install_dir_ev; - install_findlib_ev;])) - -end - - -# 5233 "setup.ml" -module OCamlbuildCommon = struct -(* # 21 "src/plugins/ocamlbuild/OCamlbuildCommon.ml" *) - - (** Functions common to OCamlbuild build and doc plugin - *) - - open OASISGettext - open BaseEnv - open BaseStandardVar - - let ocamlbuild_clean_ev = - "ocamlbuild-clean" - - let ocamlbuildflags = - var_define - ~short_desc:(fun () -> "OCamlbuild additional flags") - "ocamlbuildflags" - (fun () -> "") - - (** Fix special arguments depending on environment *) - let fix_args args extra_argv = - List.flatten - [ - if (os_type ()) = "Win32" then - [ - "-classic-display"; - "-no-log"; - "-no-links"; - "-install-lib-dir"; - (Filename.concat (standard_library ()) "ocamlbuild") - ] - else - []; - - if not (bool_of_string (is_native ())) || (os_type ()) = "Win32" then - [ - "-byte-plugin" - ] - else - []; - args; - - if bool_of_string (debug ()) then - ["-tag"; "debug"] - else - []; - - if bool_of_string (profile ()) then - ["-tag"; "profile"] - else - []; - - OASISString.nsplit (ocamlbuildflags ()) ' '; - - Array.to_list extra_argv; - ] - - (** Run 'ocamlbuild -clean' if not already done *) - let run_clean extra_argv = - let extra_cli = - String.concat " " (Array.to_list extra_argv) - in - (* Run if never called with these args *) - if not (BaseLog.exists ocamlbuild_clean_ev extra_cli) then - begin - OASISExec.run ~ctxt:!BaseContext.default - (ocamlbuild ()) (fix_args ["-clean"] extra_argv); - BaseLog.register ocamlbuild_clean_ev extra_cli; - at_exit - (fun () -> - try - BaseLog.unregister ocamlbuild_clean_ev extra_cli - with _ -> - ()) - end - - (** Run ocamlbuild, unregister all clean events *) - let run_ocamlbuild args extra_argv = - (* TODO: enforce that target in args must be UNIX encoded i.e. toto/index.html - *) - OASISExec.run ~ctxt:!BaseContext.default - (ocamlbuild ()) (fix_args args extra_argv); - (* Remove any clean event, we must run it again *) - List.iter - (fun (e, d) -> BaseLog.unregister e d) - (BaseLog.filter [ocamlbuild_clean_ev]) - - (** Determine real build directory *) - let build_dir extra_argv = - let rec search_args dir = - function - | "-build-dir" :: dir :: tl -> - search_args dir tl - | _ :: tl -> - search_args dir tl - | [] -> - dir - in - search_args "_build" (fix_args [] extra_argv) - -end - -module OCamlbuildPlugin = struct -(* # 21 "src/plugins/ocamlbuild/OCamlbuildPlugin.ml" *) - - (** Build using ocamlbuild - @author Sylvain Le Gall - *) - - open OASISTypes - open OASISGettext - open OASISUtils - open BaseEnv - open OCamlbuildCommon - open BaseStandardVar - open BaseMessage - - let cond_targets_hook = - ref (fun lst -> lst) - - let build pkg argv = - - (* Return the filename in build directory *) - let in_build_dir fn = - Filename.concat - (build_dir argv) - fn - in - - (* Return the unix filename in host build directory *) - let in_build_dir_of_unix fn = - in_build_dir (OASISHostPath.of_unix fn) - in - - let cond_targets = - List.fold_left - (fun acc -> - function - | Library (cs, bs, lib) when var_choose bs.bs_build -> - begin - let evs, unix_files = - BaseBuilt.of_library - in_build_dir_of_unix - (cs, bs, lib) - in - - let ends_with nd fn = - let nd_len = - String.length nd - in - (String.length fn >= nd_len) - && - (String.sub - fn - (String.length fn - nd_len) - nd_len) = nd - in - - let tgts = - List.flatten - (List.filter - (fun l -> l <> []) - (List.map - (List.filter - (fun fn -> - ends_with ".cma" fn - || ends_with ".cmxs" fn - || ends_with ".cmxa" fn - || ends_with (ext_lib ()) fn - || ends_with (ext_dll ()) fn)) - unix_files)) - in - - match tgts with - | _ :: _ -> - (evs, tgts) :: acc - | [] -> - failwithf - (f_ "No possible ocamlbuild targets for library %s") - cs.cs_name - end - - | Executable (cs, bs, exec) when var_choose bs.bs_build -> - begin - let evs, unix_exec_is, unix_dll_opt = - BaseBuilt.of_executable - in_build_dir_of_unix - (cs, bs, exec) - in - - let target ext = - let unix_tgt = - (OASISUnixPath.concat - bs.bs_path - (OASISUnixPath.chop_extension - exec.exec_main_is))^ext - in - let evs = - (* Fix evs, we want to use the unix_tgt, without copying *) - List.map - (function - | BaseBuilt.BExec, nm, lst when nm = cs.cs_name -> - BaseBuilt.BExec, nm, [[in_build_dir_of_unix unix_tgt]] - | ev -> - ev) - evs - in - evs, [unix_tgt] - in - - (* Add executable *) - let acc = - match bs.bs_compiled_object with - | Native -> - (target ".native") :: acc - | Best when bool_of_string (is_native ()) -> - (target ".native") :: acc - | Byte - | Best -> - (target ".byte") :: acc - in - acc - end - - | Library _ | Executable _ | Test _ - | SrcRepo _ | Flag _ | Doc _ -> - acc) - [] - (* Keep the pkg.sections ordered *) - (List.rev pkg.sections); - in - - (* Check and register built files *) - let check_and_register (bt, bnm, lst) = - List.iter - (fun fns -> - if not (List.exists OASISFileUtil.file_exists_case fns) then - failwithf - (f_ "No one of expected built files %s exists") - (String.concat (s_ ", ") (List.map (Printf.sprintf "'%s'") fns))) - lst; - (BaseBuilt.register bt bnm lst) - in - - let cond_targets = - (* Run the hook *) - !cond_targets_hook cond_targets - in - - (* Run a list of target... *) - run_ocamlbuild - (List.flatten - (List.map snd cond_targets)) - argv; - (* ... and register events *) - List.iter - check_and_register - (List.flatten (List.map fst cond_targets)) - - - let clean pkg extra_args = - run_clean extra_args; - List.iter - (function - | Library (cs, _, _) -> - BaseBuilt.unregister BaseBuilt.BLib cs.cs_name - | Executable (cs, _, _) -> - BaseBuilt.unregister BaseBuilt.BExec cs.cs_name; - BaseBuilt.unregister BaseBuilt.BExecLib cs.cs_name - | _ -> - ()) - pkg.sections - -end - -module OCamlbuildDocPlugin = struct -(* # 21 "src/plugins/ocamlbuild/OCamlbuildDocPlugin.ml" *) - - (* Create documentation using ocamlbuild .odocl files - @author Sylvain Le Gall - *) - - open OASISTypes - open OASISGettext - open OASISMessage - open OCamlbuildCommon - open BaseStandardVar - - - - let doc_build path pkg (cs, doc) argv = - let index_html = - OASISUnixPath.make - [ - path; - cs.cs_name^".docdir"; - "index.html"; - ] - in - let tgt_dir = - OASISHostPath.make - [ - build_dir argv; - OASISHostPath.of_unix path; - cs.cs_name^".docdir"; - ] - in - run_ocamlbuild [index_html] argv; - List.iter - (fun glb -> - BaseBuilt.register - BaseBuilt.BDoc - cs.cs_name - [OASISFileUtil.glob ~ctxt:!BaseContext.default - (Filename.concat tgt_dir glb)]) - ["*.html"; "*.css"] - - let doc_clean t pkg (cs, doc) argv = - run_clean argv; - BaseBuilt.unregister BaseBuilt.BDoc cs.cs_name - -end - - -# 5558 "setup.ml" -module CustomPlugin = struct -(* # 21 "src/plugins/custom/CustomPlugin.ml" *) - - (** Generate custom configure/build/doc/test/install system - @author - *) - - open BaseEnv - open OASISGettext - open OASISTypes - - - - type t = - { - cmd_main: command_line conditional; - cmd_clean: (command_line option) conditional; - cmd_distclean: (command_line option) conditional; - } - - let run = BaseCustom.run - - let main t _ extra_args = - let cmd, args = - var_choose - ~name:(s_ "main command") - t.cmd_main - in - run cmd args extra_args - - let clean t pkg extra_args = - match var_choose t.cmd_clean with - | Some (cmd, args) -> - run cmd args extra_args - | _ -> - () - - let distclean t pkg extra_args = - match var_choose t.cmd_distclean with - | Some (cmd, args) -> - run cmd args extra_args - | _ -> - () - - module Build = - struct - let main t pkg extra_args = - main t pkg extra_args; - List.iter - (fun sct -> - let evs = - match sct with - | Library (cs, bs, lib) when var_choose bs.bs_build -> - begin - let evs, _ = - BaseBuilt.of_library - OASISHostPath.of_unix - (cs, bs, lib) - in - evs - end - | Executable (cs, bs, exec) when var_choose bs.bs_build -> - begin - let evs, _, _ = - BaseBuilt.of_executable - OASISHostPath.of_unix - (cs, bs, exec) - in - evs - end - | _ -> - [] - in - List.iter - (fun (bt, bnm, lst) -> BaseBuilt.register bt bnm lst) - evs) - pkg.sections - - let clean t pkg extra_args = - clean t pkg extra_args; - (* TODO: this seems to be pretty generic (at least wrt to ocamlbuild - * considering moving this to BaseSetup? - *) - List.iter - (function - | Library (cs, _, _) -> - BaseBuilt.unregister BaseBuilt.BLib cs.cs_name - | Executable (cs, _, _) -> - BaseBuilt.unregister BaseBuilt.BExec cs.cs_name; - BaseBuilt.unregister BaseBuilt.BExecLib cs.cs_name - | _ -> - ()) - pkg.sections - - let distclean t pkg extra_args = - distclean t pkg extra_args - end - - module Test = - struct - let main t pkg (cs, test) extra_args = - try - main t pkg extra_args; - 0.0 - with Failure s -> - BaseMessage.warning - (f_ "Test '%s' fails: %s") - cs.cs_name - s; - 1.0 - - let clean t pkg (cs, test) extra_args = - clean t pkg extra_args - - let distclean t pkg (cs, test) extra_args = - distclean t pkg extra_args - end - - module Doc = - struct - let main t pkg (cs, _) extra_args = - main t pkg extra_args; - BaseBuilt.register BaseBuilt.BDoc cs.cs_name [] - - let clean t pkg (cs, _) extra_args = - clean t pkg extra_args; - BaseBuilt.unregister BaseBuilt.BDoc cs.cs_name - - let distclean t pkg (cs, _) extra_args = - distclean t pkg extra_args - end - -end - - -# 5694 "setup.ml" -open OASISTypes;; - -let setup_t = - { - BaseSetup.configure = InternalConfigurePlugin.configure; - build = OCamlbuildPlugin.build; - test = - [ - ("nonregression", - CustomPlugin.Test.main - { - CustomPlugin.cmd_main = - [(OASISExpr.EBool true, ("make", ["test-compile"]))]; - cmd_clean = [(OASISExpr.EBool true, None)]; - cmd_distclean = [(OASISExpr.EBool true, None)] - }) - ]; - doc = []; - install = InternalInstallPlugin.install; - uninstall = InternalInstallPlugin.uninstall; - clean = [OCamlbuildPlugin.clean]; - clean_test = - [ - ("nonregression", - CustomPlugin.Test.clean - { - CustomPlugin.cmd_main = - [(OASISExpr.EBool true, ("make", ["test-compile"]))]; - cmd_clean = [(OASISExpr.EBool true, None)]; - cmd_distclean = [(OASISExpr.EBool true, None)] - }) - ]; - clean_doc = []; - distclean = []; - distclean_test = - [ - ("nonregression", - CustomPlugin.Test.distclean - { - CustomPlugin.cmd_main = - [(OASISExpr.EBool true, ("make", ["test-compile"]))]; - cmd_clean = [(OASISExpr.EBool true, None)]; - cmd_distclean = [(OASISExpr.EBool true, None)] - }) - ]; - distclean_doc = []; - package = - { - oasis_version = "0.2"; - ocaml_version = None; - findlib_version = None; - name = "Lustre Compiler"; - version = "1.2"; - license = - OASISLicense.DEP5License - (OASISLicense.DEP5Unit - { - OASISLicense.license = "LGPL"; - excption = None; - version = OASISLicense.Version "2.1" - }); - license_file = None; - copyrights = []; - maintainers = []; - authors = []; - homepage = None; - synopsis = "Lustre compiler C and Java backends"; - description = None; - categories = []; - conf_type = (`Configure, "internal", Some "0.3"); - conf_custom = - { - pre_command = [(OASISExpr.EBool true, None)]; - post_command = [(OASISExpr.EBool true, None)] - }; - build_type = (`Build, "ocamlbuild", Some "0.3"); - build_custom = - { - pre_command = - [ - (OASISExpr.EBool true, - Some (("./svn_version.sh", ["$(prefix)"]))) - ]; - post_command = [(OASISExpr.EBool true, None)] - }; - install_type = (`Install, "internal", Some "0.3"); - install_custom = - { - pre_command = [(OASISExpr.EBool true, None)]; - post_command = - [ - (OASISExpr.EBool true, - Some - (("mkdir", - [ - "-p"; - "$(prefix)/include/lustrec;"; - "cp"; - "-rf"; - "include/*"; - "$(prefix)/include/lustrec" - ]))) - ] - }; - uninstall_custom = - { - pre_command = [(OASISExpr.EBool true, None)]; - post_command = [(OASISExpr.EBool true, None)] - }; - clean_custom = - { - pre_command = [(OASISExpr.EBool true, None)]; - post_command = [(OASISExpr.EBool true, None)] - }; - distclean_custom = - { - pre_command = [(OASISExpr.EBool true, None)]; - post_command = [(OASISExpr.EBool true, None)] - }; - files_ab = []; - sections = - [ - Executable - ({ - cs_name = "lustrec"; - cs_data = PropList.Data.create (); - cs_plugin_data = [] - }, - { - bs_build = [(OASISExpr.EBool true, true)]; - bs_install = [(OASISExpr.EBool true, true)]; - bs_path = "src"; - bs_compiled_object = Native; - bs_build_depends = - [ - FindlibPackage ("ocamlgraph", None); - FindlibPackage ("str", None); - FindlibPackage ("unix", None) - ]; - bs_build_tools = [ExternalTool "ocamlbuild"]; - bs_c_sources = []; - bs_data_files = []; - bs_ccopt = [(OASISExpr.EBool true, [])]; - bs_cclib = [(OASISExpr.EBool true, [])]; - bs_dlllib = [(OASISExpr.EBool true, [])]; - bs_dllpath = [(OASISExpr.EBool true, [])]; - bs_byteopt = [(OASISExpr.EBool true, [])]; - bs_nativeopt = [(OASISExpr.EBool true, [])] - }, - { - exec_custom = false; - exec_main_is = "main_lustre_compiler.ml" - }); - Test - ({ - cs_name = "nonregression"; - cs_data = PropList.Data.create (); - cs_plugin_data = [] - }, - { - test_type = (`Test, "custom", None); - test_command = - [(OASISExpr.EBool true, ("make", ["test-compile"]))]; - test_custom = - { - pre_command = [(OASISExpr.EBool true, None)]; - post_command = [(OASISExpr.EBool true, None)] - }; - test_working_directory = Some "test"; - test_run = [(OASISExpr.EBool true, true)]; - test_tools = [] - }) - ]; - plugins = [(`Extra, "DevFiles", Some "0.2")]; - schema_data = PropList.Data.create (); - plugin_data = [] - }; - oasis_fn = Some "_oasis"; - oasis_version = "0.3.0"; - oasis_digest = Some "wX\249B\007\151\134\1970p\217\138\017\214\244\241"; - oasis_exec = None; - oasis_setup_args = []; - setup_update = false - };; - -let setup () = BaseSetup.setup setup_t;; - -# 5883 "setup.ml" -(* OASIS_STOP *) -let () = setup ();; diff --git a/src/Makefile.in b/src/Makefile.in index 9d7e902c..126a31f7 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,4 +1,4 @@ -OCAMLBUILD=@OCAMLBUILD@ -classic-display -no-links +OCAMLBUILD=@OCAMLBUILD@ -classic-display -use-ocamlfind -no-links prefix=@prefix@ exec_prefix=@exec_prefix@ diff --git a/src/_tags b/src/_tags index bcfbd0e4..e3fd0e0e 100644 --- a/src/_tags +++ b/src/_tags @@ -1,10 +1,15 @@ "backends/C": include "backends/Horn": include +"plugins/salsa": include +"plugins/scopes": include <**/.svn>: -traverse <**/.svn>: not_hygienic "main_lustre_compiler.native": use_graph "main_lustre_compiler.native": use_str "main_lustre_compiler.native": use_unix -<*.ml{,i}>: use_graph -<*.ml{,i}>: use_str -<*.ml{,i}>: use_unix +"main_lustre_compiler.native": use_num, package(salsa) +#<*.ml{,i}>: package(salsa) +<**/*.cm?>: use_graph +#<*.ml{,i}>: use_str +#<*.ml{,i}>: use_unix +<**/*.cm?>: package(salsa) diff --git a/src/backends/C/c_backend.ml b/src/backends/C/c_backend.ml index a6d1d711..7938f974 100644 --- a/src/backends/C/c_backend.ml +++ b/src/backends/C/c_backend.ml @@ -26,19 +26,18 @@ let makefile_opt print basename dependencies makefile_fmt machines = *) let gen_files funs basename prog machines dependencies header_file source_lib_file source_main_file makefile_file machines = - let header_out = open_out header_file in let header_fmt = formatter_of_out_channel header_out in let source_lib_out = open_out source_lib_file in let source_lib_fmt = formatter_of_out_channel source_lib_out in - + let print_header, print_lib_c, print_main_c, print_makefile = funs in (* Generating H file *) print_header header_fmt basename prog machines dependencies; - + (* Generating Lib C file *) print_lib_c source_lib_fmt basename prog machines dependencies; - + close_out header_out; close_out source_lib_out; @@ -46,7 +45,11 @@ let gen_files funs basename prog machines dependencies header_file source_lib_fi | "" -> () (* No main node: we do not genenrate main nor makefile *) | main_node -> ( match Machine_code.get_machine_opt main_node machines with - | None -> Format.eprintf "Unable to find a main node named %s@.@?" main_node; assert false + | None -> begin + Global.main_node := main_node; + Format.eprintf "Code generation error: %a@." Corelang.pp_error LustreSpec.Main_not_found; + raise (Corelang.Error (Location.dummy_loc, LustreSpec.Main_not_found)) + end | Some m -> begin let source_main_out = open_out source_main_file in let source_main_fmt = formatter_of_out_channel source_main_out in @@ -65,7 +68,8 @@ let gen_files funs basename prog machines dependencies header_file source_lib_fi end ) -let translate_to_c header source_lib source_main makefile basename prog machines dependencies = +let translate_to_c header source_lib source_main makefile basename prog machines dependencies = + match !Options.spec with | "no" -> begin let module HeaderMod = C_backend_header.EmptyMod in @@ -77,7 +81,7 @@ let translate_to_c header source_lib source_main makefile basename prog machines let module Source = C_backend_src.Main (SourceMod) in let module SourceMain = C_backend_main.Main (SourceMainMod) in let module Makefile = C_backend_makefile.Main (MakefileMod) in - + let funs = Header.print_alloc_header, Source.print_lib_c, @@ -100,7 +104,7 @@ let translate_to_c header source_lib source_main makefile basename prog machines let module Source = C_backend_src.Main (SourceMod) in let module SourceMain = C_backend_main.Main (SourceMainMod) in let module Makefile = C_backend_makefile.Main (MakefileMod) in - + let funs = Header.print_alloc_header, Source.print_lib_c, diff --git a/src/backends/C/c_backend_common.ml b/src/backends/C/c_backend_common.ml index d7674274..a6d2bac8 100644 --- a/src/backends/C/c_backend_common.ml +++ b/src/backends/C/c_backend_common.ml @@ -90,6 +90,8 @@ let pp_machine_static_declare_name fmt id = fprintf fmt "%s_DECLARE" id let pp_machine_static_link_name fmt id = fprintf fmt "%s_LINK" id let pp_machine_static_alloc_name fmt id = fprintf fmt "%s_ALLOC" id let pp_machine_reset_name fmt id = fprintf fmt "%s_reset" id +let pp_machine_init_name fmt id = fprintf fmt "%s_init" id +let pp_machine_clear_name fmt id = fprintf fmt "%s_clear" id let pp_machine_step_name fmt id = fprintf fmt "%s_step" id let rec pp_c_dimension fmt dim = @@ -116,16 +118,17 @@ let is_basic_c_type t = let pp_basic_c_type fmt t = match (Types.repr t).Types.tdesc with - | Types.Tbool -> fprintf fmt "_Bool" - | Types.Treal -> fprintf fmt "double" - | Types.Tint -> fprintf fmt "int" + | Types.Tbool -> fprintf fmt "_Bool" + | Types.Treal when !Options.mpfr -> fprintf fmt "%s" Mpfr.mpfr_t + | Types.Treal -> fprintf fmt "double" + | Types.Tint -> fprintf fmt "int" | _ -> assert false (* Not a basic C type. Do not handle arrays or pointers *) let pp_c_type var fmt t = let rec aux t pp_suffix = match (Types.repr t).Types.tdesc with | Types.Tclock t' -> aux t' pp_suffix - | Types.Tbool | Types.Treal | Types.Tint + | Types.Tbool | Types.Tint | Types.Treal -> fprintf fmt "%a %s%a" pp_basic_c_type t var pp_suffix () | Types.Tarray (d, t') -> let pp_suffix' fmt () = fprintf fmt "%a[%a]" pp_suffix () pp_c_dimension d in @@ -135,30 +138,29 @@ let pp_c_type var fmt t = | Types.Tarrow (_, _) -> fprintf fmt "void (*%s)()" var | _ -> eprintf "internal error: C_backend_common.pp_c_type %a@." Types.print_ty t; assert false in aux t (fun fmt () -> ()) - +(* let rec pp_c_initialize fmt t = match (Types.repr t).Types.tdesc with | Types.Tint -> pp_print_string fmt "0" | Types.Tclock t' -> pp_c_initialize fmt t' | Types.Tbool -> pp_print_string fmt "0" - | Types.Treal -> pp_print_string fmt "0." + | Types.Treal when not !Options.mpfr -> pp_print_string fmt "0." | Types.Tarray (d, t') when Dimension.is_dimension_const d -> fprintf fmt "{%a}" (Utils.fprintf_list ~sep:"," (fun fmt _ -> pp_c_initialize fmt t')) (Utils.duplicate 0 (Dimension.size_const_dimension d)) | _ -> assert false - + *) let pp_c_tag fmt t = pp_print_string fmt (if t = tag_true then "1" else if t = tag_false then "0" else t) - (* Prints a constant value *) let rec pp_c_const fmt c = match c with | Const_int i -> pp_print_int fmt i - | Const_real r -> pp_print_string fmt r - | Const_float r -> pp_print_float fmt r + | Const_real (c,e,s)-> pp_print_string fmt s (* Format.fprintf fmt "%ie%i" c e*) + (* | Const_float r -> pp_print_float fmt r *) | Const_tag t -> pp_c_tag fmt t | Const_array ca -> fprintf fmt "{%a }" (Utils.fprintf_list ~sep:", " pp_c_const) ca | Const_struct fl -> fprintf fmt "{%a }" (Utils.fprintf_list ~sep:", " (fun fmt (f, c) -> pp_c_const fmt c)) fl @@ -169,17 +171,16 @@ let rec pp_c_const fmt c = but an offset suffix may be added for array variables *) let rec pp_c_val self pp_var fmt v = - (*Format.eprintf "C_backend_common.pp_c_val %a@." pp_val v;*) - match v with + match v.value_desc with | Cst c -> pp_c_const fmt c | Array vl -> fprintf fmt "{%a}" (Utils.fprintf_list ~sep:", " (pp_c_val self pp_var)) vl | Access (t, i) -> fprintf fmt "%a[%a]" (pp_c_val self pp_var) t (pp_c_val self pp_var) i - | Power (v, n) -> (Format.eprintf "internal error: C_backend_common.pp_c_val %a@." pp_val v; assert false) + | Power (v, n) -> assert false | LocalVar v -> pp_var fmt v | StateVar v -> (* array memory vars are represented by an indirection to a local var with the right type, in order to avoid casting everywhere. *) - if Types.is_array_type v.var_type + if Types.is_array_type v.var_type && not (Types.is_real_type v.var_type && !Options.mpfr) then fprintf fmt "%a" pp_var v else fprintf fmt "%s->_reg.%a" self pp_var v | Fun (n, vl) -> Basic_library.pp_c n (pp_c_val self pp_var) fmt vl @@ -191,9 +192,10 @@ let rec pp_c_val self pp_var fmt v = - moreover, dereference memory array variables. *) let pp_c_var_read m fmt id = + (* mpfr_t is a static array, not treated as general arrays *) if Types.is_address_type id.var_type then - if is_memory m id + if is_memory m id && not (Types.is_real_type id.var_type && !Options.mpfr) then fprintf fmt "(*%s)" id.var_id else fprintf fmt "%s" id.var_id else @@ -289,9 +291,9 @@ let pp_c_checks self fmt m = let pp_registers_struct fmt m = if m.mmemory <> [] then - fprintf fmt "@[%a {@[%a; @]}@] _reg; " + fprintf fmt "@[%a {@[<v>%a;@ @]}@] _reg; " pp_machine_regtype_name m.mname.node_id - (Utils.fprintf_list ~sep:"; " pp_c_decl_struct_var) m.mmemory + (Utils.fprintf_list ~sep:";@ " pp_c_decl_struct_var) m.mmemory else () @@ -302,11 +304,12 @@ let print_machine_struct fmt m = else begin (* Define struct *) - fprintf fmt "@[%a {@[%a%a%t@]};@]@." + fprintf fmt "@[%a {@[<v>%a%t%a%t@]};@]@." pp_machine_memtype_name m.mname.node_id pp_registers_struct m - (Utils.fprintf_list ~sep:"; " pp_c_decl_instance_var) m.minstances - (Utils.pp_final_char_if_non_empty "; " m.minstances) + (Utils.pp_final_char_if_non_empty "@ " m.mmemory) + (Utils.fprintf_list ~sep:";@ " pp_c_decl_instance_var) m.minstances + (Utils.pp_final_char_if_non_empty ";@ " m.minstances) end let print_machine_struct_from_header fmt inode = @@ -338,6 +341,22 @@ let print_reset_prototype self fmt (name, static) = pp_machine_memtype_name name self +let print_init_prototype self fmt (name, static) = + fprintf fmt "void %a (@[<v>%a%t%a *%s@])" + pp_machine_init_name name + (Utils.fprintf_list ~sep:",@ " pp_c_decl_input_var) static + (Utils.pp_final_char_if_non_empty ",@," static) + pp_machine_memtype_name name + self + +let print_clear_prototype self fmt (name, static) = + fprintf fmt "void %a (@[<v>%a%t%a *%s@])" + pp_machine_clear_name name + (Utils.fprintf_list ~sep:",@ " pp_c_decl_input_var) static + (Utils.pp_final_char_if_non_empty ",@," static) + pp_machine_memtype_name name + self + let print_stateless_prototype fmt (name, inputs, outputs) = fprintf fmt "void %a (@[<v>@[%a%t@]@,@[%a@]@,@])" pp_machine_step_name name @@ -383,6 +402,156 @@ let print_extern_alloc_prototypes fmt (Dep (_,_, header,_)) = | _ -> () ) header + +let pp_c_main_var_input fmt id = + fprintf fmt "%s" id.var_id + +let pp_c_main_var_output fmt id = + if Types.is_address_type id.var_type + then + fprintf fmt "%s" id.var_id + else + fprintf fmt "&%s" id.var_id + +let pp_main_call mname self fmt m (inputs: value_t list) (outputs: var_decl list) = + if m.mmemory = [] + then + fprintf fmt "%a (%a%t%a);" + pp_machine_step_name mname + (Utils.fprintf_list ~sep:", " (pp_c_val self pp_c_main_var_input)) inputs + (Utils.pp_final_char_if_non_empty ", " inputs) + (Utils.fprintf_list ~sep:", " pp_c_main_var_output) outputs + else + fprintf fmt "%a (%a%t%a%t%s);" + pp_machine_step_name mname + (Utils.fprintf_list ~sep:", " (pp_c_val self pp_c_main_var_input)) inputs + (Utils.pp_final_char_if_non_empty ", " inputs) + (Utils.fprintf_list ~sep:", " pp_c_main_var_output) outputs + (Utils.pp_final_char_if_non_empty ", " outputs) + self + +let pp_c_var m self pp_var fmt var = + if is_memory m var + then + pp_c_val self pp_var fmt (mk_val (StateVar var) var.var_type) + else + pp_c_val self pp_var fmt (mk_val (LocalVar var) var.var_type) + +let pp_array_suffix fmt loop_vars = + Utils.fprintf_list ~sep:"" (fun fmt v -> fprintf fmt "[%s]" v) fmt loop_vars + +(* type directed initialization: useless wrt the lustre compilation model, + except for MPFR injection, where values are dynamically allocated +*) +let pp_initialize m self pp_var fmt var = + let rec aux indices fmt typ = + if Types.is_array_type typ + then + let dim = Types.array_type_dimension typ in + let idx = mk_loop_var m () in + fprintf fmt "@[<v 2>{@,int %s;@,for(%s=0;%s<%a;%s++)@,%a @]@,}" + idx idx idx pp_c_dimension dim idx + (aux (idx::indices)) (Types.array_element_type typ) + else + let pp_var_suffix fmt var = + fprintf fmt "%a%a" (pp_c_var m self pp_var) var pp_array_suffix indices in + Mpfr.pp_inject_init pp_var_suffix fmt var + in + if Types.is_real_type (Types.array_base_type var.var_type) && !Options.mpfr + then + begin + reset_loop_counter (); + aux [] fmt var.var_type + end + +(* type directed clear: useless wrt the lustre compilation model, + except for MPFR injection, where values are dynamically allocated +*) +let pp_clear m self pp_var fmt var = + let rec aux indices fmt typ = + if Types.is_array_type typ + then + let dim = Types.array_type_dimension typ in + let idx = mk_loop_var m () in + fprintf fmt "@[<v 2>{@,int %s;@,for(%s=0;%s<%a;%s++)@,%a @]@,}" + idx idx idx pp_c_dimension dim idx + (aux (idx::indices)) (Types.array_element_type typ) + else + let pp_var_suffix fmt var = + fprintf fmt "%a%a" (pp_c_var m self pp_var) var pp_array_suffix indices in + Mpfr.pp_inject_clear pp_var_suffix fmt var + in + if Types.is_real_type (Types.array_base_type var.var_type) && !Options.mpfr + then + begin + reset_loop_counter (); + aux [] fmt var.var_type + end + +let pp_call m self pp_read pp_write fmt i (inputs: value_t list) (outputs: var_decl list) = + try (* stateful node instance *) + let (n,_) = List.assoc i m.minstances in + fprintf fmt "%a (%a%t%a%t%s->%s);" + pp_machine_step_name (node_name n) + (Utils.fprintf_list ~sep:", " (pp_c_val self pp_read)) inputs + (Utils.pp_final_char_if_non_empty ", " inputs) + (Utils.fprintf_list ~sep:", " pp_write) outputs + (Utils.pp_final_char_if_non_empty ", " outputs) + self + i + with Not_found -> (* stateless node instance *) + let (n,_) = List.assoc i m.mcalls in + fprintf fmt "%a (%a%t%a);" + pp_machine_step_name (node_name n) + (Utils.fprintf_list ~sep:", " (pp_c_val self pp_read)) inputs + (Utils.pp_final_char_if_non_empty ", " inputs) + (Utils.fprintf_list ~sep:", " pp_write) outputs + +let pp_basic_instance_call m self fmt i (inputs: value_t list) (outputs: var_decl list) = + pp_call m self (pp_c_var_read m) (pp_c_var_write m) fmt i inputs outputs +(* + try (* stateful node instance *) + let (n,_) = List.assoc i m.minstances in + fprintf fmt "%a (%a%t%a%t%s->%s);" + pp_machine_step_name (node_name n) + (Utils.fprintf_list ~sep:", " (pp_c_val self (pp_c_var_read m))) inputs + (Utils.pp_final_char_if_non_empty ", " inputs) + (Utils.fprintf_list ~sep:", " (pp_c_var_write m)) outputs + (Utils.pp_final_char_if_non_empty ", " outputs) + self + i + with Not_found -> (* stateless node instance *) + let (n,_) = List.assoc i m.mcalls in + fprintf fmt "%a (%a%t%a);" + pp_machine_step_name (node_name n) + (Utils.fprintf_list ~sep:", " (pp_c_val self (pp_c_var_read m))) inputs + (Utils.pp_final_char_if_non_empty ", " inputs) + (Utils.fprintf_list ~sep:", " (pp_c_var_write m)) outputs +*) + +let pp_instance_call m self fmt i (inputs: value_t list) (outputs: var_decl list) = + let pp_offset pp_var indices fmt var = + match indices with + | [] -> fprintf fmt "%a" pp_var var + | _ -> fprintf fmt "%a[%a]" pp_var var (Utils.fprintf_list ~sep:"][" pp_print_string) indices in + let rec aux indices fmt typ = + if Types.is_array_type typ + then + let dim = Types.array_type_dimension typ in + let idx = mk_loop_var m () in + fprintf fmt "@[<v 2>{@,int %s;@,for(%s=0;%s<%a;%s++)@,%a @]@,}" + idx idx idx pp_c_dimension dim idx + (aux (idx::indices)) (Types.array_element_type typ) + else + let pp_read = pp_offset (pp_c_var_read m) indices in + let pp_write = pp_offset (pp_c_var_write m) indices in + pp_call m self pp_read pp_write fmt i inputs outputs + in + begin + reset_loop_counter (); + aux [] fmt (List.hd inputs).value_type + end + (* Local Variables: *) (* compile-command:"make -C ../../.." *) (* End: *) diff --git a/src/backends/C/c_backend_header.ml b/src/backends/C/c_backend_header.ml index ffe95755..0a11a467 100644 --- a/src/backends/C/c_backend_header.ml +++ b/src/backends/C/c_backend_header.ml @@ -34,10 +34,16 @@ module Main = functor (Mod: MODIFIERS_HDR) -> struct let print_import_standard fmt = - fprintf fmt "#include \"%s/arrow.h\"@.@." Version.include_path + begin + if !Options.mpfr then + begin + fprintf fmt "#include <mpfr.h>@." + end; + fprintf fmt "#include \"%s/arrow.h\"@.@." Version.include_path + end let rec print_static_val pp_var fmt v = - match v with + match v.value_desc with | Cst c -> pp_c_const fmt c | LocalVar v -> pp_var fmt v | Fun (n, vl) -> Basic_library.pp_c n (print_static_val pp_var) fmt vl @@ -144,6 +150,7 @@ let print_static_alloc_macro fmt (m, attr, inst) = pp_machine_static_link_name m.mname.node_id inst + let print_machine_decl fmt m = Mod.print_machine_decl_prefix fmt m; if fst (get_stateless_status m) then @@ -175,6 +182,12 @@ let print_machine_decl fmt m = fprintf fmt "extern %a;@.@." (print_reset_prototype self) (m.mname.node_id, m.mstatic); + fprintf fmt "extern %a;@.@." + (print_init_prototype self) (m.mname.node_id, m.mstatic); + + fprintf fmt "extern %a;@.@." + (print_clear_prototype self) (m.mname.node_id, m.mstatic); + fprintf fmt "extern %a;@.@." (print_step_prototype self) (m.mname.node_id, m.mstep.step_inputs, m.mstep.step_outputs) @@ -233,7 +246,13 @@ let print_machine_decl_from_header fmt inode = let self = mk_new_name used "self" in fprintf fmt "extern %a;@.@." (print_reset_prototype self) (inode.nodei_id, static_inputs); - + + fprintf fmt "extern %a;@.@." + (print_init_prototype self) (inode.nodei_id, static_inputs); + + fprintf fmt "extern %a;@.@." + (print_clear_prototype self) (inode.nodei_id, static_inputs); + fprintf fmt "extern %a;@.@." (print_step_prototype self) (inode.nodei_id, inode.nodei_inputs, inode.nodei_outputs) @@ -249,8 +268,10 @@ and pp_c_type_decl filename cpt var fmt tdecl = match tdecl with | Tydec_any -> assert false | Tydec_int -> fprintf fmt "int %s" var + | Tydec_real when !Options.mpfr + -> fprintf fmt "%s %s" Mpfr.mpfr_t var | Tydec_real -> fprintf fmt "double %s" var - | Tydec_float -> fprintf fmt "float %s" var + (* | Tydec_float -> fprintf fmt "float %s" var *) | Tydec_bool -> fprintf fmt "_Bool %s" var | Tydec_clock ty -> pp_c_type_decl filename cpt var fmt ty | Tydec_const c -> fprintf fmt "%s %s" c var diff --git a/src/backends/C/c_backend_main.ml b/src/backends/C/c_backend_main.ml index 578c6a3e..f10bc45d 100644 --- a/src/backends/C/c_backend_main.ml +++ b/src/backends/C/c_backend_main.ml @@ -30,80 +30,122 @@ struct (* Main related functions *) (********************************************************************************************) -let print_get_input fmt v = - match (Types.repr v.var_type).Types.tdesc with - | Types.Tint -> fprintf fmt "_get_int(\"%s\")" v.var_id - | Types.Tbool -> fprintf fmt "_get_bool(\"%s\")" v.var_id - | Types.Treal -> fprintf fmt "_get_double(\"%s\")" v.var_id - | _ -> assert false +let print_get_inputs fmt m = + let pi fmt (v', v) = + match v.var_type.Types.tdesc with + | Types.Tint -> fprintf fmt "%s = _get_int(\"%s\")" v.var_id v'.var_id + | Types.Tbool -> fprintf fmt "%s = _get_bool(\"%s\")" v.var_id v'.var_id + | Types.Treal when !Options.mpfr -> fprintf fmt "mpfr_set_d(%s, _get_double(\"%s\"), %i)" v.var_id v'.var_id (Mpfr.mpfr_prec ()) + | Types.Treal -> fprintf fmt "%s = _get_double(\"%s\")" v.var_id v'.var_id + | _ -> + begin + Global.main_node := !Options.main_node; + Format.eprintf "Code generation error: %a%a@." + pp_error Main_wrong_kind + Location.pp_loc v'.var_loc; + raise (Error (v'.var_loc, Main_wrong_kind)) + end + in + List.iter2 (fun v' v -> fprintf fmt "@ %a;" pi (v', v)) m.mname.node_inputs m.mstep.step_inputs -let print_put_outputs fmt ol = - let po fmt o = - match (Types.repr o.var_type).Types.tdesc with - | Types.Tint -> fprintf fmt "_put_int(\"%s\", %s)" o.var_id o.var_id - | Types.Tbool -> fprintf fmt "_put_bool(\"%s\", %s)" o.var_id o.var_id - | Types.Treal -> fprintf fmt "_put_double(\"%s\", %s)" o.var_id o.var_id +let print_put_outputs fmt m = + let po fmt (o', o) = + match o.var_type.Types.tdesc with + | Types.Tint -> fprintf fmt "_put_int(\"%s\", %s)" o'.var_id o.var_id + | Types.Tbool -> fprintf fmt "_put_bool(\"%s\", %s)" o'.var_id o.var_id + | Types.Treal when !Options.mpfr -> fprintf fmt "_put_double(\"%s\", mpfr_get_d(%s, %s))" o'.var_id o.var_id (Mpfr.mpfr_rnd ()) + | Types.Treal -> fprintf fmt "_put_double(\"%s\", %s)" o'.var_id o.var_id | _ -> assert false in - List.iter (fprintf fmt "@ %a;" po) ol + List.iter2 (fun v' v -> fprintf fmt "@ %a;" po (v', v)) m.mname.node_outputs m.mstep.step_outputs + +let print_main_inout_declaration fmt m = + begin + fprintf fmt "/* Declaration of inputs/outputs variables */@ "; + List.iter + (fun v -> fprintf fmt "%a;@ " (pp_c_type v.var_id) v.var_type + ) m.mstep.step_inputs; + List.iter + (fun v -> fprintf fmt "%a;@ " (pp_c_type v.var_id) v.var_type + ) m.mstep.step_outputs + end + +let print_main_memory_allocation mname main_mem fmt m = + if m.mmemory <> [] then + begin + fprintf fmt "@ /* Main memory allocation */@ "; + if (!Options.static_mem && !Options.main_node <> "") + then (fprintf fmt "%a(static,main_mem);@ " pp_machine_static_alloc_name mname) + else (fprintf fmt "%a *main_mem = %a();@ " pp_machine_memtype_name mname pp_machine_alloc_name mname); + fprintf fmt "@ /* Initialize the main memory */@ "; + fprintf fmt "%a(%s);@ " pp_machine_reset_name mname main_mem; + end + +let print_main_initialize mname main_mem fmt m = + if m.mmemory <> [] + then + fprintf fmt "@ /* Initialize inputs, outputs and memories */@ %a%t%a%t%a(%s);@ " + (Utils.fprintf_list ~sep:"@ " (pp_initialize m main_mem (pp_c_var_read m))) m.mstep.step_inputs + (Utils.pp_newline_if_non_empty m.mstep.step_inputs) + (Utils.fprintf_list ~sep:"@ " (pp_initialize m main_mem (pp_c_var_read m))) m.mstep.step_outputs + (Utils.pp_newline_if_non_empty m.mstep.step_inputs) + pp_machine_init_name mname + main_mem + else + fprintf fmt "@ /* Initialize inputs and outputs */@ %a%t%a@ " + (Utils.fprintf_list ~sep:"@ " (pp_initialize m main_mem (pp_c_var_read m))) m.mstep.step_inputs + (Utils.pp_newline_if_non_empty m.mstep.step_inputs) + (Utils.fprintf_list ~sep:"@ " (pp_initialize m main_mem (pp_c_var_read m))) m.mstep.step_outputs + +let print_main_clear mname main_mem fmt m = + if m.mmemory <> [] + then + fprintf fmt "@ /* Clear inputs, outputs and memories */@ %a%t%a%t%a(%s);@ " + (Utils.fprintf_list ~sep:"@ " (pp_clear m main_mem (pp_c_var_read m))) m.mstep.step_inputs + (Utils.pp_newline_if_non_empty m.mstep.step_inputs) + (Utils.fprintf_list ~sep:"@ " (pp_clear m main_mem (pp_c_var_read m))) m.mstep.step_outputs + (Utils.pp_newline_if_non_empty m.mstep.step_inputs) + pp_machine_clear_name mname + main_mem + else + fprintf fmt "@ /* Clear inputs and outputs */@ %a%t%a@ " + (Utils.fprintf_list ~sep:"@ " (pp_clear m main_mem (pp_c_var_read m))) m.mstep.step_inputs + (Utils.pp_newline_if_non_empty m.mstep.step_inputs) + (Utils.fprintf_list ~sep:"@ " (pp_clear m main_mem (pp_c_var_read m))) m.mstep.step_outputs + +let print_main_loop mname main_mem fmt m = + let input_values = + List.map (fun v -> mk_val (LocalVar v) v.var_type) + m.mstep.step_inputs in + begin + fprintf fmt "@ ISATTY = isatty(0);@ "; + fprintf fmt "@ /* Infinite loop */@ "; + fprintf fmt "@[<v 2>while(1){@ "; + fprintf fmt "fflush(stdout);@ "; + fprintf fmt "%a@ %t%a" + print_get_inputs m + (fun fmt -> pp_main_call mname main_mem fmt m input_values m.mstep.step_outputs) + print_put_outputs m + end -let print_main_fun machines m fmt = +let print_main_code fmt m = let mname = m.mname.node_id in let main_mem = if (!Options.static_mem && !Options.main_node <> "") then "&main_mem" else "main_mem" in fprintf fmt "@[<v 2>int main (int argc, char *argv[]) {@ "; - fprintf fmt "/* Declaration of inputs/outputs variables */@ "; - List.iter - (fun v -> fprintf fmt "%a = %a;@ " (pp_c_type v.var_id) v.var_type pp_c_initialize v.var_type - ) m.mstep.step_inputs; - List.iter - (fun v -> fprintf fmt "%a = %a;@ " (pp_c_type v.var_id) v.var_type pp_c_initialize v.var_type - ) m.mstep.step_outputs; - fprintf fmt "@ /* Main memory allocation */@ "; - if (!Options.static_mem && !Options.main_node <> "") - then (fprintf fmt "%a(static,main_mem);@ " pp_machine_static_alloc_name mname) - else (fprintf fmt "%a *main_mem = %a();@ " pp_machine_memtype_name mname pp_machine_alloc_name mname); - fprintf fmt "@ /* Initialize the main memory */@ "; - fprintf fmt "%a(%s);@ " pp_machine_reset_name mname main_mem; - fprintf fmt "@ ISATTY = isatty(0);@ "; - fprintf fmt "@ /* Infinite loop */@ "; - fprintf fmt "@[<v 2>while(1){@ "; - fprintf fmt "fflush(stdout);@ "; - List.iter - (fun v -> fprintf fmt "%s = %a;@ " - v.var_id - print_get_input v - ) m.mstep.step_inputs; - (match m.mstep.step_outputs with - (* | [] -> ( *) - (* fprintf fmt "%a(%a%t%s);@ " *) - (* pp_machine_step_name mname *) - (* (Utils.fprintf_list ~sep:", " (fun fmt v -> pp_print_string fmt v.var_id)) m.mstep.step_inputs *) - (* (pp_final_char_if_non_empty ", " m.mstep.step_inputs) *) - (* main_mem *) - (* ) *) - (* | [o] -> ( *) - (* fprintf fmt "%s = %a(%a%t%a, %s);%a" *) - (* o.var_id *) - (* pp_machine_step_name mname *) - (* (Utils.fprintf_list ~sep:", " (fun fmt v -> pp_print_string fmt v.var_id)) m.mstep.step_inputs *) - (* (pp_final_char_if_non_empty ", " m.mstep.step_inputs) *) - (* (Utils.fprintf_list ~sep:", " (fun fmt v -> fprintf fmt "&%s" v.var_id)) m.mstep.step_outputs *) - (* main_mem *) - (* print_put_outputs [o]) *) - | _ -> ( - fprintf fmt "%a(%a%t%a, %s);%a" - pp_machine_step_name mname - (Utils.fprintf_list ~sep:", " (fun fmt v -> pp_print_string fmt v.var_id)) m.mstep.step_inputs - (Utils.pp_final_char_if_non_empty ", " m.mstep.step_inputs) - (Utils.fprintf_list ~sep:", " (fun fmt v -> fprintf fmt "&%s" v.var_id)) m.mstep.step_outputs - main_mem - print_put_outputs m.mstep.step_outputs) - ); - fprintf fmt "@]@ }@ "; - fprintf fmt "return 1;"; + print_main_inout_declaration fmt m; + print_main_memory_allocation mname main_mem fmt m; + print_main_initialize mname main_mem fmt m; + print_main_loop mname main_mem fmt m; + if Scopes.Plugin.is_active () then + begin + fprintf fmt "@ %t" Scopes.Plugin.pp + end; + fprintf fmt "@]@ }@ @ "; + print_main_clear mname main_mem fmt m; + fprintf fmt "@ return 1;"; fprintf fmt "@]@ }@." let print_main_header fmt = @@ -118,7 +160,7 @@ let print_main_c main_fmt main_machine basename prog machines _ (*dependencies*) (* Print the svn version number and the supported C standard (C90 or C99) *) print_version main_fmt; - print_main_fun machines main_machine main_fmt + print_main_code main_fmt main_machine end (* Local Variables: *) diff --git a/src/backends/C/c_backend_makefile.ml b/src/backends/C/c_backend_makefile.ml index 70371b40..99c10b05 100644 --- a/src/backends/C/c_backend_makefile.ml +++ b/src/backends/C/c_backend_makefile.ml @@ -18,7 +18,7 @@ let header_has_code header = (fun top -> match top.top_decl_desc with | Const _ -> true - | ImportedNode nd -> nd.nodei_in_lib = None + | ImportedNode nd -> nd.nodei_in_lib = [] | _ -> false ) header @@ -26,9 +26,7 @@ let header_has_code header = let header_libs header = List.fold_left (fun accu top -> match top.top_decl_desc with - | ImportedNode nd -> (match nd.nodei_in_lib with - | None -> accu - | Some lib -> Utils.list_union [lib] accu) + | ImportedNode nd -> Utils.list_union nd.nodei_in_lib accu | _ -> accu ) [] header @@ -87,6 +85,8 @@ let print_makefile basename nodename (dependencies: dep_t list) fmt = fprintf fmt "clean:@."; fprintf fmt "\t\\rm -f *.o %s_%s@." basename nodename; fprintf fmt "@."; + fprintf fmt ".PHONY: %s_%s@." basename nodename; + fprintf fmt "@."; Mod.other_targets fmt basename nodename dependencies; fprintf fmt "@."; diff --git a/src/backends/C/c_backend_src.ml b/src/backends/C/c_backend_src.ml index c8c06713..d1b46796 100644 --- a/src/backends/C/c_backend_src.ml +++ b/src/backends/C/c_backend_src.ml @@ -30,42 +30,50 @@ struct (* Instruction Printing functions *) (********************************************************************************************) - (* Computes the depth to which multi-dimension array assignments should be expanded. It equals the maximum number of nested static array constructions accessible from root [v]. *) -let rec expansion_depth v = - match v with - | Cst (Const_array cl) -> 1 + List.fold_right (fun c -> max (expansion_depth (Cst c))) cl 0 - | Cst _ - | LocalVar _ - | StateVar _ -> 0 - | Fun (_, vl) -> List.fold_right (fun v -> max (expansion_depth v)) vl 0 - | Array vl -> 1 + List.fold_right (fun v -> max (expansion_depth v)) vl 0 - | Access (v, i) -> max 0 (expansion_depth v - 1) - | Power (v, n) -> 0 (*1 + expansion_depth v*) - -let rec merge_static_loop_profiles lp1 lp2 = - match lp1, lp2 with - | [] , _ -> lp2 - | _ , [] -> lp1 - | p1 :: q1, p2 :: q2 -> (p1 || p2) :: merge_static_loop_profiles q1 q2 + let rec expansion_depth v = + match v.value_desc with + | Cst cst -> expansion_depth_cst cst + | LocalVar _ + | StateVar _ -> 0 + | Fun (_, vl) -> List.fold_right (fun v -> max (expansion_depth v)) vl 0 + | Array vl -> 1 + List.fold_right (fun v -> max (expansion_depth v)) vl 0 + | Access (v, i) -> max 0 (expansion_depth v - 1) + | Power (v, n) -> 0 (*1 + expansion_depth v*) + and expansion_depth_cst c = + match c with + Const_array cl -> 1 + List.fold_right (fun c -> max (expansion_depth_cst c)) cl 0 + | _ -> 0 + + let rec merge_static_loop_profiles lp1 lp2 = + match lp1, lp2 with + | [] , _ -> lp2 + | _ , [] -> lp1 + | p1 :: q1, p2 :: q2 -> (p1 || p2) :: merge_static_loop_profiles q1 q2 (* Returns a list of bool values, indicating whether the indices must be static or not *) -let rec static_loop_profile v = - match v with - | Cst (Const_array cl) -> - List.fold_right (fun c lp -> merge_static_loop_profiles lp (static_loop_profile (Cst c))) cl [] - | Cst _ - | LocalVar _ - | StateVar _ -> [] - | Fun (_, vl) -> List.fold_right (fun v lp -> merge_static_loop_profiles lp (static_loop_profile v)) vl [] - | Array vl -> true :: List.fold_right (fun v lp -> merge_static_loop_profiles lp (static_loop_profile v)) vl [] - | Access (v, i) -> (match (static_loop_profile v) with [] -> [] | _ :: q -> q) - | Power (v, n) -> false :: static_loop_profile v - + let rec static_loop_profile v = + match v.value_desc with + | Cst cst -> static_loop_profile_cst cst + | LocalVar _ + | StateVar _ -> [] + | Fun (_, vl) -> List.fold_right (fun v lp -> merge_static_loop_profiles lp (static_loop_profile v)) vl [] + | Array vl -> true :: List.fold_right (fun v lp -> merge_static_loop_profiles lp (static_loop_profile v)) vl [] + | Access (v, i) -> (match (static_loop_profile v) with [] -> [] | _ :: q -> q) + | Power (v, n) -> false :: static_loop_profile v + and static_loop_profile_cst cst = + match cst with + Const_array cl -> List.fold_right + (fun c lp -> merge_static_loop_profiles lp (static_loop_profile_cst c)) + cl + [] + | _ -> [] + + let rec is_const_index v = - match v with + match v.value_desc with | Cst (Const_int _) -> true | Fun (_, vl) -> List.for_all is_const_index vl | _ -> false @@ -114,65 +122,29 @@ let pp_loop_var fmt lv = let pp_suffix fmt loop_vars = Utils.fprintf_list ~sep:"" pp_loop_var fmt loop_vars -(* Prints a value expression [v], with internal function calls only. - [pp_var] is a printer for variables (typically [pp_c_var_read]), - but an offset suffix may be added for array variables -*) -(* Prints a constant value before a suffix (needs casting) *) -let rec pp_c_const_suffix var_type fmt c = - match c with - | Const_int i -> pp_print_int fmt i - | Const_real r -> pp_print_string fmt r - | Const_float r -> pp_print_float fmt r - | Const_tag t -> pp_c_tag fmt t - | Const_array ca -> let var_type = Types.array_element_type var_type in - fprintf fmt "(%a[]){%a }" (pp_c_type "") var_type (Utils.fprintf_list ~sep:", " (pp_c_const_suffix var_type)) ca - | Const_struct fl -> fprintf fmt "{%a }" (Utils.fprintf_list ~sep:", " (fun fmt (f, c) -> (pp_c_const_suffix (Types.struct_field_type var_type f)) fmt c)) fl - | Const_string _ -> assert false (* string occurs in annotations not in C *) - - -(* Prints a [value] of type [var_type] indexed by the suffix list [loop_vars] *) -let rec pp_value_suffix self var_type loop_vars pp_value fmt value = -(*Format.eprintf "pp_value_suffix: %a %a %a@." Types.print_ty var_type Machine_code.pp_val value pp_suffix loop_vars;*) - match loop_vars, value with - | (x, LAcc i) :: q, _ when is_const_index i -> - let r = ref (Dimension.size_const_dimension (Machine_code.dimension_of_value i)) in - pp_value_suffix self var_type ((x, LInt r)::q) pp_value fmt value - | (_, LInt r) :: q, Cst (Const_array cl) -> - let var_type = Types.array_element_type var_type in - pp_value_suffix self var_type q pp_value fmt (Cst (List.nth cl !r)) +(* Prints a [value] indexed by the suffix list [loop_vars] *) +let rec pp_value_suffix self loop_vars pp_value fmt value = + match loop_vars, value.value_desc with | (_, LInt r) :: q, Array vl -> - let var_type = Types.array_element_type var_type in - pp_value_suffix self var_type q pp_value fmt (List.nth vl !r) - | loop_var :: q, Array vl -> - let var_type = Types.array_element_type var_type in - Format.fprintf fmt "(%a[]){%a }%a" (pp_c_type "") var_type (Utils.fprintf_list ~sep:", " (pp_value_suffix self var_type q pp_value)) vl pp_suffix [loop_var] - | [] , Array vl -> - let var_type = Types.array_element_type var_type in - Format.fprintf fmt "(%a[]){%a }" (pp_c_type "") var_type (Utils.fprintf_list ~sep:", " (pp_value_suffix self var_type [] pp_value)) vl + pp_value_suffix self q pp_value fmt (List.nth vl !r) | _ :: q, Power (v, n) -> - pp_value_suffix self var_type q pp_value fmt v + pp_value_suffix self q pp_value fmt v | _ , Fun (n, vl) -> - Basic_library.pp_c n (pp_value_suffix self var_type loop_vars pp_value) fmt vl + Basic_library.pp_c n (pp_value_suffix self loop_vars pp_value) fmt vl | _ , Access (v, i) -> - let var_type = Type_predef.type_array (Dimension.mkdim_var ()) var_type in - pp_value_suffix self var_type ((Dimension.mkdim_var (), LAcc i) :: loop_vars) pp_value fmt v - | _ , LocalVar v -> Format.fprintf fmt "%a%a" pp_value v pp_suffix loop_vars - | _ , StateVar v -> - (* array memory vars are represented by an indirection to a local var with the right type, - in order to avoid casting everywhere. *) - if Types.is_array_type v.var_type - then Format.fprintf fmt "%a%a" pp_value v pp_suffix loop_vars - else Format.fprintf fmt "%s->_reg.%a%a" self pp_value v pp_suffix loop_vars - | _ , Cst cst -> pp_c_const_suffix var_type fmt cst - | _ , _ -> (Format.eprintf "internal error: C_backend_src.pp_value_suffix %a %a %a@." Types.print_ty var_type Machine_code.pp_val value pp_suffix loop_vars; assert false) - -(* Subsumes C_backend_common.pp_c_val to cope with aggressive substitution - which may yield constant arrays in expressions. - Type is needed to correctly print constant arrays. - *) -let pp_c_val self pp_var fmt (t, v) = - pp_value_suffix self t [] pp_var fmt v + pp_value_suffix self ((Dimension.mkdim_var (), LAcc i) :: loop_vars) pp_value fmt v + | _ , _ -> + let pp_var_suffix fmt v = fprintf fmt "%a%a" pp_value v pp_suffix loop_vars in + pp_c_val self pp_var_suffix fmt value + +let pp_basic_assign pp_var fmt typ var_name value = + if Types.is_real_type typ && !Options.mpfr + then + Mpfr.pp_inject_assign pp_var fmt var_name value + else + fprintf fmt "%a = %a;" + pp_var var_name + pp_var value (* type_directed assignment: array vs. statically sized type - [var_type]: type of variable to be assigned @@ -180,49 +152,68 @@ let pp_c_val self pp_var fmt (t, v) = - [value]: assigned value - [pp_var]: printer for variables *) -(* -let pp_assign_rec pp_var var_type var_name value = - match (Types.repr var_type).Types.tdesc, value with - | Types.Tarray (d, ty'), Array vl -> - let szl = Utils.enumerate (Dimension.size_const_dimension d) in - fprintf fmt "@[<v 2>{@,%a@]@,}" - (Utils.fprintf_list ~sep:"@," (fun fmt i -> r := i; aux fmt q)) szl - | Types.Tarray (d, ty'), Power (v, _) -> - | Types.Tarray (d, ty'), _ -> - | _ , _ -> - fprintf fmt "%a = %a;" - pp_var var_name - (pp_value_suffix self loop_vars pp_var) value -*) let pp_assign m self pp_var fmt var_type var_name value = let depth = expansion_depth value in -(*Format.eprintf "pp_assign %a %a %a %d@." Types.print_ty var_type pp_val var_name pp_val value depth;*) +(*eprintf "pp_assign %a %a %d@." Types.print_ty var_type pp_val var_name depth;*) let loop_vars = mk_loop_variables m var_type depth in let reordered_loop_vars = reorder_loop_variables loop_vars in - let rec aux fmt vars = + let rec aux typ fmt vars = match vars with | [] -> - fprintf fmt "%a = %a;" - (pp_value_suffix self var_type loop_vars pp_var) var_name - (pp_value_suffix self var_type loop_vars pp_var) value + pp_basic_assign (pp_value_suffix self loop_vars pp_var) fmt typ var_name value | (d, LVar i) :: q -> + let typ' = Types.array_element_type typ in (*eprintf "pp_aux %a %s@." Dimension.pp_dimension d i;*) fprintf fmt "@[<v 2>{@,int %s;@,for(%s=0;%s<%a;%s++)@,%a @]@,}" - i i i Dimension.pp_dimension d i - aux q + i i i pp_c_dimension d i + (aux typ') q | (d, LInt r) :: q -> (*eprintf "pp_aux %a %d@." Dimension.pp_dimension d (!r);*) - let szl = Utils.enumerate (Dimension.size_const_dimension d) in - fprintf fmt "@[<v 2>{@,%a@]@,}" - (Utils.fprintf_list ~sep:"@," (fun fmt i -> r := i; aux fmt q)) szl + let typ' = Types.array_element_type typ in + let szl = Utils.enumerate (Dimension.size_const_dimension d) in + fprintf fmt "@[<v 2>{@,%a@]@,}" + (Utils.fprintf_list ~sep:"@," (fun fmt i -> r := i; aux typ' fmt q)) szl | _ -> assert false in begin reset_loop_counter (); (*reset_addr_counter ();*) - aux fmt reordered_loop_vars + aux var_type fmt reordered_loop_vars end +let pp_machine_reset (m: machine_t) self fmt inst = + let (node, static) = + try + List.assoc inst m.minstances + with Not_found -> (Format.eprintf "internal error: pp_machine_reset %s %s %s:@." m.mname.node_id self inst; raise Not_found) in + fprintf fmt "%a(%a%t%s->%s);" + pp_machine_reset_name (node_name node) + (Utils.fprintf_list ~sep:", " Dimension.pp_dimension) static + (Utils.pp_final_char_if_non_empty ", " static) + self inst + +let pp_machine_init (m: machine_t) self fmt inst = + let (node, static) = + try + List.assoc inst m.minstances + with Not_found -> (Format.eprintf "internal error: pp_machine_init %s %s %s@." m.mname.node_id self inst; raise Not_found) in + fprintf fmt "%a(%a%t%s->%s);" + pp_machine_init_name (node_name node) + (Utils.fprintf_list ~sep:", " Dimension.pp_dimension) static + (Utils.pp_final_char_if_non_empty ", " static) + self inst + +let pp_machine_clear (m: machine_t) self fmt inst = + let (node, static) = + try + List.assoc inst m.minstances + with Not_found -> (Format.eprintf "internal error: pp_machine_clear %s %s %s@." m.mname.node_id self inst; raise Not_found) in + fprintf fmt "%a(%a%t%s->%s);" + pp_machine_clear_name (node_name node) + (Utils.fprintf_list ~sep:", " Dimension.pp_dimension) static + (Utils.pp_final_char_if_non_empty ", " static) + self inst + let has_c_prototype funname dependencies = let imported_node_opt = (* We select the last imported node with the name funname. The order of evaluation of dependencies should be @@ -248,51 +239,9 @@ let has_c_prototype funname dependencies = | None -> false | Some nd -> (match nd.nodei_prototype with Some "C" -> true | _ -> false) -let pp_instance_call dependencies m self fmt i (inputs: value_t list) (outputs: var_decl list) = - try (* stateful node instance *) - let (n,_) = List.assoc i m.minstances in - let (input_types, _) = Typing.get_type_of_call n in - let inputs = List.combine input_types inputs in - fprintf fmt "%a (%a%t%a%t%s->%s);" - pp_machine_step_name (node_name n) - (Utils.fprintf_list ~sep:", " (pp_c_val self (pp_c_var_read m))) inputs - (Utils.pp_final_char_if_non_empty ", " inputs) - (Utils.fprintf_list ~sep:", " (pp_c_var_write m)) outputs - (Utils.pp_final_char_if_non_empty ", " outputs) - self - i - with Not_found -> (* stateless node instance *) - let (n,_) = List.assoc i m.mcalls in - let (input_types, output_types) = Typing.get_type_of_call n in - let inputs = List.combine input_types inputs in - if has_c_prototype i dependencies - then (* external C function *) - let outputs = List.map2 (fun t v -> t, LocalVar v) output_types outputs in - fprintf fmt "%a = %s(%a);" - (Utils.fprintf_list ~sep:", " (pp_c_val self (pp_c_var_read m))) outputs - i - (Utils.fprintf_list ~sep:", " (pp_c_val self (pp_c_var_read m))) inputs - else - fprintf fmt "%a (%a%t%a);" - pp_machine_step_name (node_name n) - (Utils.fprintf_list ~sep:", " (pp_c_val self (pp_c_var_read m))) inputs - (Utils.pp_final_char_if_non_empty ", " inputs) - (Utils.fprintf_list ~sep:", " (pp_c_var_write m)) outputs - -let pp_machine_reset (m: machine_t) self fmt inst = - let (node, static) = - try - List.assoc inst m.minstances - with Not_found -> (Format.eprintf "pp_machine_reset %s %s %s: internal error@," m.mname.node_id self inst; raise Not_found) in - fprintf fmt "%a(%a%t%s->%s);" - pp_machine_reset_name (node_name node) - (Utils.fprintf_list ~sep:", " Dimension.pp_dimension) static - (Utils.pp_final_char_if_non_empty ", " static) - self inst - let rec pp_conditional dependencies (m: machine_t) self fmt c tl el = fprintf fmt "@[<v 2>if (%a) {%t%a@]@,@[<v 2>} else {%t%a@]@,}" - (pp_c_val self (pp_c_var_read m)) (Type_predef.type_bool, c) + (pp_c_val self (pp_c_var_read m)) c (Utils.pp_newline_if_non_empty tl) (Utils.fprintf_list ~sep:"@," (pp_machine_instr dependencies m self)) tl (Utils.pp_newline_if_non_empty el) @@ -305,31 +254,42 @@ and pp_machine_instr dependencies (m: machine_t) self fmt instr = | MLocalAssign (i,v) -> pp_assign m self (pp_c_var_read m) fmt - i.var_type (LocalVar i) v + i.var_type (mk_val (LocalVar i) i.var_type) v | MStateAssign (i,v) -> pp_assign m self (pp_c_var_read m) fmt - i.var_type (StateVar i) v - | MStep ([i0], i, vl) when Basic_library.is_internal_fun i -> - pp_machine_instr dependencies m self fmt (MLocalAssign (i0, Fun (i, vl))) + i.var_type (mk_val (StateVar i) i.var_type) v + | MStep ([i0], i, vl) when Basic_library.is_value_internal_fun (mk_val (Fun (i, vl)) i0.var_type) -> + pp_machine_instr dependencies m self fmt + (MLocalAssign (i0, mk_val (Fun (i, vl)) i0.var_type)) + | MStep ([i0], i, vl) when has_c_prototype i dependencies -> + fprintf fmt "%a = %s(%a);" + (pp_c_val self (pp_c_var_read m)) (mk_val (LocalVar i0) i0.var_type) + i + (Utils.fprintf_list ~sep:", " (pp_c_val self (pp_c_var_read m))) vl + | MStep (il, i, vl) when Mpfr.is_homomorphic_fun i -> + pp_instance_call m self fmt i vl il | MStep (il, i, vl) -> - pp_instance_call dependencies m self fmt i vl il - | MBranch (_, []) -> (Format.eprintf "internal error: C_backend_src.pp_machine_instr %a@." pp_instr instr; assert false) - | MBranch (g, hl) -> - if let t = fst (List.hd hl) in t = tag_true || t = tag_false + pp_basic_instance_call m self fmt i vl il + | MBranch (g,hl) -> + if hl <> [] && let t = fst (List.hd hl) in t = tag_true || t = tag_false then (* boolean case, needs special treatment in C because truth value is not unique *) - (* may disappear if we optimize code by replacing last branch test with default *) + (* may disappear if we optimize code by replacing last branch test with default *) let tl = try List.assoc tag_true hl with Not_found -> [] in let el = try List.assoc tag_false hl with Not_found -> [] in pp_conditional dependencies m self fmt g tl el else (* enum type case *) - let g_typ = Typing.type_const Location.dummy_loc (Const_tag (fst (List.hd hl))) in fprintf fmt "@[<v 2>switch(%a) {@,%a@,}@]" - (pp_c_val self (pp_c_var_read m)) (g_typ, g) + (pp_c_val self (pp_c_var_read m)) g (Utils.fprintf_list ~sep:"@," (pp_machine_branch dependencies m self)) hl + | MComment s -> + fprintf fmt "//%s@ " s + and pp_machine_branch dependencies m self fmt (t, h) = - fprintf fmt "@[<v 2>case %a:@,%a@,break;@]" pp_c_tag t (Utils.fprintf_list ~sep:"@," (pp_machine_instr dependencies m self)) h + fprintf fmt "@[<v 2>case %a:@,%a@,break;@]" + pp_c_tag t + (Utils.fprintf_list ~sep:"@," (pp_machine_instr dependencies m self)) h (********************************************************************************************) @@ -379,31 +339,43 @@ let print_stateless_code dependencies fmt m = if not (!Options.ansi && is_generic_node { top_decl_desc = Node m.mname; top_decl_loc = Location.dummy_loc; top_decl_owner = ""; top_decl_itf = false }) then (* C99 code *) - fprintf fmt "@[<v 2>%a {@,%a%t@,%a%a%t%t@]@,}@.@." + fprintf fmt "@[<v 2>%a {@,%a%t%a%t@,%a%a%t%a%t%t@]@,}@.@." print_stateless_prototype (m.mname.node_id, m.mstep.step_inputs, m.mstep.step_outputs) (* locals *) (Utils.fprintf_list ~sep:";@," (pp_c_decl_local_var m)) m.mstep.step_locals (Utils.pp_final_char_if_non_empty ";@," m.mstep.step_locals) + (* locals initialization *) + (Utils.fprintf_list ~sep:"@," (pp_initialize m self (pp_c_var_read m))) m.mstep.step_locals + (Utils.pp_newline_if_non_empty m.mstep.step_locals) (* check assertions *) (pp_c_checks self) m (* instrs *) (Utils.fprintf_list ~sep:"@," (pp_machine_instr dependencies m self)) m.mstep.step_instrs (Utils.pp_newline_if_non_empty m.mstep.step_instrs) + (* locals clear *) + (Utils.fprintf_list ~sep:"@," (pp_clear m self (pp_c_var_read m))) m.mstep.step_locals + (Utils.pp_newline_if_non_empty m.mstep.step_locals) (fun fmt -> fprintf fmt "return;") else (* C90 code *) let (gen_locals, base_locals) = List.partition (fun v -> Types.is_generic_type v.var_type) m.mstep.step_locals in let gen_calls = List.map (fun e -> let (id, _, _) = call_of_expr e in mk_call_var_decl e.expr_loc id) m.mname.node_gencalls in - fprintf fmt "@[<v 2>%a {@,%a%t@,%a%a%t%t@]@,}@.@." + fprintf fmt "@[<v 2>%a {@,%a%t%a%t@,%a%a%t%a%t%t@]@,}@.@." print_stateless_prototype (m.mname.node_id, (m.mstep.step_inputs@gen_locals@gen_calls), m.mstep.step_outputs) (* locals *) (Utils.fprintf_list ~sep:";@," (pp_c_decl_local_var m)) base_locals (Utils.pp_final_char_if_non_empty ";" base_locals) + (* locals initialization *) + (Utils.fprintf_list ~sep:"@," (pp_initialize m self (pp_c_var_read m))) m.mstep.step_locals + (Utils.pp_newline_if_non_empty m.mstep.step_locals) (* check assertions *) (pp_c_checks self) m (* instrs *) (Utils.fprintf_list ~sep:"@," (pp_machine_instr dependencies m self)) m.mstep.step_instrs (Utils.pp_newline_if_non_empty m.mstep.step_instrs) + (* locals clear *) + (Utils.fprintf_list ~sep:"@," (pp_clear m self (pp_c_var_read m))) m.mstep.step_locals + (Utils.pp_newline_if_non_empty m.mstep.step_locals) (fun fmt -> fprintf fmt "return;") let print_reset_code dependencies fmt m self = @@ -417,39 +389,81 @@ let print_reset_code dependencies fmt m self = (Utils.fprintf_list ~sep:"@," (pp_machine_instr dependencies m self)) m.minit (Utils.pp_newline_if_non_empty m.minit) +let print_init_code dependencies fmt m self = + let minit = List.map (function MReset i -> i | _ -> assert false) m.minit in + let array_mems = List.filter (fun v -> Types.is_array_type v.var_type) m.mmemory in + fprintf fmt "@[<v 2>%a {@,%a%t%a%t%a%treturn;@]@,}@.@." + (print_init_prototype self) (m.mname.node_id, m.mstatic) + (* array mems *) + (Utils.fprintf_list ~sep:";@," (pp_c_decl_array_mem self)) array_mems + (Utils.pp_final_char_if_non_empty ";@," array_mems) + (* memory initialization *) + (Utils.fprintf_list ~sep:"@," (pp_initialize m self (pp_c_var_read m))) m.mmemory + (Utils.pp_newline_if_non_empty m.mmemory) + (* sub-machines initialization *) + (Utils.fprintf_list ~sep:"@," (pp_machine_init m self)) minit + (Utils.pp_newline_if_non_empty m.minit) + +let print_clear_code dependencies fmt m self = + let minit = List.map (function MReset i -> i | _ -> assert false) m.minit in + let array_mems = List.filter (fun v -> Types.is_array_type v.var_type) m.mmemory in + fprintf fmt "@[<v 2>%a {@,%a%t%a%t%a%treturn;@]@,}@.@." + (print_clear_prototype self) (m.mname.node_id, m.mstatic) + (* array mems *) + (Utils.fprintf_list ~sep:";@," (pp_c_decl_array_mem self)) array_mems + (Utils.pp_final_char_if_non_empty ";@," array_mems) + (* memory clear *) + (Utils.fprintf_list ~sep:"@," (pp_clear m self (pp_c_var_read m))) m.mmemory + (Utils.pp_newline_if_non_empty m.mmemory) + (* sub-machines clear*) + (Utils.fprintf_list ~sep:"@," (pp_machine_clear m self)) minit + (Utils.pp_newline_if_non_empty m.minit) + let print_step_code dependencies fmt m self = if not (!Options.ansi && is_generic_node { top_decl_desc = Node m.mname; top_decl_loc = Location.dummy_loc; top_decl_owner = ""; top_decl_itf = false }) then (* C99 code *) let array_mems = List.filter (fun v -> Types.is_array_type v.var_type) m.mmemory in - fprintf fmt "@[<v 2>%a {@,%a%t%a%t@,%a%a%t%t@]@,}@.@." + fprintf fmt "@[<v 2>%a {@,%a%t%a%t%a%t@,%a%a%t%a%t%t@]@,}@.@." (print_step_prototype self) (m.mname.node_id, m.mstep.step_inputs, m.mstep.step_outputs) - (* locals *) + (* locals declaration *) (Utils.fprintf_list ~sep:";@," (pp_c_decl_local_var m)) m.mstep.step_locals (Utils.pp_final_char_if_non_empty ";@," m.mstep.step_locals) (* array mems *) (Utils.fprintf_list ~sep:";@," (pp_c_decl_array_mem self)) array_mems (Utils.pp_final_char_if_non_empty ";@," array_mems) + (* locals initialization *) + (Utils.fprintf_list ~sep:"@," (pp_initialize m self (pp_c_var_read m))) m.mstep.step_locals + (Utils.pp_newline_if_non_empty m.mstep.step_locals) (* check assertions *) (pp_c_checks self) m (* instrs *) (Utils.fprintf_list ~sep:"@," (pp_machine_instr dependencies m self)) m.mstep.step_instrs (Utils.pp_newline_if_non_empty m.mstep.step_instrs) + (* locals clear *) + (Utils.fprintf_list ~sep:"@," (pp_clear m self (pp_c_var_read m))) m.mstep.step_locals + (Utils.pp_newline_if_non_empty m.mstep.step_locals) (fun fmt -> fprintf fmt "return;") else (* C90 code *) let (gen_locals, base_locals) = List.partition (fun v -> Types.is_generic_type v.var_type) m.mstep.step_locals in let gen_calls = List.map (fun e -> let (id, _, _) = call_of_expr e in mk_call_var_decl e.expr_loc id) m.mname.node_gencalls in - fprintf fmt "@[<v 2>%a {@,%a%t@,%a%a%t%t@]@,}@.@." + fprintf fmt "@[<v 2>%a {@,%a%t%a%t@,%a%a%t%a%t%t@]@,}@.@." (print_step_prototype self) (m.mname.node_id, (m.mstep.step_inputs@gen_locals@gen_calls), m.mstep.step_outputs) - (* locals *) + (* locals declaration *) (Utils.fprintf_list ~sep:";@," (pp_c_decl_local_var m)) base_locals (Utils.pp_final_char_if_non_empty ";" base_locals) + (* locals initialization *) + (Utils.fprintf_list ~sep:"@," (pp_initialize m self (pp_c_var_read m))) m.mstep.step_locals + (Utils.pp_newline_if_non_empty m.mstep.step_locals) (* check assertions *) (pp_c_checks self) m (* instrs *) (Utils.fprintf_list ~sep:"@," (pp_machine_instr dependencies m self)) m.mstep.step_instrs (Utils.pp_newline_if_non_empty m.mstep.step_instrs) + (* locals clear *) + (Utils.fprintf_list ~sep:"@," (pp_clear m self (pp_c_var_read m))) m.mstep.step_locals + (Utils.pp_newline_if_non_empty m.mstep.step_locals) (fun fmt -> fprintf fmt "return;") @@ -476,18 +490,29 @@ let print_machine dependencies fmt m = let self = mk_self m in (* Reset function *) print_reset_code dependencies fmt m self; + (* Init function *) + print_init_code dependencies fmt m self; + (* Clear function *) + print_clear_code dependencies fmt m self; (* Step function *) print_step_code dependencies fmt m self end +let print_import_standard source_fmt = + begin + fprintf source_fmt "#include <assert.h>@."; + if not !Options.static_mem then + begin + fprintf source_fmt "#include <stdlib.h>@."; + end; + if !Options.mpfr then + begin + fprintf source_fmt "#include <mpfr.h>@."; + end + end let print_lib_c source_fmt basename prog machines dependencies = - - fprintf source_fmt "#include <assert.h>@."; - if not !Options.static_mem then - begin - fprintf source_fmt "#include <stdlib.h>@."; - end; + print_import_standard source_fmt; print_import_prototype source_fmt (Dep (true, basename, [], true (* assuming it is stateful *))); pp_print_newline source_fmt (); (* Print the svn version number and the supported C standard (C90 or C99) *) diff --git a/src/backends/Horn/horn_backend.ml b/src/backends/Horn/horn_backend.ml index 24f58617..bf533895 100644 --- a/src/backends/Horn/horn_backend.ml +++ b/src/backends/Horn/horn_backend.ml @@ -111,8 +111,8 @@ let pp_horn_tag fmt t = let rec pp_horn_const fmt c = match c with | Const_int i -> pp_print_int fmt i - | Const_real r -> pp_print_string fmt r - | Const_float r -> pp_print_float fmt r + | Const_real (c,e,s) -> assert false (* TODO rational pp_print_string fmt r *) + (* | Const_float r -> pp_print_float fmt r *) | Const_tag t -> pp_horn_tag fmt t | _ -> assert false @@ -121,7 +121,7 @@ let rec pp_horn_const fmt c = but an offset suffix may be added for array variables *) let rec pp_horn_val ?(is_lhs=false) self pp_var fmt v = - match v with + match v.value_desc with | Cst c -> pp_horn_const fmt c | Array _ | Access _ -> assert false (* no arrays *) @@ -135,7 +135,7 @@ let rec pp_horn_val ?(is_lhs=false) self pp_var fmt v = (* Prints a [value] indexed by the suffix list [loop_vars] *) let rec pp_value_suffix self pp_value fmt value = - match value with + match value.value_desc with | Fun (n, vl) -> Basic_library.pp_horn n (pp_value_suffix self pp_value) fmt vl | _ -> @@ -163,12 +163,12 @@ let pp_instance_call self (pp_horn_var m) fmt - o.var_type (LocalVar o) i1 + o.var_type (mk_val (LocalVar o) o.var_type) i1 else pp_assign m self (pp_horn_var m) fmt - o.var_type (LocalVar o) i2 - + o.var_type (mk_val (LocalVar o) o.var_type) i2 + end | name, _, _ -> begin @@ -181,8 +181,8 @@ let pp_instance_call inputs (Utils.pp_final_char_if_non_empty " " inputs) (* outputs *) - (Utils.fprintf_list ~sep:" " (pp_horn_val self (pp_horn_var m))) - (List.map (fun v -> LocalVar v) outputs) + (Utils.fprintf_list ~sep:" " (pp_horn_val self (pp_horn_var m))) + (List.map (fun v -> mk_val (LocalVar v) v.var_type) outputs) (Utils.pp_final_char_if_non_empty " " outputs) (* memories (next) *) (Utils.fprintf_list ~sep:" " pp_var) ( @@ -195,9 +195,9 @@ let pp_instance_call Format.fprintf fmt "(%a %a%t%a%t%a)" pp_machine_step_name (node_name n) (Utils.fprintf_list ~sep:" " (pp_horn_val self (pp_horn_var m))) inputs - (Utils.pp_final_char_if_non_empty " " inputs) - (Utils.fprintf_list ~sep:" " (pp_horn_val self (pp_horn_var m))) - (List.map (fun v -> LocalVar v) outputs) + (Utils.pp_final_char_if_non_empty " " inputs) + (Utils.fprintf_list ~sep:" " (pp_horn_val self (pp_horn_var m))) + (List.map (fun v -> mk_val (LocalVar v) v.var_type) outputs) (Utils.pp_final_char_if_non_empty " " outputs) (Utils.fprintf_list ~sep:" " pp_var) ( (rename_machine_list @@ -218,9 +218,9 @@ let pp_instance_call (node_name n) (Utils.fprintf_list ~sep:" " (pp_horn_val self (pp_horn_var m))) inputs - (Utils.pp_final_char_if_non_empty " " inputs) - (Utils.fprintf_list ~sep:" " (pp_horn_val self (pp_horn_var m))) - (List.map (fun v -> LocalVar v) outputs) + (Utils.pp_final_char_if_non_empty " " inputs) + (Utils.fprintf_list ~sep:" " (pp_horn_val self (pp_horn_var m))) + (List.map (fun v -> mk_val (LocalVar v) v.var_type) outputs) ) let pp_machine_init (m: machine_t) self fmt inst = @@ -247,12 +247,12 @@ and pp_machine_instr machines ?(init=false) (m: machine_t) self fmt instr = | MLocalAssign (i,v) -> pp_assign m self (pp_horn_var m) fmt - i.var_type (LocalVar i) v + i.var_type (mk_val (LocalVar i) i.var_type) v | MStateAssign (i,v) -> pp_assign m self (pp_horn_var m) fmt - i.var_type (StateVar i) v - | MStep ([i0], i, vl) when Basic_library.is_internal_fun i -> + i.var_type (mk_val (StateVar i) i.var_type) v + | MStep ([i0], i, vl) when Basic_library.is_value_internal_fun (mk_val (Fun (i, vl)) i0.var_type) -> assert false (* This should not happen anymore *) | MStep (il, i, vl) -> pp_instance_call machines ~init:init m self fmt i vl il @@ -264,6 +264,7 @@ and pp_machine_instr machines ?(init=false) (m: machine_t) self fmt instr = let el = try List.assoc tag_false hl with Not_found -> [] in pp_conditional machines ~init:init m self fmt g tl el else assert false (* enum type case *) + | MComment _ -> () (**************************************************************) @@ -362,6 +363,18 @@ let print_machine machines fmt m = (Utils.fprintf_list ~sep:" " pp_var) (step_vars machines m); end ); + +(* + match m.mspec with + None -> () (* No node spec; we do nothing *) + | Some {requires = []; ensures = [EnsuresExpr e]; behaviors = []} -> + ( + (* For the moment, we only deal with simple case: single ensures, no other parameters *) + () + + ) + | _ -> () (* Other cases give nothing *) +*) end end @@ -427,7 +440,8 @@ let check_prop machines fmt node machine = (pp_conj pp_var) main_output (Utils.fprintf_list ~sep:" " pp_var) main_memory_next ; - if !Options.horn_query then Format.fprintf fmt "(query ERR)@." + if !Options.horn_queries then + Format.fprintf fmt "(query ERR)@." let cex_computation machines fmt node machine = diff --git a/src/basic_library.ml b/src/basic_library.ml index 7e35b8a4..e290f787 100644 --- a/src/basic_library.ml +++ b/src/basic_library.ml @@ -23,15 +23,15 @@ let static_op ty = type_static (mkdim_var ()) ty let type_env = - List.fold_left + List.fold_left (fun env (op, op_type) -> TE.add_value env op op_type) TE.initial [ "true", (static_op type_bool); "false", (static_op type_bool); "+", (static_op type_bin_poly_op); - "uminus", (static_op type_unary_poly_op); - "-", (static_op type_bin_poly_op); + "uminus", (static_op type_unary_poly_op); + "-", (static_op type_bin_poly_op); "*", (static_op type_bin_poly_op); "/", (static_op type_bin_poly_op); "mod", (static_op type_bin_int_op); @@ -48,7 +48,7 @@ let type_env = "=", (static_op type_bin_comp_op); "not", (static_op type_unary_bool_op) ] - + module CE = Env let clock_env = @@ -56,10 +56,10 @@ let clock_env = let env' = List.fold_right (fun op env -> CE.add_value env op ck_nullary_univ) ["true"; "false"] init_env in - let env' = + let env' = List.fold_right (fun op env -> CE.add_value env op ck_unary_univ) ["uminus"; "not"] env' in - let env' = + let env' = List.fold_right (fun op env -> CE.add_value env op ck_bin_univ) ["+"; "-"; "*"; "/"; "mod"; "&&"; "||"; "xor"; "equi"; "impl"; "<"; "<="; ">"; ">="; "!="; "="] env' in env' @@ -74,10 +74,10 @@ let delay_env = let env' = List.fold_right (fun op env -> DE.add_value env op delay_unary_poly_op) ["uminus"; "not"] env' in - let env' = + let env' = List.fold_right (fun op env -> DE.add_value env op delay_binary_poly_op) ["+"; "-"; "*"; "/"; "mod"; "&&"; "||"; "xor"; "equi"; "impl"; "<"; "<="; ">"; ">="; "!="; "="] env' in - let env' = + let env' = List.fold_right (fun op env -> DE.add_value env op delay_ternary_poly_op) [] env' in env' @@ -85,7 +85,7 @@ let delay_env = module VE = Env let eval_env = - let defs = [ + let defs = [ "uminus", (function [Dint a] -> Dint (-a) | _ -> assert false); "not", (function [Dbool b] -> Dbool (not b) | _ -> assert false); "+", (function [Dint a; Dint b] -> Dint (a+b) | _ -> assert false); @@ -106,14 +106,42 @@ let eval_env = "=", (function [a; b] -> Dbool (a=b) | _ -> assert false); ] in - List.fold_left + List.fold_left (fun env (op, op_eval) -> VE.add_value env op op_eval) VE.initial defs -let internal_funs = ["+";"-";"*";"/";"mod";"&&";"||";"xor";"equi";"impl";"<";">";"<=";">=";"!=";"=";"uminus";"not"] +let bool_ops = ["&&";"||";"xor";"equi";"impl";"not"] +let rel_ops = ["<";">";"<=";">=";"!=";"="] +let num_ops = ["+";"-";"*";"/";"mod";"uminus"] + +let internal_funs = bool_ops@rel_ops@num_ops + +let rec is_internal_fun x targs = +(*Format.eprintf "is_internal_fun %s %a@." x Types.print_ty (List.hd targs);*) + match targs with + | [] -> assert false + | t::_ when Types.is_real_type t -> List.mem x internal_funs && not !Options.mpfr + | t::_ when Types.is_array_type t -> is_internal_fun x [Types.array_element_type t] + | _ -> List.mem x internal_funs + +let is_expr_internal_fun expr = + match expr.expr_desc with + | Expr_appl (f, e, _) -> is_internal_fun f (Types.type_list_of_type e.expr_type) + | _ -> assert false -let is_internal_fun x = +let is_value_internal_fun v = + match v.value_desc with + | Fun (f, vl) -> is_internal_fun f (List.map (fun v -> v.value_type) vl) + | _ -> assert false + +let is_numeric_operator x = + List.mem x num_ops + +let is_homomorphic_fun x = + List.mem x internal_funs + +let is_stateless_fun x = List.mem x internal_funs let pp_c i pp_val fmt vl = @@ -133,25 +161,24 @@ let pp_java i pp_val fmt vl = match i, vl with (* | "ite", [v1; v2; v3] -> Format.fprintf fmt "(%a?(%a):(%a))" pp_val v1 pp_val v2 pp_val v3 *) | "uminus", [v] -> Format.fprintf fmt "(- %a)" pp_val v - | "not", [v] -> Format.fprintf fmt "(!%a)" pp_val v - | "impl", [v1; v2] -> Format.fprintf fmt "(!%a || %a)" pp_val v1 pp_val v2 - | "=", [v1; v2] -> Format.fprintf fmt "(%a == %a)" pp_val v1 pp_val v2 + | "not", [v] -> Format.fprintf fmt "(!%a)" pp_val v + | "impl", [v1; v2] -> Format.fprintf fmt "(!%a || %a)" pp_val v1 pp_val v2 + | "=", [v1; v2] -> Format.fprintf fmt "(%a == %a)" pp_val v1 pp_val v2 | "mod", [v1; v2] -> Format.fprintf fmt "(%a %% %a)" pp_val v1 pp_val v2 | "equi", [v1; v2] -> Format.fprintf fmt "(%a == %a)" pp_val v1 pp_val v2 | "xor", [v1; v2] -> Format.fprintf fmt "(%a != %a)" pp_val v1 pp_val v2 | _, [v1; v2] -> Format.fprintf fmt "(%a %s %a)" pp_val v1 i pp_val v2 | _ -> (Format.eprintf "internal error: Basic_library.pp_java %s@." i; assert false) -let pp_horn i pp_val fmt vl = +let pp_horn i pp_val fmt vl = match i, vl with - | "ite", [v1; v2; v3] -> Format.fprintf fmt "(@[<hov 2>ite %a@ %a@ %a@])" pp_val v1 pp_val v2 pp_val v3 - + | "ite", [v1; v2; v3] -> Format.fprintf fmt "(@[<hov 2>ite %a@ %a@ %a@])" pp_val v1 pp_val v2 pp_val v3 | "uminus", [v] -> Format.fprintf fmt "(- %a)" pp_val v - | "not", [v] -> Format.fprintf fmt "(not %a)" pp_val v - | "=", [v1; v2] -> Format.fprintf fmt "(= %a %a)" pp_val v1 pp_val v2 - | "&&", [v1; v2] -> Format.fprintf fmt "(and %a %a)" pp_val v1 pp_val v2 - | "||", [v1; v2] -> Format.fprintf fmt "(or %a %a)" pp_val v1 pp_val v2 - | "impl", [v1; v2] -> Format.fprintf fmt "(=> %a %a)" pp_val v1 pp_val v2 + | "not", [v] -> Format.fprintf fmt "(not %a)" pp_val v + | "=", [v1; v2] -> Format.fprintf fmt "(= %a %a)" pp_val v1 pp_val v2 + | "&&", [v1; v2] -> Format.fprintf fmt "(and %a %a)" pp_val v1 pp_val v2 + | "||", [v1; v2] -> Format.fprintf fmt "(or %a %a)" pp_val v1 pp_val v2 + | "impl", [v1; v2] -> Format.fprintf fmt "(=> %a %a)" pp_val v1 pp_val v2 | "mod", [v1; v2] -> Format.fprintf fmt "(mod %a %a)" pp_val v1 pp_val v2 | "equi", [v1; v2] -> Format.fprintf fmt "(%a = %a)" pp_val v1 pp_val v2 | "xor", [v1; v2] -> Format.fprintf fmt "(%a xor %a)" pp_val v1 pp_val v2 diff --git a/src/causality.ml b/src/causality.ml index 00efa543..e616ec5f 100644 --- a/src/causality.ml +++ b/src/causality.ml @@ -225,7 +225,7 @@ let add_eq_dependencies mems inputs node_vars eq (g, g') = | Expr_arrow (e1, e2) -> add_dep lhs_is_mem lhs e2 (add_dep lhs_is_mem lhs e1 g) | Expr_when (e, c, _) -> add_dep lhs_is_mem lhs e (add_var lhs_is_mem lhs c g) | Expr_appl (f, e, None) -> - if Basic_library.is_internal_fun f + if Basic_library.is_expr_internal_fun rhs (* tuple component-wise dependency for internal operators *) then List.fold_right (add_dep lhs_is_mem lhs) (expr_list_of_expr e) g @@ -278,7 +278,7 @@ module NodeDep = struct | Expr_pre e | Expr_when (e,_,_) -> get_expr_calls prednode e | Expr_appl (id,e, _) -> - if not (Basic_library.is_internal_fun id) && prednode id + if not (Basic_library.is_expr_internal_fun expr) && prednode id then ESet.add expr (get_expr_calls prednode e) else (get_expr_calls prednode e) diff --git a/src/clock_calculus.ml b/src/clock_calculus.ml index ee201a88..879d4313 100755 --- a/src/clock_calculus.ml +++ b/src/clock_calculus.ml @@ -608,7 +608,7 @@ and clock_subtyping_arg env ?(sub=true) real_arg formal_clock = (* computes clocks for node application *) and clock_appl env f args clock_reset loc = let args = expr_list_of_expr args in - if Basic_library.is_internal_fun f && List.exists is_tuple_expr args + if Basic_library.is_homomorphic_fun f && List.exists is_tuple_expr args then let args = Utils.transpose_list (List.map expr_list_of_expr args) in Clocks.clock_of_clock_list (List.map (fun args -> clock_call env f args clock_reset loc) args) diff --git a/src/compiler_common.ml b/src/compiler_common.ml index f375d06a..8d5e2040 100644 --- a/src/compiler_common.ml +++ b/src/compiler_common.ml @@ -14,6 +14,13 @@ open Format open LustreSpec open Corelang +let check_main () = + if !Options.main_node = "" then + begin + eprintf "Code generation error: %a@." pp_error No_main_specified; + raise (Error (Location.dummy_loc, No_main_specified)) + end + let create_dest_dir () = begin if not (Sys.file_exists !Options.dest_dir) then @@ -88,6 +95,16 @@ let check_stateless_decls decls = Location.pp_loc loc; raise exc +let force_stateful_decls decls = + Log.report ~level:1 (fun fmt -> fprintf fmt ".. forcing stateful status@ "); + try + Stateless.force_prog decls + with (Stateless.Error (loc, err)) as exc -> + eprintf "Stateless status error: %a%a@." + Stateless.pp_error err + Location.pp_loc loc; + raise exc + let type_decls env decls = Log.report ~level:1 (fun fmt -> fprintf fmt ".. typing@ "); let new_env = diff --git a/src/corelang.ml b/src/corelang.ml index 857209ec..57450e82 100755 --- a/src/corelang.ml +++ b/src/corelang.ml @@ -1,4 +1,3 @@ -(********************************************************************) (* *) (* The LustreC compiler toolset / The LustreC Development Team *) (* Copyright 2012 - -- ONERA - CNRS - INPT *) @@ -55,6 +54,7 @@ let mkvar_decl loc ?(orig=false) (id, ty_dec, ck_dec, is_const, value) = let mkexpr loc d = { expr_tag = Utils.new_tag (); + expr_desc = d; expr_type = Types.new_var (); expr_clock = Clocks.new_var true; @@ -245,14 +245,14 @@ let mktop = mktop_decl Location.dummy_loc Version.include_path false let top_int_type = mktop (TypeDef {tydef_id = "int"; tydef_desc = Tydec_int}) let top_bool_type = mktop (TypeDef {tydef_id = "bool"; tydef_desc = Tydec_bool}) -let top_float_type = mktop (TypeDef {tydef_id = "float"; tydef_desc = Tydec_float}) +(* let top_float_type = mktop (TypeDef {tydef_id = "float"; tydef_desc = Tydec_float}) *) let top_real_type = mktop (TypeDef {tydef_id = "real"; tydef_desc = Tydec_real}) let type_table = Utils.create_hashtable 20 [ Tydec_int , top_int_type; Tydec_bool , top_bool_type; - Tydec_float, top_float_type; + (* Tydec_float, top_float_type; *) Tydec_real , top_real_type ] @@ -270,7 +270,7 @@ let print_type_table fmt () = let rec is_user_type typ = match typ with | Tydec_int | Tydec_bool | Tydec_real - | Tydec_float | Tydec_any | Tydec_const _ -> false + (* | Tydec_float *) | Tydec_any | Tydec_const _ -> false | Tydec_clock typ' -> is_user_type typ' | _ -> true @@ -289,7 +289,7 @@ let rec coretype_equal ty1 ty2 = | _ , Tydec_const _ -> coretype_equal ty2 ty1 | Tydec_int , Tydec_int | Tydec_real , Tydec_real - | Tydec_float , Tydec_float + (* | Tydec_float , Tydec_float *) | Tydec_bool , Tydec_bool -> true | Tydec_clock ty1 , Tydec_clock ty2 -> coretype_equal ty1 ty2 | Tydec_array (d1,ty1), Tydec_array (d2, ty2) -> Dimension.is_eq_dimension d1 d2 && coretype_equal ty1 ty2 @@ -461,7 +461,7 @@ let rec dimension_of_expr expr = match expr.expr_desc with | Expr_const c -> dimension_of_const expr.expr_loc c | Expr_ident id -> mkdim_ident expr.expr_loc id - | Expr_appl (f, args, None) when Basic_library.is_internal_fun f -> + | Expr_appl (f, args, None) when Basic_library.is_expr_internal_fun expr -> let k = Types.get_static_value (Env.lookup_value Basic_library.type_env f) in if k = None then raise InvalidDimension; mkdim_appl expr.expr_loc f (List.map dimension_of_expr (expr_list_of_expr args)) @@ -501,7 +501,7 @@ let mk_new_node_name nd id = mk_new_name used id let get_var id var_list = - List.find (fun v -> v.var_id = id) var_list + List.find (fun v -> v.var_id = id) var_list let get_node_var id node = get_var id (get_node_vars node) @@ -579,7 +579,7 @@ let get_node_interface nd = nodei_stateless = nd.node_dec_stateless; nodei_spec = nd.node_spec; nodei_prototype = None; - nodei_in_lib = None; + nodei_in_lib = []; } (************************************************************************) @@ -624,16 +624,15 @@ let rec rename_carrier rename cck = let eq_replace_rhs_var pvar fvar eq = let pvar l = List.exists pvar l in let rec replace lhs rhs = - { rhs with expr_desc = replace_desc lhs rhs.expr_desc } - and replace_desc lhs rhs_desc = + { rhs with expr_desc = match lhs with | [] -> assert false - | [_] -> if pvar lhs then expr_desc_replace_var fvar rhs_desc else rhs_desc + | [_] -> if pvar lhs then expr_desc_replace_var fvar rhs.expr_desc else rhs.expr_desc | _ -> - (match rhs_desc with + (match rhs.expr_desc with | Expr_tuple tl -> Expr_tuple (List.map2 (fun v e -> replace [v] e) lhs tl) - | Expr_appl (f, arg, None) when Basic_library.is_internal_fun f -> + | Expr_appl (f, arg, None) when Basic_library.is_expr_internal_fun rhs -> let args = expr_list_of_expr arg in Expr_appl (f, expr_of_expr_list arg.expr_loc (List.map (replace lhs) args), None) | Expr_array _ @@ -643,8 +642,8 @@ let rec rename_carrier rename cck = | Expr_ident _ | Expr_appl _ -> if pvar lhs - then expr_desc_replace_var fvar rhs_desc - else rhs_desc + then expr_desc_replace_var fvar rhs.expr_desc + else rhs.expr_desc | Expr_ite (c, t, e) -> Expr_ite (replace lhs c, replace lhs t, replace lhs e) | Expr_arrow (e1, e2) -> Expr_arrow (replace lhs e1, replace lhs e2) | Expr_fby (e1, e2) -> Expr_fby (replace lhs e1, replace lhs e2) @@ -654,6 +653,7 @@ let rec rename_carrier rename cck = | Expr_merge (i, hl) -> let i' = if pvar lhs then fvar i else i in Expr_merge (i', List.map (fun (t, h) -> (t, replace lhs h)) hl) ) + } in { eq with eq_rhs = replace eq.eq_lhs eq.eq_rhs } @@ -792,15 +792,15 @@ let pp_prog_clock fmt prog = Utils.fprintf_list ~sep:"" pp_decl_clock fmt prog let pp_error fmt = function - | Main_not_found -> - fprintf fmt "cannot compile node %s: could not find the node definition.@." - !Options.main_node + Main_not_found -> + fprintf fmt "could not find the definition of main node %s.@." + !Global.main_node | Main_wrong_kind -> fprintf fmt - "name %s does not correspond to a (non-imported) node definition.@." - !Options.main_node + "name %s does not correspond to a valid main node definition.@." + !Global.main_node | No_main_specified -> - fprintf fmt "no main node specified.@." + fprintf fmt "no main node specified (use -node option)@." | Unbound_symbol sym -> fprintf fmt "%s is undefined.@." @@ -811,11 +811,11 @@ let pp_error fmt = function sym | Unknown_library sym -> fprintf fmt - "impossible to load library %s.lusic.@.Please compile the corresponding interface or source file.@." + "impossible to load library %s.lusic@.Please compile the corresponding interface or source file.@." sym | Wrong_number sym -> fprintf fmt - "library %s.lusic has a different version number and may crash compiler.@.Please recompile the corresponding interface or source file.@." + "library %s.lusic has a different version number and may crash the compiler.@.Please recompile the corresponding interface or source file.@." sym (* filling node table with internal functions *) @@ -844,7 +844,7 @@ let mk_internal_node id = nodei_stateless = Types.get_static_value ty <> None; nodei_spec = spec; nodei_prototype = None; - nodei_in_lib = None; + nodei_in_lib = []; }) let add_internal_funs () = @@ -927,10 +927,8 @@ let rec substitute_expr vars_to_replace defs e = *) let rec get_expr_calls nodes e = - get_calls_expr_desc nodes e.expr_desc -and get_calls_expr_desc nodes expr_desc = let get_calls = get_expr_calls nodes in - match expr_desc with + match e.expr_desc with | Expr_const _ | Expr_ident _ -> Utils.ISet.empty | Expr_tuple el @@ -944,7 +942,7 @@ and get_calls_expr_desc nodes expr_desc = | Expr_fby (e1, e2) -> Utils.ISet.union (get_calls e1) (get_calls e2) | Expr_merge (_, hl) -> List.fold_left (fun accu (_, h) -> Utils.ISet.union accu (get_calls h)) Utils.ISet.empty hl | Expr_appl (i, e', i') -> - if Basic_library.is_internal_fun i then + if Basic_library.is_expr_internal_fun e then (get_calls e') else let calls = Utils.ISet.add i (get_calls e') in diff --git a/src/corelang.mli b/src/corelang.mli index d4a1d568..e31c4c9d 100755 --- a/src/corelang.mli +++ b/src/corelang.mli @@ -29,6 +29,8 @@ val mktop_decl: Location.t -> ident -> bool -> top_decl_desc -> top_decl val mkpredef_call: Location.t -> ident -> expr list -> expr val mk_new_name: (ident -> bool) -> ident -> ident val mk_new_node_name: node_desc -> ident -> ident +val mktop: top_decl_desc -> top_decl + val node_table : (ident, top_decl) Hashtbl.t val print_node_table: Format.formatter -> unit -> unit @@ -122,6 +124,7 @@ val eq_replace_rhs_var: (ident -> bool) -> (ident -> ident) -> eq -> eq (** rename_prog f_node f_var f_const prog *) val rename_prog: (ident -> ident) -> (ident -> ident) -> (ident -> ident) -> program -> program + val substitute_expr: var_decl list -> eq list -> expr -> expr val copy_var_decl: var_decl -> var_decl @@ -134,7 +137,7 @@ val copy_prog: top_decl list -> top_decl list val mkeexpr: Location.t -> expr -> eexpr val merge_node_annot: node_annot -> node_annot -> node_annot val extend_eexpr: (quantifier_type * var_decl list) list -> eexpr -> eexpr -val update_expr_annot: ident -> expr -> LustreSpec.expr_annot -> expr +val update_expr_annot: ident -> expr -> expr_annot -> expr (* val mkpredef_call: Location.t -> ident -> eexpr list -> eexpr*) (* Local Variables: *) diff --git a/src/dimension.ml b/src/dimension.ml index 79d6f1f0..ec0b2f26 100644 --- a/src/dimension.ml +++ b/src/dimension.ml @@ -243,14 +243,7 @@ let rec eval eval_op eval_const dim = end | Dvar -> () | Dunivar -> assert false -(* -in -begin - Format.eprintf "Dimension.eval %a = " pp_dimension dim; - eval eval_op eval_const dim; - Format.eprintf "%a@." pp_dimension dim -end -*) + let uneval const univar = let univar = repr univar in match univar.dim_desc with diff --git a/src/env.ml b/src/env.ml index 31111bb5..9fc32db2 100755 --- a/src/env.ml +++ b/src/env.ml @@ -13,6 +13,7 @@ clock-calculus. *) open Utils +type 'a t = 'a IMap.t (* Same namespace for nodes, variables and constants *) let initial = IMap.empty diff --git a/src/inliner.ml b/src/inliner.ml index 1a2dd103..691e387a 100644 --- a/src/inliner.ml +++ b/src/inliner.ml @@ -64,6 +64,7 @@ We select the called node equations and variables. the resulting expression is tuple_of_renamed_outputs TODO: convert the specification/annotation/assert and inject them +DONE: annotations TODO: deal with reset *) let inline_call node orig_expr args reset locals caller = @@ -136,10 +137,14 @@ in }) node.node_asserts in + let annots' = + Plugins.inline_annots rename node.node_annot + in expr, inputs'@outputs'@locals'@locals, assign_inputs::eqs', - asserts' + asserts', + annots' @@ -156,27 +161,27 @@ let rec inline_expr ?(selection_on_annotation=false) expr locals node nodes = let inline_expr = inline_expr ~selection_on_annotation:selection_on_annotation in let inline_node = inline_node ~selection_on_annotation:selection_on_annotation in let inline_tuple el = - List.fold_right (fun e (el_tail, locals, eqs, asserts) -> - let e', locals', eqs', asserts' = inline_expr e locals node nodes in - e'::el_tail, locals', eqs'@eqs, asserts@asserts' - ) el ([], locals, [], []) + List.fold_right (fun e (el_tail, locals, eqs, asserts, annots) -> + let e', locals', eqs', asserts', annots' = inline_expr e locals node nodes in + e'::el_tail, locals', eqs'@eqs, asserts@asserts', annots@annots' + ) el ([], locals, [], [], []) in let inline_pair e1 e2 = - let el', l', eqs', asserts' = inline_tuple [e1;e2] in + let el', l', eqs', asserts', annots' = inline_tuple [e1;e2] in match el' with - | [e1'; e2'] -> e1', e2', l', eqs', asserts' + | [e1'; e2'] -> e1', e2', l', eqs', asserts', annots' | _ -> assert false in let inline_triple e1 e2 e3 = - let el', l', eqs', asserts' = inline_tuple [e1;e2;e3] in + let el', l', eqs', asserts', annots' = inline_tuple [e1;e2;e3] in match el' with - | [e1'; e2'; e3'] -> e1', e2', e3', l', eqs', asserts' + | [e1'; e2'; e3'] -> e1', e2', e3', l', eqs', asserts', annots' | _ -> assert false in - + match expr.expr_desc with | Expr_appl (id, args, reset) -> - let args', locals', eqs', asserts' = inline_expr args locals node nodes in + let args', locals', eqs', asserts', annots' = inline_expr args locals node nodes in if List.exists (check_node_name id) nodes && (* the current node call is provided as arguments nodes *) (not selection_on_annotation || is_inline_expr expr) (* and if selection on annotation is activated, @@ -188,68 +193,70 @@ let rec inline_expr ?(selection_on_annotation=false) expr locals node nodes = with Not_found -> (assert false) in let called = node_of_top called in let called' = inline_node called nodes in - let expr, locals', eqs'', asserts'' = + let expr, locals', eqs'', asserts'', annots'' = inline_call called' expr args' reset locals' node in - expr, locals', eqs'@eqs'', asserts'@asserts'' + 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)}, locals', eqs', - asserts' + asserts', + annots' (* For other cases, we just keep the structure, but convert sub-expressions *) | Expr_const _ - | Expr_ident _ -> expr, locals, [], [] + | Expr_ident _ -> expr, locals, [], [], [] | Expr_tuple el -> - let el', l', eqs', asserts' = inline_tuple el in - { expr with expr_desc = Expr_tuple el' }, l', eqs', asserts' + let el', l', eqs', asserts', annots' = inline_tuple el in + { expr with expr_desc = Expr_tuple el' }, l', eqs', asserts', annots' | Expr_ite (g, t, e) -> - let g', t', e', l', eqs', asserts' = inline_triple g t e in - { expr with expr_desc = Expr_ite (g', t', e') }, l', eqs', asserts' + let g', t', e', l', eqs', asserts', annots' = inline_triple g t e in + { expr with expr_desc = Expr_ite (g', t', e') }, l', eqs', asserts', annots' | Expr_arrow (e1, e2) -> - let e1', e2', l', eqs', asserts' = inline_pair e1 e2 in - { expr with expr_desc = Expr_arrow (e1', e2') } , l', eqs', asserts' + let e1', e2', l', eqs', asserts', annots' = inline_pair e1 e2 in + { expr with expr_desc = Expr_arrow (e1', e2') } , l', eqs', asserts', annots' | Expr_fby (e1, e2) -> - let e1', e2', l', eqs', asserts' = inline_pair e1 e2 in - { expr with expr_desc = Expr_fby (e1', e2') }, l', eqs', asserts' + let e1', e2', l', eqs', asserts', annots' = inline_pair e1 e2 in + { expr with expr_desc = Expr_fby (e1', e2') }, l', eqs', asserts', annots' | Expr_array el -> - let el', l', eqs', asserts' = inline_tuple el in - { expr with expr_desc = Expr_array el' }, l', eqs', asserts' + let el', l', eqs', asserts', annots' = inline_tuple el in + { expr with expr_desc = Expr_array el' }, l', eqs', asserts', annots' | Expr_access (e, dim) -> - let e', l', eqs', asserts' = inline_expr e locals node nodes in - { expr with expr_desc = Expr_access (e', dim) }, l', eqs', asserts' + let e', l', eqs', asserts', annots' = inline_expr e locals node nodes in + { expr with expr_desc = Expr_access (e', dim) }, l', eqs', asserts', annots' | Expr_power (e, dim) -> - let e', l', eqs', asserts' = inline_expr e locals node nodes in - { expr with expr_desc = Expr_power (e', dim) }, l', eqs', asserts' + let e', l', eqs', asserts', annots' = inline_expr e locals node nodes in + { expr with expr_desc = Expr_power (e', dim) }, l', eqs', asserts', annots' | Expr_pre e -> - let e', l', eqs', asserts' = inline_expr e locals node nodes in - { expr with expr_desc = Expr_pre e' }, l', eqs', asserts' + let e', l', eqs', asserts', annots' = inline_expr e locals node nodes in + { expr with expr_desc = Expr_pre e' }, l', eqs', asserts', annots' | Expr_when (e, id, label) -> - let e', l', eqs', asserts' = inline_expr e locals node nodes in - { expr with expr_desc = Expr_when (e', id, label) }, l', eqs', asserts' + let e', l', eqs', asserts', annots' = inline_expr e locals node nodes in + { expr with expr_desc = Expr_when (e', id, label) }, l', eqs', asserts', annots' | Expr_merge (id, branches) -> - let el, l', eqs', asserts' = inline_tuple (List.map snd branches) in + let el, l', eqs', asserts', annots' = inline_tuple (List.map snd branches) in let branches' = List.map2 (fun (label, _) v -> label, v) branches el in - { expr with expr_desc = Expr_merge (id, branches') }, l', eqs', asserts' + { expr with expr_desc = Expr_merge (id, branches') }, l', eqs', asserts', annots' and inline_node ?(selection_on_annotation=false) node nodes = try copy_node (Hashtbl.find inline_table node.node_id) with Not_found -> let inline_expr = inline_expr ~selection_on_annotation:selection_on_annotation in - let new_locals, eqs, asserts = - List.fold_left (fun (locals, eqs, asserts) eq -> - let eq_rhs', locals', new_eqs', asserts' = + let new_locals, eqs, asserts, annots = + List.fold_left (fun (locals, eqs, asserts, annots) eq -> + let eq_rhs', locals', new_eqs', asserts', annots' = inline_expr eq.eq_rhs locals node nodes in - locals', { eq with eq_rhs = eq_rhs' }::new_eqs'@eqs, asserts'@asserts - ) (node.node_locals, [], node.node_asserts) (get_node_eqs node) + locals', { eq with eq_rhs = eq_rhs' }::new_eqs'@eqs, asserts'@asserts, annots'@annots + ) (node.node_locals, [], node.node_asserts, node.node_annot) (get_node_eqs node) in let inlined = { node with node_locals = new_locals; node_stmts = List.map (fun eq -> Eq eq) eqs; node_asserts = asserts; + node_annot = annots; } in begin @@ -363,6 +370,11 @@ let witness filename main_name orig inlined type_env clock_env = in let main = [{ top_decl_desc = Node main_node; top_decl_loc = loc; top_decl_owner = filename; top_decl_itf = false }] in let new_prog = others@nodes_origs@nodes_inlined@main in +(* + let _ = Typing.type_prog type_env new_prog in + let _ = Clock_calculus.clock_prog clock_env new_prog in +*) + let witness_file = (Options.get_witness_dir filename) ^ "/" ^ "inliner_witness.lus" in let witness_out = open_out witness_file in let witness_fmt = Format.formatter_of_out_channel witness_out in @@ -387,11 +399,16 @@ let global_inline basename prog type_env clock_env = | _ -> main_opt, nodes, top::others) prog (None, [], []) in - (* Recursively each call of a node in the top node is replaced *) let main_node = Utils.desome main_node in let main_node' = inline_all_calls main_node other_nodes in - let res = List.map (fun top -> if check_node_name !Options.main_node top then main_node' else top) prog in + let res = main_node'::other_tops in + if !Options.witnesses then ( + witness + basename + (match main_node.top_decl_desc with Node nd -> nd.node_id | _ -> assert false) + prog res type_env clock_env + ); res let local_inline basename prog type_env clock_env = diff --git a/src/lexerLustreSpec.mll b/src/lexerLustreSpec.mll index da4ca6ca..8a000223 100644 --- a/src/lexerLustreSpec.mll +++ b/src/lexerLustreSpec.mll @@ -44,7 +44,7 @@ let keyword_table = "wcet", WCET; "int", TINT; "bool", TBOOL; - "float", TFLOAT; + (* "float", TFLOAT; *) "real", TREAL; "clock", TCLOCK; "not", NOT; @@ -86,11 +86,13 @@ rule token = parse token lexbuf } | blank + {token lexbuf} - | '-'? ['0'-'9'] ['0'-'9']* '.' ['0'-'9']* - {FLOAT (float_of_string (Lexing.lexeme lexbuf))} + | (('-'? ['0'-'9'] ['0'-'9']* as l) '.' (['0'-'9']* as r)) as s + {REAL (Num.num_of_string (l^r), String.length r, s)} | '-'? ['0'-'9']+ {INT (int_of_string (Lexing.lexeme lexbuf)) } - | '-'? ['0'-'9']+ '.' ['0'-'9']+ ('E'|'e') ('+'|'-') ['0'-'9'] ['0'-'9'] as s {REAL s} + | (('-'? ['0'-'9']+ as l) '.' (['0'-'9']+ as r) ('E'|'e') (('+'|'-') ['0'-'9'] ['0'-'9']* as exp)) as s + {REAL (Num.num_of_string (l^r), String.length r + -1 * int_of_string exp, s)} + (* | '/' (['_' 'A'-'Z' 'a'-'z'] ['A'-'Z' 'a'-'z' '_' '0'-'9']* '/')+ as s {IDENT s} *) diff --git a/src/lexer_lustre.mll b/src/lexer_lustre.mll index 6fa09b53..5fbdb4d8 100755 --- a/src/lexer_lustre.mll +++ b/src/lexer_lustre.mll @@ -48,7 +48,7 @@ let keyword_table = "type", TYPE; "int", TINT; "bool", TBOOL; - "float", TFLOAT; + (* "float", TFLOAT; *) "real", TREAL; "clock", TCLOCK; "not", NOT; @@ -89,113 +89,115 @@ let newline = ('\010' | '\013' | "\013\010") let notnewline = [^ '\010' '\013'] let blank = [' ' '\009' '\012'] -rule token = parse -| "--@" { Buffer.clear buf; - spec_singleline lexbuf } -| "(*@" { Buffer.clear buf; - spec_multiline 0 lexbuf } -| "--!" { Buffer.clear buf; - annot_singleline lexbuf } -| "(*!" { Buffer.clear buf; - annot_multiline 0 lexbuf } -| "(*" - { comment 0 lexbuf } -| "--" [^ '!' '@'] notnewline* (newline|eof) - { incr_line lexbuf; - token lexbuf } -| newline - { incr_line lexbuf; - token lexbuf } -| blank + - {token lexbuf} -| ['0'-'9'] ['0'-'9']* '.' ['0'-'9']* - {FLOAT (float_of_string (Lexing.lexeme lexbuf))} -| ['0'-'9']+ - {INT (int_of_string (Lexing.lexeme lexbuf)) } -| ['0'-'9']+ '.' ['0'-'9']+ ('E'|'e') ('+'|'-') ['0'-'9'] ['0'-'9']* as s {REAL s} -| "tel." {TEL} -| "tel;" {TEL} -| "#open" { OPEN } -| ['_' 'a'-'z'] [ '_' 'a'-'z' 'A'-'Z' '0'-'9']* - {let s = Lexing.lexeme lexbuf in - try - Hashtbl.find keyword_table s - with Not_found -> - IDENT s} -| ['A'-'Z'] [ '_' 'a'-'z' 'A'-'Z' '0'-'9']* - {let s = Lexing.lexeme lexbuf in - try - Hashtbl.find keyword_table s - with Not_found -> - UIDENT s} -| "->" {ARROW} -| "=>" {IMPL} -| "<=" {LTE} -| ">=" {GTE} -| "<>" {NEQ} -| '<' {LT} -| '>' {GT} -| "!=" {NEQ} -| '-' {MINUS} -| '+' {PLUS} -| '/' {DIV} -| '*' {MULT} -| '=' {EQ} -| '(' {LPAR} -| ')' {RPAR} -| '[' {LBRACKET} -| ']' {RBRACKET} -| '{' {LCUR} -| '}' {RCUR} -| ';' {SCOL} -| ':' {COL} -| ',' {COMMA} -| '=' {EQ} -| '/' {DIV} -| "&&" {AMPERAMPER} -| "||" {BARBAR} -| "::" {COLCOL} -| "^" {POWER} -| '"' {QUOTE} -| eof { EOF } -| _ { raise (Error (Location.curr lexbuf)) } + rule token = parse + | "--@" { Buffer.clear buf; + spec_singleline lexbuf } + | "(*@" { Buffer.clear buf; + spec_multiline 0 lexbuf } + | "--!" { Buffer.clear buf; + annot_singleline lexbuf } + | "(*!" { Buffer.clear buf; + annot_multiline 0 lexbuf } + | "(*" + { comment 0 lexbuf } + | "--" [^ '!' '@'] notnewline* (newline|eof) + { incr_line lexbuf; + token lexbuf } + | newline + { incr_line lexbuf; + token lexbuf } + | blank + + {token lexbuf} + + | ((['0'-'9']+ as l) '.' (['0'-'9']* as r) ('E'|'e') (('+'|'-')? ['0'-'9']+ as exp)) as s + {REAL (Num.num_of_string (l^r), String.length r + -1 * int_of_string exp , s)} + | ((['0'-'9']+ as l) '.' (['0'-'9']* as r)) as s + {REAL (Num.num_of_string (l^r), String.length r, s)} + | ['0'-'9']+ + {INT (int_of_string (Lexing.lexeme lexbuf)) } + | "tel." {TEL} + | "tel;" {TEL} + | "#open" { OPEN } + | ['_' 'a'-'z'] [ '_' 'a'-'z' 'A'-'Z' '0'-'9']* + {let s = Lexing.lexeme lexbuf in + try + Hashtbl.find keyword_table s + with Not_found -> + IDENT s} + | ['A'-'Z'] [ '_' 'a'-'z' 'A'-'Z' '0'-'9']* + {let s = Lexing.lexeme lexbuf in + try + Hashtbl.find keyword_table s + with Not_found -> + UIDENT s} + | "->" {ARROW} + | "=>" {IMPL} + | "<=" {LTE} + | ">=" {GTE} + | "<>" {NEQ} + | '<' {LT} + | '>' {GT} + | "!=" {NEQ} + | '-' {MINUS} + | '+' {PLUS} + | '/' {DIV} + | '*' {MULT} + | '=' {EQ} + | '(' {LPAR} + | ')' {RPAR} + | '[' {LBRACKET} + | ']' {RBRACKET} + | '{' {LCUR} + | '}' {RCUR} + | ';' {SCOL} + | ':' {COL} + | ',' {COMMA} + | '=' {EQ} + | '/' {DIV} + | "&&" {AMPERAMPER} + | "||" {BARBAR} + | "::" {COLCOL} + | "^" {POWER} + | '"' {QUOTE} + | eof { EOF } + | _ { raise (Error (Location.curr lexbuf)) } and comment n = parse -| eof - { raise (Error (Location.curr lexbuf)) } -| "(*" - { comment (n+1) lexbuf } -| "*)" - { if n > 0 then comment (n-1) lexbuf else token lexbuf } -| newline - { incr_line lexbuf; - comment n lexbuf } -| _ { comment n lexbuf } + | eof + { raise (Error (Location.curr lexbuf)) } + | "(*" + { comment (n+1) lexbuf } + | "*)" + { if n > 0 then comment (n-1) lexbuf else token lexbuf } + | newline + { incr_line lexbuf; + comment n lexbuf } + | _ { comment n lexbuf } and annot_singleline = parse - | newline { incr_line lexbuf; make_annot lexbuf (Buffer.contents buf) } - | _ as c { Buffer.add_char buf c; annot_singleline lexbuf } + | newline { incr_line lexbuf; make_annot lexbuf (Buffer.contents buf) } + | _ as c { Buffer.add_char buf c; annot_singleline lexbuf } and annot_multiline n = parse - | "*)" as s { - if n > 0 then - (Buffer.add_string buf s; annot_multiline (n-1) lexbuf) - else - make_annot lexbuf (Buffer.contents buf) } - | "(*" as s { Buffer.add_string buf s; annot_multiline (n+1) lexbuf } - | newline as s { incr_line lexbuf; Buffer.add_string buf s; annot_multiline n lexbuf } - | _ as c { Buffer.add_char buf c; annot_multiline n lexbuf } + | "*)" as s { + if n > 0 then + (Buffer.add_string buf s; annot_multiline (n-1) lexbuf) + else + make_annot lexbuf (Buffer.contents buf) } + | "(*" as s { Buffer.add_string buf s; annot_multiline (n+1) lexbuf } + | newline as s { incr_line lexbuf; Buffer.add_string buf s; annot_multiline n lexbuf } + | _ as c { Buffer.add_char buf c; annot_multiline n lexbuf } and spec_singleline = parse - | newline { incr_line lexbuf; make_spec lexbuf (Buffer.contents buf) } - | _ as c { Buffer.add_char buf c; spec_singleline lexbuf } + | newline { incr_line lexbuf; make_spec lexbuf (Buffer.contents buf) } + | _ as c { Buffer.add_char buf c; spec_singleline lexbuf } and spec_multiline n = parse - | "*)" as s { if n > 0 then - (Buffer.add_string buf s; spec_multiline (n-1) lexbuf) - else - make_spec lexbuf (Buffer.contents buf) } - | "(*" as s { Buffer.add_string buf s; spec_multiline (n+1) lexbuf } - | newline as s { incr_line lexbuf; Buffer.add_string buf s; spec_multiline n lexbuf } - | _ as c { Buffer.add_char buf c; spec_multiline n lexbuf } + | "*)" as s { if n > 0 then + (Buffer.add_string buf s; spec_multiline (n-1) lexbuf) + else + make_spec lexbuf (Buffer.contents buf) } + | "(*" as s { Buffer.add_string buf s; spec_multiline (n+1) lexbuf } + | newline as s { incr_line lexbuf; Buffer.add_string buf s; spec_multiline n lexbuf } + | _ as c { Buffer.add_char buf c; spec_multiline n lexbuf } diff --git a/src/liveness.ml b/src/liveness.ml index 21b716df..f754b4cc 100755 --- a/src/liveness.ml +++ b/src/liveness.ml @@ -115,7 +115,7 @@ let remove_roots ctx = (* checks whether a variable is aliasable, depending on its (address) type *) let is_aliasable var = - Types.is_address_type var.var_type + (not (!Options.mpfr && Types.is_real_type var.var_type)) && Types.is_address_type var.var_type (* checks whether a variable [v] is an input of the [var] equation, with an address type. if so, [var] could not safely reuse/alias [v], should [v] be dead in the caller node, @@ -127,11 +127,7 @@ let is_aliasable_input node var = | None -> [] | Some (_, args) -> List.fold_right (fun e r -> match e.expr_desc with Expr_ident id -> id::r | _ -> r) args [] in fun v -> is_aliasable v && List.mem v.var_id inputs_var -(* - let res = -is_aliasable v && List.mem v.var_id inputs_var - in (Format.eprintf "aliasable %s by %s = %B@." var v.var_id res; res) -*) + (* replace variable [v] by [v'] in graph [g]. [v'] is a dead variable *) @@ -207,10 +203,8 @@ let compute_reuse node ctx heads var = let disjoint_live = Disjunction.CISet.inter disjoint live in Log.report ~level:7 (fun fmt -> Format.fprintf fmt "disjoint live:%a@." Disjunction.pp_ciset disjoint_live); let reuse = Disjunction.CISet.max_elt disjoint_live in - (*let reuse' = Hashtbl.find ctx.policy reuse.var_id in*) begin IdentDepGraph.add_edge ctx.dep_graph var.var_id reuse.var_id; - (*if reuse != reuse' then IdentDepGraph.add_edge ctx.dep_graph reuse.var_id reuse'.var_id;*) Hashtbl.add ctx.policy var.var_id reuse; ctx.evaluated <- Disjunction.CISet.add var ctx.evaluated; (*Format.eprintf "%s reused by live@." var.var_id;*) @@ -220,13 +214,11 @@ let compute_reuse node ctx heads var = let dead = Disjunction.CISet.filter (fun v -> is_graph_root v.var_id ctx.dep_graph) quasi_dead in Log.report ~level:7 (fun fmt -> Format.fprintf fmt "dead:%a@." Disjunction.pp_ciset dead); let reuse = Disjunction.CISet.choose dead in - (*let reuse' = Hashtbl.find ctx.policy reuse.var_id in*) begin IdentDepGraph.add_edge ctx.dep_graph var.var_id reuse.var_id; - (*if reuse != reuse' then IdentDepGraph.add_edge ctx.dep_graph reuse.var_id reuse'.var_id;*) Hashtbl.add ctx.policy var.var_id reuse; ctx.evaluated <- Disjunction.CISet.add var ctx.evaluated; - (*Format.eprintf "%s reused by dead %a@." var.var_id Disjunction.pp_ciset dead;*) + (*Format.eprintf "%s reused by dead %s@." var.var_id reuse.var_id;*) end with Not_found -> begin diff --git a/src/lustreSpec.ml b/src/lustreSpec.ml index 4202fe41..ae2524cd 100644 --- a/src/lustreSpec.ml +++ b/src/lustreSpec.ml @@ -24,7 +24,7 @@ and type_dec_desc = | Tydec_any | Tydec_int | Tydec_real - | Tydec_float + (* | Tydec_float *) | Tydec_bool | Tydec_clock of type_dec_desc | Tydec_const of ident @@ -50,8 +50,8 @@ and clock_dec_desc = type constant = | Const_int of int - | Const_real of string - | Const_float of float + | Const_real of Num.num * int * string (* (a,b, c) means a * 10^-b. c is the original string *) + (* | Const_float of float *) | Const_array of constant list | Const_tag of label | Const_string of string (* used only for annotations *) @@ -75,8 +75,6 @@ type var_decl = is mutable (and initialized to dummy values). This avoids to have to duplicate ast structures (e.g. ast, typed_ast, clocked_ast). *) - - (* The tag of an expression is a unique identifier used to distinguish different instances of the same node *) and expr = @@ -188,7 +186,7 @@ type imported_node_desc = nodei_stateless: bool; nodei_spec: node_annot option; nodei_prototype: string option; - nodei_in_lib: string option; + nodei_in_lib: string list; } type const_desc = @@ -220,6 +218,33 @@ type dep_t = Dep of * (top_decl list) * bool (* is stateful *) + +(************ Machine code types *************) + +type value_t = + { + value_desc: value_t_desc; + value_type: Types.type_expr; + value_annot: expr_annot option + } +and value_t_desc = + | Cst of constant + | LocalVar of var_decl + | StateVar of var_decl + | Fun of ident * value_t list + | Array of value_t list + | Access of value_t * value_t + | Power of value_t * value_t + +type instr_t = + | MLocalAssign of var_decl * value_t + | MStateAssign of var_decl * value_t + | MReset of ident + | MStep of var_decl list * ident * value_t list + | MBranch of value_t * (label * instr_t list) list + | MComment of string + + type error = Main_not_found | Main_wrong_kind diff --git a/src/machine_code.ml b/src/machine_code.ml index cd1fc9d7..e969e160 100644 --- a/src/machine_code.ml +++ b/src/machine_code.ml @@ -19,27 +19,13 @@ exception NormalizationError module OrdVarDecl:Map.OrderedType with type t=var_decl = struct type t = var_decl;; let compare = compare end -module ISet = Set.Make(OrdVarDecl) -type value_t = - | Cst of constant - | LocalVar of var_decl - | StateVar of var_decl - | Fun of ident * value_t list - | Array of value_t list - | Access of value_t * value_t - | Power of value_t * value_t - -type instr_t = - | MLocalAssign of var_decl * value_t - | MStateAssign of var_decl * value_t - | MReset of ident - | MStep of var_decl list * ident * value_t list - | MBranch of value_t * (label * instr_t list) list +module ISet = Set.Make(OrdVarDecl) + let rec pp_val fmt v = - match v with - | Cst c -> Printers.pp_const fmt c + match v.value_desc with + | Cst c -> Printers.pp_const fmt c | LocalVar v -> Format.pp_print_string fmt v.var_id | StateVar v -> Format.pp_print_string fmt v.var_id | Array vl -> Format.fprintf fmt "[%a]" (Utils.fprintf_list ~sep:", " pp_val) vl @@ -48,19 +34,20 @@ let rec pp_val fmt v = | Fun (n, vl) -> Format.fprintf fmt "%s (%a)" n (Utils.fprintf_list ~sep:", " pp_val) vl let rec pp_instr fmt i = - match i with + match i with | MLocalAssign (i,v) -> Format.fprintf fmt "%s<-l- %a" i.var_id pp_val v | MStateAssign (i,v) -> Format.fprintf fmt "%s<-s- %a" i.var_id pp_val v | MReset i -> Format.fprintf fmt "reset %s" i | MStep (il, i, vl) -> Format.fprintf fmt "%a = %s (%a)" (Utils.fprintf_list ~sep:", " (fun fmt v -> Format.pp_print_string fmt v.var_id)) il - i + i (Utils.fprintf_list ~sep:", " pp_val) vl | MBranch (g,hl) -> Format.fprintf fmt "@[<v 2>case(%a) {@,%a@,}@]" pp_val g (Utils.fprintf_list ~sep:"@," pp_branch) hl + | MComment s -> Format.pp_print_string fmt s and pp_branch fmt (t, h) = Format.fprintf fmt "@[<v 2>%s:@,%a@]" t (Utils.fprintf_list ~sep:"@," pp_instr) h @@ -92,6 +79,7 @@ type machine_t = { mannot: expr_annot list; } +let machine_vars m = m.mstep.step_inputs @ m.mstep.step_locals @ m.mstep.step_outputs @ m.mmemory let pp_step fmt s = Format.fprintf fmt "@[<v>inputs : %a@ outputs: %a@ locals : %a@ checks : %a@ instrs : @[%a@]@ asserts : @[%a@]@]@ " (Utils.fprintf_list ~sep:", " Printers.pp_var) s.step_inputs @@ -119,6 +107,12 @@ let pp_machine fmt m = (fun fmt -> match m.mspec with | None -> () | Some spec -> Printers.pp_spec fmt spec) (Utils.fprintf_list ~sep:"@ " Printers.pp_expr_annot) m.mannot +let rec is_const_value v = + match v.value_desc with + | Cst _ -> true + | Fun (id, args) -> Basic_library.is_value_internal_fun v && List.for_all is_const_value args + | _ -> false + (* 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) @@ -177,18 +171,24 @@ let arrow_top_decl = top_decl_loc = Location.dummy_loc } +let mk_val v t = { value_desc = v; + value_type = t; + value_annot = None } + let arrow_machine = let state = "_first" in let var_state = dummy_var_decl state (Types.new_ty Types.Tbool) in let var_input1 = List.nth arrow_desc.node_inputs 0 in let var_input2 = List.nth arrow_desc.node_inputs 1 in let var_output = List.nth arrow_desc.node_outputs 0 in + let cst b = mk_val (Cst (const_of_bool b)) Type_predef.type_bool in + let t_arg = Types.new_univar () in (* TODO Xavier: c'est bien la bonne def ? *) { mname = arrow_desc; mmemory = [var_state]; mcalls = []; minstances = []; - minit = [MStateAssign(var_state, Cst (const_of_bool true))]; + minit = [MStateAssign(var_state, cst true)]; mconst = []; mstatic = []; mstep = { @@ -196,10 +196,10 @@ let arrow_machine = step_outputs = arrow_desc.node_outputs; step_locals = []; step_checks = []; - step_instrs = [conditional (StateVar var_state) - [MStateAssign(var_state, Cst (const_of_bool false)); - MLocalAssign(var_output, LocalVar var_input1)] - [MLocalAssign(var_output, LocalVar var_input2)] ]; + step_instrs = [conditional (mk_val (StateVar var_state) Type_predef.type_bool) + [MStateAssign(var_state, cst false); + MLocalAssign(var_output, mk_val (LocalVar var_input1) t_arg)] + [MLocalAssign(var_output, mk_val (LocalVar var_input2) t_arg)] ]; step_asserts = []; }; mspec = None; @@ -222,7 +222,6 @@ let new_instance = o end - (* translate_<foo> : node -> context -> <foo> -> machine code/expression *) (* the context contains m : state aka memory variables *) (* si : initialization instructions *) @@ -233,15 +232,19 @@ let translate_ident node (m, si, j, d, s) id = try (* id is a node var *) let var_id = get_node_var id node in if ISet.exists (fun v -> v.var_id = id) m - then StateVar var_id - else LocalVar var_id + then mk_val (StateVar var_id) var_id.var_type + else mk_val (LocalVar var_id) var_id.var_type with Not_found -> try (* id is a constant *) - LocalVar (Corelang.var_decl_of_const (const_of_top (Hashtbl.find Corelang.consts_table id))) + let vdecl = (Corelang.var_decl_of_const (const_of_top (Hashtbl.find Corelang.consts_table id))) in + mk_val (LocalVar vdecl) vdecl.var_type with Not_found -> (* id is a tag *) - Cst (Const_tag id) - + (* TODO construire une liste des enum declarés et alors chercher dedans la liste + qui contient id *) + let cst = Const_tag id in + mk_val (Cst cst) (Typing.type_const Location.dummy_loc cst) + let rec control_on_clock node ((m, si, j, d, s) as args) ck inst = match (Clocks.repr ck).cdesc with | Con (ck1, cr, l) -> @@ -272,7 +275,7 @@ let join_guards_list insts = List.fold_right join_guards insts [] (* specialize predefined (polymorphic) operators - wrt their instances, so that the C semantics + wrt their instances, so that the C semantics is preserved *) let specialize_to_c expr = match expr.expr_desc with @@ -292,36 +295,36 @@ let specialize_op expr = | "C" -> specialize_to_c expr | _ -> expr -let rec translate_expr ?(ite=false) node ((m, si, j, d, s) as args) expr = +let rec translate_expr node ((m, si, j, d, s) as args) expr = let expr = specialize_op expr in - match expr.expr_desc with - | Expr_const v -> Cst v - | Expr_ident x -> translate_ident node args x - | Expr_array el -> Array (List.map (translate_expr node args) el) - | Expr_access (t, i) -> Access (translate_expr node args t, translate_expr node args (expr_of_dimension i)) - | Expr_power (e, n) -> Power (translate_expr node args e, translate_expr node args (expr_of_dimension n)) - | Expr_tuple _ - | Expr_arrow _ - | Expr_fby _ - | Expr_pre _ -> (Printers.pp_expr Format.err_formatter expr; Format.pp_print_flush Format.err_formatter (); raise NormalizationError) - | Expr_when (e1, _, _) -> translate_expr node args e1 - | Expr_merge (x, _) -> raise NormalizationError - | Expr_appl (id, e, _) when Basic_library.is_internal_fun id -> - let nd = node_from_name id in - Fun (node_name nd, List.map (translate_expr node args) (expr_list_of_expr e)) - | Expr_ite (g,t,e) -> ( - (* special treatment depending on the active backend. For horn backend, ite - are preserved in expression. While they are removed for C or Java - backends. *) - match !Options.output with - | "horn" -> - Fun ("ite", [translate_expr node args g; translate_expr node args t; translate_expr node args e]) - | ("C" | "java") when ite -> - Fun ("ite", [translate_expr node args g; translate_expr node args t; translate_expr node args e]) - | _ -> - (Format.eprintf "option:%s@." !Options.output; Printers.pp_expr Format.err_formatter expr; Format.pp_print_flush Format.err_formatter (); raise NormalizationError) - ) - | _ -> raise NormalizationError + let value_desc = + match expr.expr_desc with + | Expr_const v -> Cst v + | Expr_ident x -> (translate_ident node args x).value_desc + | Expr_array el -> Array (List.map (translate_expr node args) el) + | Expr_access (t, i) -> Access (translate_expr node args t, translate_expr node args (expr_of_dimension i)) + | Expr_power (e, n) -> Power (translate_expr node args e, translate_expr node args (expr_of_dimension n)) + | Expr_tuple _ + | Expr_arrow _ + | Expr_fby _ + | Expr_pre _ -> (Printers.pp_expr Format.err_formatter expr; Format.pp_print_flush Format.err_formatter (); raise NormalizationError) + | Expr_when (e1, _, _) -> (translate_expr node args e1).value_desc + | Expr_merge (x, _) -> raise NormalizationError + | Expr_appl (id, e, _) when Basic_library.is_expr_internal_fun expr -> + let nd = node_from_name id in + Fun (node_name nd, List.map (translate_expr node args) (expr_list_of_expr e)) + | Expr_ite (g,t,e) -> ( + (* special treatment depending on the active backend. For horn backend, ite + are preserved in expression. While they are removed for C or Java + backends. *) + match !Options.output with | "horn" -> + Fun ("ite", [translate_expr node args g; translate_expr node args t; translate_expr node args e]) + | "C" | "java" | _ -> + (Printers.pp_expr Format.err_formatter expr; Format.pp_print_flush Format.err_formatter (); raise NormalizationError) + ) + | _ -> raise NormalizationError + in + mk_val value_desc expr.expr_type let translate_guard node args expr = match expr.expr_desc with @@ -334,8 +337,7 @@ let rec translate_act node ((m, si, j, d, s) as args) (y, expr) = conditional g [translate_act node args (y, t)] [translate_act node args (y, e)] | Expr_merge (x, hl) -> MBranch (translate_ident node args x, List.map (fun (t, h) -> t, [translate_act node args (y, h)]) hl) - | _ -> - MLocalAssign (y, translate_expr node args expr) + | _ -> MLocalAssign (y, translate_expr node args expr) let reset_instance node args i r c = match r with @@ -344,7 +346,7 @@ let reset_instance node args i r c = [control_on_clock node args c (conditional g [MReset i] [])] let translate_eq node ((m, si, j, d, s) as args) eq = - (* Format.eprintf "translate_eq %a with clock %a@." Printers.pp_node_eq eq Clocks.print_ck eq.eq_rhs.expr_clock; *) + (*Format.eprintf "translate_eq %a with clock %a@." Printers.pp_node_eq eq Clocks.print_ck eq.eq_rhs.expr_clock;*) match eq.eq_lhs, eq.eq_rhs.expr_desc with | [x], Expr_arrow (e1, e2) -> let var_x = get_node_var x node in @@ -371,14 +373,14 @@ let translate_eq node ((m, si, j, d, s) as args) eq = d, control_on_clock node args eq.eq_rhs.expr_clock (MStateAssign (var_x, translate_expr node args e2)) :: s) - | p , Expr_appl (f, arg, r) when not (Basic_library.is_internal_fun f) -> + | p , Expr_appl (f, arg, r) when not (Basic_library.is_expr_internal_fun eq.eq_rhs) -> let var_p = List.map (fun v -> get_node_var v node) p in let el = expr_list_of_expr arg in let vl = List.map (translate_expr node args) el in let node_f = node_from_name f in let call_f = node_f, - NodeDep.filter_static_inputs (node_inputs node_f) el in + NodeDep.filter_static_inputs (node_inputs node_f) el in let o = new_instance node node_f eq.eq_rhs.expr_tag in let env_cks = List.fold_right (fun arg cks -> arg.expr_clock :: cks) el [eq.eq_rhs.expr_clock] in let call_ck = Clock_calculus.compute_root_clock (Clock_predef.ck_tuple env_cks) in @@ -397,22 +399,22 @@ let translate_eq node ((m, si, j, d, s) as args) eq = (* special treatment depending on the active backend. For horn backend, x = ite (g,t,e) are preserved. While they are replaced as if g then x = t else x = e in C or Java backends. *) - | [x], Expr_ite (c, t, e) + | [x], Expr_ite (c, t, e) when (match !Options.output with | "horn" -> true | "C" | "java" | _ -> false) - -> + -> let var_x = get_node_var x node in - (m, - si, - j, - d, - (control_on_clock node args eq.eq_rhs.expr_clock + (m, + si, + j, + d, + (control_on_clock node args eq.eq_rhs.expr_clock (MLocalAssign (var_x, translate_expr node args eq.eq_rhs))::s) ) - + | [x], _ -> ( let var_x = get_node_var x node in - (m, si, j, d, - control_on_clock + (m, si, j, d, + control_on_clock node args eq.eq_rhs.expr_clock @@ -421,7 +423,7 @@ let translate_eq node ((m, si, j, d, s) as args) eq = ) | _ -> begin - Format.eprintf "unsupported equation: %a@?" Printers.pp_node_eq eq; + Format.eprintf "internal error: Machine_code.translate_eq %a@?" Printers.pp_node_eq eq; assert false end @@ -436,22 +438,30 @@ let find_eq xl eqs = assert false end | hd::tl -> - if List.exists (fun x -> List.mem x hd.eq_lhs) xl then hd, accu@tl else aux (hd::accu) tl + if List.exists (fun x -> List.mem x hd.eq_lhs) xl then + hd, accu@tl + else + aux (hd::accu) tl in aux [] eqs -(* Sort the set of equations of node [nd] according +(* Sort the set of equations of node [nd] according to the computed schedule [sch] *) let sort_equations_from_schedule nd sch = -(*Format.eprintf "%s schedule: %a@." - nd.node_id - (Utils.fprintf_list ~sep:" ; " Scheduling.pp_eq_schedule) sch;*) + (* Format.eprintf "%s schedule: %a@." *) + (* nd.node_id *) + (* (Utils.fprintf_list ~sep:" ; " Scheduling.pp_eq_schedule) sch; *) let split_eqs = Splitting.tuple_split_eq_list (get_node_eqs nd) in let eqs_rev, remainder = List.fold_left (fun (accu, node_eqs_remainder) vl -> - if List.exists (fun eq -> List.exists (fun v -> List.mem v eq.eq_lhs) vl) accu + if List.exists + (fun eq -> (* This could be also evaluated with a forall. + But, by construction, each vl should be + associated to a single equation *) + List.exists (fun v -> List.mem v eq.eq_lhs) vl) + accu then (accu, node_eqs_remainder) else @@ -467,7 +477,10 @@ let sort_equations_from_schedule nd sch = Printers.pp_node_eqs remainder Printers.pp_node_eqs (get_node_eqs nd); assert false); - List.rev eqs_rev + let res = List.rev eqs_rev in + (* (\* Debug code, to be removed *\) *) + (* List.iteri (fun cpt eq -> Format.eprintf "Eq %i: %a@." cpt (Utils.fprintf_list ~sep:", " Format.pp_print_string) eq.eq_lhs) res; *) + res end let constant_equations nd = @@ -519,7 +532,7 @@ let translate_decl nd sch = | "horn" -> s | "C" | "java" | _ -> join_guards_list s ); - step_asserts = + step_asserts = let exprl = List.map (fun assert_ -> assert_.assert_expr ) nd.node_asserts in List.map (translate_expr nd init_args) exprl ; @@ -530,19 +543,19 @@ let translate_decl nd sch = (** takes the global declarations and the scheduling associated to each node *) let translate_prog decls node_schs = - let nodes = get_nodes decls in - List.map - (fun decl -> + let nodes = get_nodes decls in + List.map + (fun decl -> let node = node_of_top decl in let sch = (Utils.IMap.find node.node_id node_schs).Scheduling.schedule in - translate_decl node sch + translate_decl node sch ) nodes -let get_machine_opt name machines = - List.fold_left - (fun res m -> - match res with - | Some _ -> res +let get_machine_opt name machines = + List.fold_left + (fun res m -> + match res with + | Some _ -> res | None -> if m.mname.node_id = name then Some m else None) None machines @@ -557,29 +570,35 @@ let get_const_assign m id = let value_of_ident m id = (* is is a state var *) try - StateVar (List.find (fun v -> v.var_id = id) m.mmemory) + let mem = List.find (fun v -> v.var_id = id) m.mmemory in + mk_val (StateVar mem) mem.var_type with Not_found -> try (* id is a node var *) - LocalVar (get_node_var id m.mname) + let var = get_node_var id m.mname in + mk_val (LocalVar var) var.var_type with Not_found -> try (* id is a constant *) - LocalVar (Corelang.var_decl_of_const (const_of_top (Hashtbl.find Corelang.consts_table id))) + let cst = Corelang.var_decl_of_const (const_of_top (Hashtbl.find Corelang.consts_table id)) in + mk_val (LocalVar cst) cst.var_type with Not_found -> (* id is a tag *) - Cst (Const_tag id) + let tag = Const_tag id in + mk_val (Cst tag) (Typing.type_const Location.dummy_loc tag) let rec value_of_dimension m dim = match dim.Dimension.dim_desc with - | Dimension.Dbool b -> Cst (Const_tag (if b then Corelang.tag_true else Corelang.tag_false)) - | Dimension.Dint i -> Cst (Const_int i) + | Dimension.Dbool b -> mk_val (Cst (Const_tag (if b then Corelang.tag_true else Corelang.tag_false))) Type_predef.type_bool + | Dimension.Dint i -> mk_val (Cst (Const_int i)) Type_predef.type_int | Dimension.Dident v -> value_of_ident m v - | Dimension.Dappl (f, args) -> Fun (f, List.map (value_of_dimension m) args) - | Dimension.Dite (i, t, e) -> Fun ("ite", List.map (value_of_dimension m) [i; t; e]) + | Dimension.Dappl (f, args) -> let typ = if Basic_library.is_numeric_operator f then Type_predef.type_int else Type_predef.type_bool + in mk_val (Fun (f, List.map (value_of_dimension m) args)) typ + | Dimension.Dite (i, t, e) -> let [vi; vt; ve] = List.map (value_of_dimension m) [i; t; e] in + mk_val (Fun ("ite", [vi; vt; ve])) vt.value_type | Dimension.Dlink dim' -> value_of_dimension m dim' | _ -> assert false let rec dimension_of_value value = - match value with + match value.value_desc with | Cst (Const_tag t) when t = Corelang.tag_true -> Dimension.mkdim_bool Location.dummy_loc true | Cst (Const_tag t) when t = Corelang.tag_false -> Dimension.mkdim_bool Location.dummy_loc false | Cst (Const_int i) -> Dimension.mkdim_int Location.dummy_loc i diff --git a/src/main_lustre_compiler.ml b/src/main_lustre_compiler.ml index 5e84c33e..5bc03a73 100644 --- a/src/main_lustre_compiler.ml +++ b/src/main_lustre_compiler.ml @@ -1,371 +1,465 @@ -(********************************************************************) -(* *) -(* The LustreC compiler toolset / The LustreC Development Team *) -(* Copyright 2012 - -- ONERA - CNRS - INPT *) -(* *) -(* LustreC is free software, distributed WITHOUT ANY WARRANTY *) -(* under the terms of the GNU Lesser General Public License *) -(* version 2.1. *) -(* *) -(********************************************************************) - -open Format -open Log - -open Utils -open LustreSpec -open Compiler_common - -let usage = "Usage: lustrec [options] <source-file>" - -let extensions = [".ec"; ".lus"; ".lusi"] - -(* print a .lusi header file from a source prog *) -let print_lusi prog dirname basename extension = - let header = Lusic.extract_header dirname basename prog in - let header_name = dirname ^ "/" ^ basename ^ extension in - let h_out = open_out header_name in - let h_fmt = formatter_of_out_channel h_out in - begin - Typing.uneval_prog_generics header; - Clock_calculus.uneval_prog_generics header; - Printers.pp_lusi_header h_fmt basename header; - close_out h_out - end - -(* compile a .lusi header file *) -let compile_header dirname basename extension = - let destname = !Options.dest_dir ^ "/" ^ basename in - let header_name = basename ^ extension in - let lusic_ext = extension ^ "c" in - begin - Log.report ~level:1 (fun fmt -> fprintf fmt "@[<v>"); - let header = parse_header true (dirname ^ "/" ^ header_name) in - ignore (Modules.load_header ISet.empty header); - ignore (check_top_decls header); - create_dest_dir (); - Log.report ~level:1 - (fun fmt -> fprintf fmt ".. generating compiled header file %sc@," (destname ^ extension)); - Lusic.write_lusic true header destname lusic_ext; - Lusic.print_lusic_to_h destname lusic_ext; - Log.report ~level:1 (fun fmt -> fprintf fmt ".. done !@ @]@.") - end - -(* check whether a source file has a compiled header, - if not, generate the compiled header *) -let compile_source_to_header prog computed_types_env computed_clocks_env dirname basename extension = - let destname = !Options.dest_dir ^ "/" ^ basename in - let lusic_ext = extension ^ "c" in - let header_name = destname ^ lusic_ext in - begin - if not (Sys.file_exists header_name) then - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. generating compiled header file %s@," header_name); - Lusic.write_lusic false (Lusic.extract_header dirname basename prog) destname lusic_ext; - Lusic.print_lusic_to_h destname lusic_ext - end - else - let lusic = Lusic.read_lusic destname lusic_ext in - if not lusic.Lusic.from_lusi then - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. generating compiled header file %s@," header_name); - Lusic.write_lusic false (Lusic.extract_header dirname basename prog) destname lusic_ext; - (*List.iter (fun top_decl -> Format.eprintf "lusic: %a@." Printers.pp_decl top_decl) lusic.Lusic.contents;*) - Lusic.print_lusic_to_h destname lusic_ext - end - else - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. loading compiled header file %s@," header_name); - Modules.check_dependency lusic destname; - let header = lusic.Lusic.contents in - let (declared_types_env, declared_clocks_env) = get_envs_from_top_decls header in - check_compatibility - (prog, computed_types_env, computed_clocks_env) - (header, declared_types_env, declared_clocks_env) - end - end - - - -(* compile a .lus source file *) -let rec compile_source dirname basename extension = - - Log.report ~level:1 (fun fmt -> fprintf fmt "@[<v>"); - - (* Parsing source *) - let prog = parse_source (dirname ^ "/" ^ basename ^ extension) in - - (* Removing automata *) - let prog = Automata.expand_decls prog in - - (* Importing source *) - let _ = Modules.load_program ISet.empty prog in - - (* Extracting dependencies *) - let dependencies, type_env, clock_env = import_dependencies prog in - - (* Sorting nodes *) - let prog = SortProg.sort prog in - - (* Perform inlining before any analysis *) - let orig, prog = - if !Options.global_inline && !Options.main_node <> "" then - (if !Options.witnesses then prog else []), - Inliner.global_inline basename prog type_env clock_env - else (* if !Option.has_local_inline *) - [], - Inliner.local_inline basename prog type_env clock_env - in - - (* Checking stateless/stateful status *) - check_stateless_decls prog; - - (* Typing *) - let computed_types_env = type_decls type_env prog in - - (* Clock calculus *) - let computed_clocks_env = clock_decls clock_env prog in - - (* Generating a .lusi header file only *) - if !Options.lusi then - begin - let lusi_ext = extension ^ "i" in - Log.report ~level:1 (fun fmt -> fprintf fmt ".. generating interface file %s@," (dirname ^ "/" ^ basename ^ lusi_ext)); - print_lusi prog dirname basename lusi_ext; - Log.report ~level:1 (fun fmt -> fprintf fmt ".. done !@ @]@."); - exit 0 - end; - - (* Delay calculus *) - (* TO BE DONE LATER (Xavier) - if(!Options.delay_calculus) - then - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. initialisation analysis@?"); - try - Delay_calculus.delay_prog Basic_library.delay_env prog - with (Delay.Error (loc,err)) as exc -> - Location.print loc; - eprintf "%a" Delay.pp_error err; - Utils.track_exception (); - raise exc - end; - *) - - (* Creating destination directory if needed *) - create_dest_dir (); - - (* Compatibility with Lusi *) - (* Checking the existence of a lusi (Lustre Interface file) *) - (match !Options.output with - "C" -> - begin - let extension = ".lusi" in - compile_source_to_header prog computed_types_env computed_clocks_env dirname basename extension; - end - |_ -> ()); - - Typing.uneval_prog_generics prog; - Clock_calculus.uneval_prog_generics prog; - - if !Options.global_inline && !Options.main_node <> "" && !Options.witnesses then - begin - let orig = Corelang.copy_prog orig in - Log.report ~level:1 (fun fmt -> fprintf fmt ".. generating witness file@,"); - check_stateless_decls orig; - let _ = Typing.type_prog type_env orig in - let _ = Clock_calculus.clock_prog clock_env orig in - Typing.uneval_prog_generics orig; - Clock_calculus.uneval_prog_generics orig; - Inliner.witness - basename - !Options.main_node - orig prog type_env clock_env - end; - -(*Format.eprintf "Inliner.global_inline<<@.%a@.>>@." Printers.pp_prog prog;*) - (* Computes and stores generic calls for each node, - only useful for ANSI C90 compliant generic node compilation *) - if !Options.ansi then Causality.NodeDep.compute_generic_calls prog; - (*Hashtbl.iter (fun id td -> match td.Corelang.top_decl_desc with Corelang.Node nd -> Format.eprintf "%s calls %a" id Causality.NodeDep.pp_generic_calls nd | _ -> ()) Corelang.node_table;*) - - (* Normalization phase *) - Log.report ~level:1 (fun fmt -> fprintf fmt ".. normalization@,"); - (* Special treatment of arrows in lustre backend. We want to keep them *) - if !Options.output = "lustre" then - Normalization.unfold_arrow_active := false; - let prog = Normalization.normalize_prog prog in - - Log.report ~level:2 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Printers.pp_prog prog); - (* Checking array accesses *) - if !Options.check then - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. array access checks@,"); - Access.check_prog prog; - end; - - (* 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@,"); - let prog, node_schs = Scheduling.schedule_prog prog in - Log.report ~level:1 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Scheduling.pp_warning_unused node_schs); - Log.report ~level:3 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Scheduling.pp_schedule node_schs); - Log.report ~level:3 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Scheduling.pp_fanin_table node_schs); - Log.report ~level:3 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Printers.pp_prog prog); - - (* Optimization of prog: - - Unfold consts - - eliminate trivial expressions - *) - let prog = - if !Options.optimization >= 5 then - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. constants elimination@,"); - Optimize_prog.prog_unfold_consts prog - end - else - prog - in - (* DFS with modular code generation *) - Log.report ~level:1 (fun fmt -> fprintf fmt ".. machines generation@,"); - let machine_code = Machine_code.translate_prog prog node_schs in - - Log.report ~level:2 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," - (Utils.fprintf_list ~sep:"@ " Machine_code.pp_machine) - machine_code); - - (* Optimize machine code *) - let machine_code = - if !Options.optimization >= 4 && !Options.output <> "horn" then - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. machines optimization (phase 3)@,"); - Optimize_machine.machines_cse machine_code - end - else - machine_code - in - - (* Optimize machine code *) - let machine_code = - if !Options.optimization >= 2 && !Options.output <> "horn" then - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. machines optimization (phase 1)@,"); - Optimize_machine.machines_unfold (Corelang.get_consts prog) node_schs machine_code - end - else - machine_code - in - (* Optimize machine code *) - let machine_code = - if !Options.optimization >= 3 && !Options.output <> "horn" then - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. machines optimization (phase 2)@,"); - Optimize_machine.machines_fusion (Optimize_machine.machines_reuse_variables machine_code node_schs) - end - else - machine_code - in - - if !Options.optimization >= 2 then - begin - Log.report ~level:3 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," - (Utils.fprintf_list ~sep:"@ " Machine_code.pp_machine) - machine_code); - end; - - (* Printing code *) - let basename = Filename.basename basename in - let destname = !Options.dest_dir ^ "/" ^ basename in - let _ = match !Options.output with - | "C" -> - begin - let alloc_header_file = destname ^ "_alloc.h" in (* Could be changed *) - let source_lib_file = destname ^ ".c" in (* Could be changed *) - let source_main_file = destname ^ "_main.c" in (* Could be changed *) - let makefile_file = destname ^ ".makefile" in (* Could be changed *) - Log.report ~level:1 (fun fmt -> fprintf fmt ".. C code generation@,"); - C_backend.translate_to_c - alloc_header_file source_lib_file source_main_file makefile_file - basename prog machine_code dependencies - end - | "java" -> - begin - failwith "Sorry, but not yet supported !" - (*let source_file = basename ^ ".java" in - Log.report ~level:1 (fun fmt -> fprintf fmt ".. opening file %s@,@?" source_file); - let source_out = open_out source_file in - let source_fmt = formatter_of_out_channel source_out in - Log.report ~level:1 (fun fmt -> fprintf fmt ".. java code generation@,@?"); - Java_backend.translate_to_java source_fmt basename normalized_prog machine_code;*) - end - | "horn" -> - begin - let source_file = destname ^ ".smt2" in (* Could be changed *) - let source_out = open_out source_file in - let fmt = formatter_of_out_channel source_out in - Log.report ~level:1 (fun fmt -> fprintf fmt ".. hornification@,"); - Horn_backend.translate fmt basename prog machine_code; - (* Tracability file if option is activated *) - if !Options.traces then ( - let traces_file = destname ^ ".traces.xml" in (* Could be changed *) - let traces_out = open_out traces_file in - let fmt = formatter_of_out_channel traces_out in - Log.report ~level:1 (fun fmt -> fprintf fmt ".. tracing info@,"); - Horn_backend.traces_file fmt basename prog machine_code; - ) - end - | "lustre" -> - begin - let source_file = destname ^ ".lustrec.lus" in (* Could be changed *) - let source_out = open_out source_file in - let fmt = formatter_of_out_channel source_out in - Printers.pp_prog fmt prog; -(* Lustre_backend.translate fmt basename normalized_prog machine_code *) - () - end - - | _ -> assert false - in - begin - Log.report ~level:1 (fun fmt -> fprintf fmt ".. done @ @]@."); - (* We stop the process here *) - exit 0 - end - -let compile dirname basename extension = - match extension with - | ".lusi" -> compile_header dirname basename extension - | ".lus" -> compile_source dirname basename extension - | _ -> assert false - -let anonymous filename = - let ok_ext, ext = List.fold_left - (fun (ok, ext) ext' -> - if not ok && Filename.check_suffix filename ext' then - true, ext' - else - ok, ext) - (false, "") extensions in - if ok_ext then - let dirname = Filename.dirname filename in - let basename = Filename.chop_suffix (Filename.basename filename) ext in - compile dirname basename ext - else - raise (Arg.Bad ("Can only compile *.lusi, *.lus or *.ec files")) - -let _ = - Corelang.add_internal_funs (); - try - Printexc.record_backtrace true; - Arg.parse Options.options anonymous usage - with - | Parse.Syntax_err _ | Lexer_lustre.Error _ - | Types.Error (_,_) | Clocks.Error (_,_) - | Corelang.Error _ (*| Task_set.Error _*) - | Causality.Cycle _ -> exit 1 - | Sys_error msg -> (eprintf "Failure: %s@." msg) - | exc -> (Utils.track_exception (); raise exc) - -(* Local Variables: *) -(* compile-command:"make -C .." *) -(* End: *) +(********************************************************************) +(* *) +(* The LustreC compiler toolset / The LustreC Development Team *) +(* Copyright 2012 - -- ONERA - CNRS - INPT *) +(* *) +(* LustreC is free software, distributed WITHOUT ANY WARRANTY *) +(* under the terms of the GNU Lesser General Public License *) +(* version 2.1. *) +(* *) +(********************************************************************) + +open Format +open Log + +open Utils +open LustreSpec +open Compiler_common + +exception StopPhase1 of program + +let usage = "Usage: lustrec [options] \x1b[4msource file\x1b[0m" + +let extensions = [".ec"; ".lus"; ".lusi"] + +(* print a .lusi header file from a source prog *) +let print_lusi prog dirname basename extension = + let header = Lusic.extract_header dirname basename prog in + let header_name = dirname ^ "/" ^ basename ^ extension in + let h_out = open_out header_name in + let h_fmt = formatter_of_out_channel h_out in + begin + Typing.uneval_prog_generics header; + Clock_calculus.uneval_prog_generics header; + Printers.pp_lusi_header h_fmt basename header; + close_out h_out + end + +(* compile a .lusi header file *) +let compile_header dirname basename extension = + let destname = !Options.dest_dir ^ "/" ^ basename in + let header_name = basename ^ extension in + let lusic_ext = extension ^ "c" in + begin + Log.report ~level:1 (fun fmt -> fprintf fmt "@[<v>"); + let header = parse_header true (dirname ^ "/" ^ header_name) in + ignore (Modules.load_header ISet.empty header); + ignore (check_top_decls header); + create_dest_dir (); + Log.report ~level:1 + (fun fmt -> fprintf fmt ".. generating compiled header file %sc@," (destname ^ extension)); + Lusic.write_lusic true header destname lusic_ext; + Lusic.print_lusic_to_h destname lusic_ext; + Log.report ~level:1 (fun fmt -> fprintf fmt ".. done !@ @]@.") + end + +(* check whether a source file has a compiled header, + if not, generate the compiled header *) +let compile_source_to_header prog computed_types_env computed_clocks_env dirname basename extension = + let destname = !Options.dest_dir ^ "/" ^ basename in + let lusic_ext = extension ^ "c" in + let header_name = destname ^ lusic_ext in + begin + if not (Sys.file_exists header_name) then + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. generating compiled header file %s@," header_name); + Lusic.write_lusic false (Lusic.extract_header dirname basename prog) destname lusic_ext; + Lusic.print_lusic_to_h destname lusic_ext + end + else + let lusic = Lusic.read_lusic destname lusic_ext in + if not lusic.Lusic.from_lusi then + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. generating compiled header file %s@," header_name); + Lusic.write_lusic false (Lusic.extract_header dirname basename prog) destname lusic_ext; +(*List.iter (fun top_decl -> Format.eprintf "lusic: %a@." Printers.pp_decl top_decl) lusic.Lusic.contents;*) + Lusic.print_lusic_to_h destname lusic_ext + end + else + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. loading compiled header file %s@," header_name); + Modules.check_dependency lusic destname; + let header = lusic.Lusic.contents in + let (declared_types_env, declared_clocks_env) = get_envs_from_top_decls header in + check_compatibility + (prog, computed_types_env, computed_clocks_env) + (header, declared_types_env, declared_clocks_env) + end + end + +(* From prog to prog *) +let stage1 prog dirname basename = + (* Removing automata *) + let prog = Automata.expand_decls prog in + + (* Importing source *) + let _ = Modules.load_program ISet.empty prog in + + (* Extracting dependencies *) + let dependencies, type_env, clock_env = import_dependencies prog in + + (* Sorting nodes *) + let prog = SortProg.sort prog in + + (* Perform inlining before any analysis *) + let orig, prog = + if !Options.global_inline && !Options.main_node <> "" then + (if !Options.witnesses then prog else []), + Inliner.global_inline basename prog type_env clock_env + else (* if !Option.has_local_inline *) + [], + Inliner.local_inline basename prog type_env clock_env + in + + (* Checking stateless/stateful status *) + if Scopes.Plugin.is_active () then + force_stateful_decls prog + else + check_stateless_decls prog; + + (* Typing *) + let computed_types_env = type_decls type_env prog in + + (* Clock calculus *) + let computed_clocks_env = clock_decls clock_env prog in + + (* Generating a .lusi header file only *) + if !Options.lusi then + (* We stop here the processing and produce the current prog. It will be + exported as a lusi *) + raise (StopPhase1 prog); + + (* Delay calculus *) + (* TO BE DONE LATER (Xavier) + if(!Options.delay_calculus) + then + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. initialisation analysis@?"); + try + Delay_calculus.delay_prog Basic_library.delay_env prog + with (Delay.Error (loc,err)) as exc -> + Location.print loc; + eprintf "%a" Delay.pp_error err; + Utils.track_exception (); + raise exc + end; + *) + + (* Creating destination directory if needed *) + create_dest_dir (); + + (* Compatibility with Lusi *) + (* Checking the existence of a lusi (Lustre Interface file) *) + let extension = ".lusi" in + compile_source_to_header prog computed_types_env computed_clocks_env dirname basename extension; + + Typing.uneval_prog_generics prog; + Clock_calculus.uneval_prog_generics prog; + + if !Options.global_inline && !Options.main_node <> "" && !Options.witnesses then + begin + let orig = Corelang.copy_prog orig in + Log.report ~level:1 (fun fmt -> fprintf fmt ".. generating witness file@,"); + check_stateless_decls orig; + let _ = Typing.type_prog type_env orig in + let _ = Clock_calculus.clock_prog clock_env orig in + Typing.uneval_prog_generics orig; + Clock_calculus.uneval_prog_generics orig; + Inliner.witness + basename + !Options.main_node + orig prog type_env clock_env + end; + + (* Computes and stores generic calls for each node, + only useful for ANSI C90 compliant generic node compilation *) + if !Options.ansi then Causality.NodeDep.compute_generic_calls prog; + (*Hashtbl.iter (fun id td -> match td.Corelang.top_decl_desc with + Corelang.Node nd -> Format.eprintf "%s calls %a" id + Causality.NodeDep.pp_generic_calls nd | _ -> ()) Corelang.node_table;*) + + (* Optimization of prog: + - Unfold consts + - eliminate trivial expressions + *) + let prog = + if !Options.const_unfold || !Options.optimization >= 4 then + Optimize_prog.prog_unfold_consts prog + else + prog + in + + (* Normalization phase *) + Log.report ~level:1 (fun fmt -> fprintf fmt ".. normalization@,"); + (* Special treatment of arrows in lustre backend. We want to keep them *) + if !Options.output = "lustre" then + Normalization.unfold_arrow_active := false; + let prog = Normalization.normalize_prog prog in + Log.report ~level:2 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Printers.pp_prog prog); + + let prog = + if !Options.mpfr + then + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. targetting MPFR library@,"); + Mpfr.inject_prog prog + end + else + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. keeping FP numbers@,"); + prog + end in + Log.report ~level:2 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Printers.pp_prog prog); + + (* Checking array accesses *) + if !Options.check then + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. array access checks@,"); + Access.check_prog prog; + end; + + prog, dependencies + +(* prog -> machine *) + +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@,"); + let prog, node_schs = Scheduling.schedule_prog prog in + Log.report ~level:1 (fun fmt -> fprintf fmt "%a" Scheduling.pp_warning_unused node_schs); + Log.report ~level:3 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@," Scheduling.pp_schedule node_schs); + 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); + + + (* TODO Salsa optimize prog: + - emits warning for programs with pre inside expressions + - make sure each node arguments and memory is bounded by a local annotation + - introduce fresh local variables for each real pure subexpression + *) + (* let prog = *) + (* if true then *) + (* Salsa.Prog.normalize prog *) + (* else *) + (* prog *) + (* in *) + + (* DFS with modular code generation *) + Log.report ~level:1 (fun fmt -> fprintf fmt ".. machines generation@ "); + let machine_code = Machine_code.translate_prog prog node_schs in + + (* Optimize machine code *) + let machine_code, removed_table = + if !Options.optimization >= 2 && !Options.output <> "horn" then + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. machines optimization (phase 1)@ "); + Optimize_machine.machines_unfold (Corelang.get_consts prog) node_schs machine_code + end + else + machine_code, IMap.empty + in + (* Optimize machine code *) + let machine_code = + if !Options.optimization >= 3 && !Options.output <> "horn" then + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. machines optimization (phase 2)@ "); + let node_schs = Scheduling.remove_prog_inlined_locals removed_table node_schs in + let reuse_tables = Scheduling.compute_prog_reuse_table node_schs in + Optimize_machine.machines_fusion (Optimize_machine.machines_reuse_variables machine_code reuse_tables) + end + else + machine_code + in + + (* Salsa optimize machine code *) + let machine_code = + if !Options.salsa_enabled then + begin + check_main (); + Log.report ~level:1 (fun fmt -> fprintf fmt ".. salsa machines optimization (phase 3)@ "); + (* Selecting float constants for Salsa *) + let constEnv = List.fold_left ( + fun accu c_topdecl -> + match c_topdecl.top_decl_desc with + | Const c when Types.is_real_type c.const_type -> + (c.const_id, c.const_value) :: accu + | _ -> accu + ) [] (Corelang.get_consts prog) + in + List.map + (Machine_salsa_opt.machine_t2machine_t_optimized_by_salsa constEnv) + machine_code + end + else + machine_code + in + Log.report ~level:3 (fun fmt -> fprintf fmt "@[<v 2>@ %a@]@ " + (Utils.fprintf_list ~sep:"@ " Machine_code.pp_machine) + machine_code); + + machine_code + + +let stage3 prog machine_code dependencies basename = + (* Printing code *) + let basename = Filename.basename basename in + let destname = !Options.dest_dir ^ "/" ^ basename in + match !Options.output with + "C" -> + begin + let alloc_header_file = destname ^ "_alloc.h" in (* Could be changed *) + let source_lib_file = destname ^ ".c" in (* Could be changed *) + let source_main_file = destname ^ "_main.c" in (* Could be changed *) + let makefile_file = destname ^ ".makefile" in (* Could be changed *) + Log.report ~level:1 (fun fmt -> fprintf fmt ".. C code generation@,"); + C_backend.translate_to_c + alloc_header_file source_lib_file source_main_file makefile_file + basename prog machine_code dependencies + end + | "java" -> + begin + failwith "Sorry, but not yet supported !" + (*let source_file = basename ^ ".java" in + Log.report ~level:1 (fun fmt -> fprintf fmt ".. opening file %s@,@?" source_file); + let source_out = open_out source_file in + let source_fmt = formatter_of_out_channel source_out in + Log.report ~level:1 (fun fmt -> fprintf fmt ".. java code generation@,@?"); + Java_backend.translate_to_java source_fmt basename normalized_prog machine_code;*) + end + | "horn" -> + begin + let source_file = destname ^ ".smt2" in (* Could be changed *) + let source_out = open_out source_file in + let fmt = formatter_of_out_channel source_out in + Log.report ~level:1 (fun fmt -> fprintf fmt ".. hornification@,"); + Horn_backend.translate fmt basename prog machine_code; + (* Tracability file if option is activated *) + if !Options.horntraces then ( + let traces_file = destname ^ ".traces" in (* Could be changed *) + let traces_out = open_out traces_file in + let fmt = formatter_of_out_channel traces_out in + Log.report ~level:1 (fun fmt -> fprintf fmt ".. tracing info@,"); + Horn_backend.traces_file fmt basename prog machine_code; + ) + end + | "lustre" -> + begin + let source_file = destname ^ ".lustrec.lus" in (* Could be changed *) + let source_out = open_out source_file in + let fmt = formatter_of_out_channel source_out in + Printers.pp_prog fmt prog; +(* Lustre_backend.translate fmt basename normalized_prog machine_code *) + () + end + + | _ -> assert false + +(* compile a .lus source file *) +let compile_source dirname basename extension = + + Log.report ~level:1 (fun fmt -> fprintf fmt "@[<v>"); + + (* Parsing source *) + let prog = parse_source (dirname ^ "/" ^ basename ^ extension) in + + let prog = + if !Options.mpfr then + Mpfr.mpfr_module::prog + else + prog + in + let prog, dependencies = + try + stage1 prog dirname basename + with StopPhase1 prog -> ( + if !Options.lusi then + begin + let lusi_ext = "lusi" (* extension ^ "i" *) in + Log.report ~level:1 (fun fmt -> fprintf fmt ".. generating interface file %s@," (dirname ^ "/" ^ basename ^ lusi_ext)); + print_lusi prog dirname basename lusi_ext; + Log.report ~level:1 (fun fmt -> fprintf fmt ".. done !@ @]@."); + exit 0 + end + else + assert false + ) + in + + let machine_code = + stage2 prog + in + if Scopes.Plugin.show_scopes () then + begin + let all_scopes = Scopes.compute_scopes prog !Options.main_node in + (* Printing scopes *) + if !Options.verbose_level >= 1 then + Format.printf "Possible scopes are:@ "; + Format.printf "@[<v>%a@ @]@.@?" Scopes.print_scopes all_scopes; + exit 0 + + end; + + let machine_code = + if Scopes.Plugin.is_active () then + Scopes.Plugin.process_scopes !Options.main_node prog machine_code + else + machine_code + in + + stage3 prog machine_code dependencies basename; + begin + Log.report ~level:1 (fun fmt -> fprintf fmt ".. done !@ @]@."); + (* We stop the process here *) + exit 0 + end + +let compile dirname basename extension = + match extension with + | ".lusi" -> compile_header dirname basename extension + | ".lus" -> compile_source dirname basename extension + | _ -> assert false + +let anonymous filename = + let ok_ext, ext = List.fold_left + (fun (ok, ext) ext' -> + if not ok && Filename.check_suffix filename ext' then + true, ext' + else + ok, ext) + (false, "") extensions in + if ok_ext then + let dirname = Filename.dirname filename in + let basename = Filename.chop_suffix (Filename.basename filename) ext in + compile dirname basename ext + else + raise (Arg.Bad ("Can only compile *.lusi, *.lus or *.ec files")) + +let _ = + Global.initialize (); + Corelang.add_internal_funs (); + try + Printexc.record_backtrace true; + + let options = Options.options @ + List.flatten ( + List.map Options.plugin_opt [ + Scopes.Plugin.name, Scopes.Plugin.activate, Scopes.Plugin.options + ] + ) + in + + Arg.parse options anonymous usage + with + | Parse.Syntax_err _ | Lexer_lustre.Error _ + | Types.Error (_,_) | Clocks.Error (_,_) + | Corelang.Error _ (*| Task_set.Error _*) + | Causality.Cycle _ -> exit 1 + | Sys_error msg -> (eprintf "Failure: %s@." msg) + | exc -> (Utils.track_exception (); raise exc) + +(* Local Variables: *) +(* compile-command:"make -C .." *) +(* End: *) diff --git a/src/normalization.ml b/src/normalization.ml index 30e4066f..ea8027fb 100644 --- a/src/normalization.ml +++ b/src/normalization.ml @@ -95,15 +95,12 @@ let replace_expr locals expr = let unfold_offsets e offsets = let add_offset e d = -(*Format.eprintf "add_offset %a(%a) %a @." Printers.pp_expr e Types.print_ty e.expr_type Dimension.pp_dimension d; - let res = *) +(*Format.eprintf "add_offset %a %a@." Dimension.pp_dimension (Types.array_type_dimension e.expr_type) Dimension.pp_dimension d;*) { e with expr_tag = Utils.new_tag (); expr_loc = d.Dimension.dim_loc; expr_type = Types.array_element_type e.expr_type; - expr_desc = Expr_access (e, d) } -(*in (Format.eprintf "= %a @." Printers.pp_expr res; res) *) - in + expr_desc = Expr_access (e, d) } in List.fold_left add_offset e offsets (* Create an alias for [expr], if none exists yet *) @@ -121,22 +118,33 @@ let mk_expr_alias node (defs, vars) expr = (Clocks.clock_list_of_clock expr.expr_clock) in let new_def = mkeq expr.expr_loc (List.map (fun v -> v.var_id) new_aliases, expr) - in - (* Format.eprintf "Checking def of alias: %a -> %a@." (fprintf_list ~sep:", " (fun fmt v -> Format.pp_print_string fmt v.var_id)) new_aliases Printers.pp_expr expr; *) - (new_def::defs, new_aliases@vars), replace_expr new_aliases expr + in (new_def::defs, new_aliases@vars), replace_expr new_aliases expr (* Create an alias for [expr], if [expr] is not already an alias (i.e. an ident) and [opt] is true *) -let mk_expr_alias_opt opt node defvars expr = +let mk_expr_alias_opt opt node (defs, vars) expr = +(*Format.eprintf "mk_expr_alias_opt %B %a %a %a@." opt Printers.pp_expr expr Types.print_ty expr.expr_type Clocks.print_ck expr.expr_clock;*) match expr.expr_desc with | Expr_ident alias -> - defvars, expr + (defs, vars), expr | _ -> - if opt - then - mk_expr_alias node defvars expr - else - defvars, expr + match get_expr_alias defs expr with + | Some eq -> + let aliases = List.map (fun id -> List.find (fun v -> v.var_id = id) vars) eq.eq_lhs in + (defs, vars), replace_expr aliases expr + | None -> + if opt + then + let new_aliases = + List.map2 + (mk_fresh_var node expr.expr_loc) + (Types.type_list_of_type expr.expr_type) + (Clocks.clock_list_of_clock expr.expr_clock) in + let new_def = + mkeq expr.expr_loc (List.map (fun v -> v.var_id) new_aliases, expr) + in (new_def::defs, new_aliases@vars), replace_expr new_aliases expr + else + (defs, vars), expr (* Create a (normalized) expression from [ref_e], replacing description with [norm_d], @@ -159,7 +167,7 @@ let rec normalize_list alias node offsets norm_element defvars elist = ) elist (defvars, []) let rec normalize_expr ?(alias=true) node offsets defvars expr = -(* Format.eprintf "normalize %B %a [%a]@." alias Printers.pp_expr expr (Utils.fprintf_list ~sep:"," Dimension.pp_dimension) offsets;*) +(*Format.eprintf "normalize %B %a:%a [%a]@." alias Printers.pp_expr expr Types.print_ty expr.expr_type (Utils.fprintf_list ~sep:"," Dimension.pp_dimension) offsets;*) match expr.expr_desc with | Expr_const _ | Expr_ident _ -> defvars, unfold_offsets expr offsets @@ -179,8 +187,8 @@ let rec normalize_expr ?(alias=true) node offsets defvars expr = let defvars, norm_elist = normalize_list alias node offsets (fun alias -> normalize_expr ~alias:alias) defvars elist in defvars, mk_norm_expr offsets expr (Expr_tuple norm_elist) - | Expr_appl (id, args, None) - when Basic_library.is_internal_fun id + | Expr_appl (id, args, None) + when Basic_library.is_homomorphic_fun id && Types.is_array_type expr.expr_type -> let defvars, norm_args = normalize_list @@ -192,7 +200,7 @@ let rec normalize_expr ?(alias=true) node offsets defvars expr = (expr_list_of_expr args) in defvars, mk_norm_expr offsets expr (Expr_appl (id, expr_of_expr_list args.expr_loc norm_args, None)) - | Expr_appl (id, args, None) when Basic_library.is_internal_fun id -> + | Expr_appl (id, args, None) when Basic_library.is_expr_internal_fun expr -> let defvars, norm_args = normalize_expr ~alias:true node offsets defvars args in defvars, mk_norm_expr offsets expr (Expr_appl (id, norm_args, None)) | Expr_appl (id, args, r) -> @@ -203,7 +211,7 @@ let rec normalize_expr ?(alias=true) node offsets defvars expr = let defvars, norm_expr = normalize_expr node [] defvars norm_expr in normalize_expr ~alias:alias node offsets defvars norm_expr else - mk_expr_alias_opt (alias && not (Basic_library.is_internal_fun id)) node defvars norm_expr + mk_expr_alias_opt (alias && not (Basic_library.is_expr_internal_fun expr)) node defvars norm_expr | Expr_arrow (e1,e2) when !unfold_arrow_active && not (is_expr_once expr) -> (* Here we differ from Colaco paper: arrows are pushed to the top *) normalize_expr ~alias:alias node offsets defvars (unfold_arrow expr) | Expr_arrow (e1,e2) -> @@ -251,7 +259,7 @@ and normalize_branches node offsets defvars hl = hl (defvars, []) and normalize_array_expr ?(alias=true) node offsets defvars expr = -(* Format.eprintf "normalize_array %B %a [%a]@." alias Printers.pp_expr expr (Utils.fprintf_list ~sep:"," Dimension.pp_dimension) offsets;*) + (*Format.eprintf "normalize_array %B %a [%a]@." alias Printers.pp_expr expr (Utils.fprintf_list ~sep:"," Dimension.pp_dimension) offsets;*) match expr.expr_desc with | Expr_power (e1, d) when offsets = [] -> let defvars, norm_e1 = normalize_expr node offsets defvars e1 in @@ -262,7 +270,7 @@ and normalize_array_expr ?(alias=true) node offsets defvars expr = | Expr_array elist when offsets = [] -> let defvars, norm_elist = normalize_list alias node offsets (fun _ -> normalize_array_expr ~alias:true) defvars elist in defvars, mk_norm_expr offsets expr (Expr_array norm_elist) - | Expr_appl (id, args, None) when Basic_library.is_internal_fun id -> + | Expr_appl (id, args, None) when Basic_library.is_expr_internal_fun expr -> let defvars, norm_args = normalize_list alias node offsets (fun _ -> normalize_array_expr ~alias:true) defvars (expr_list_of_expr args) in defvars, mk_norm_expr offsets expr (Expr_appl (id, expr_of_expr_list args.expr_loc norm_args, None)) | _ -> normalize_expr ~alias:alias node offsets defvars expr @@ -310,6 +318,7 @@ let decouple_outputs node defvars eq = defvars', {eq with eq_lhs = lhs' } let rec normalize_eq node defvars eq = +(*Format.eprintf "normalize_eq %a@." Types.print_ty eq.eq_rhs.expr_type;*) match eq.eq_rhs.expr_desc with | Expr_pre _ | Expr_fby _ -> @@ -321,7 +330,7 @@ let rec normalize_eq node defvars eq = let (defs', vars'), norm_rhs = normalize_array_expr ~alias:false node [] defvars eq.eq_rhs in let norm_eq = { eq with eq_rhs = norm_rhs } in (norm_eq::defs', vars') - | Expr_appl (id, _, None) when Basic_library.is_internal_fun id && Types.is_array_type eq.eq_rhs.expr_type -> + | Expr_appl (id, _, None) when Basic_library.is_homomorphic_fun id && Types.is_array_type eq.eq_rhs.expr_type -> let (defs', vars'), norm_rhs = normalize_array_expr ~alias:false node [] defvars eq.eq_rhs in let norm_eq = { eq with eq_rhs = norm_rhs } in (norm_eq::defs', vars') @@ -353,10 +362,10 @@ let normalize_node node = List.fold_left ( fun (vars, def_accu, assert_accu) assert_ -> let assert_expr = assert_.assert_expr in - let (defs, vars'), expr = - normalize_expr - ~alias:true - node + let (defs, vars'), expr = + normalize_expr + ~alias:false + node [] (* empty offset for arrays *) ([], vars) (* defvar only contains vars *) assert_expr @@ -368,7 +377,7 @@ let normalize_node node = (*Format.eprintf "New locals: %a@.@?" (fprintf_list ~sep:", " Printers.pp_var) new_locals;*) let new_annots = - if !Options.traces then + if !Options.horntraces then begin (* Compute traceability info: - gather newly bound variables @@ -401,10 +410,7 @@ let normalize_node node = node_asserts = asserts; node_annot = new_annots; } - in ((*Printers.pp_node Format.err_formatter node;*) - node -) - + in ((*Printers.pp_node Format.err_formatter node;*) node) let normalize_decl decl = match decl.top_decl_desc with diff --git a/src/optimize_machine.ml b/src/optimize_machine.ml index 48a36f96..6d932a1d 100644 --- a/src/optimize_machine.ml +++ b/src/optimize_machine.ml @@ -26,7 +26,8 @@ let pp_elim fmt elim = let rec eliminate elim instr = let e_expr = eliminate_expr elim in - match instr with + match instr with + | MComment _ -> instr | MLocalAssign (i,v) -> MLocalAssign (i, e_expr v) | MStateAssign (i,v) -> MStateAssign (i, e_expr v) | MReset i -> instr @@ -41,126 +42,44 @@ let rec eliminate elim instr = ) and eliminate_expr elim expr = - match expr with - | StateVar v + match expr.value_desc with | LocalVar v -> (try IMap.find v.var_id elim with Not_found -> expr) - | Fun (id, vl) -> Fun (id, List.map (eliminate_expr elim) vl) - | Array(vl) -> Array(List.map (eliminate_expr elim) vl) - | Access(v1, v2) -> Access(eliminate_expr elim v1, eliminate_expr elim v2) - | Power(v1, v2) -> Power(eliminate_expr elim v1, eliminate_expr elim v2) - | Cst _ -> expr - -let eliminate_dim elim dim = - Dimension.expr_replace_expr (fun v -> try dimension_of_value (IMap.find v elim) with Not_found -> mkdim_ident dim.dim_loc v) dim - -let unfold_expr_offset m offset expr = - List.fold_left (fun res -> (function Index i -> Access(res, value_of_dimension m i) | Field f -> failwith "not yet implemented")) expr offset - -let rec simplify_cst_expr m offset cst = - match offset, cst with - | [] , _ - -> Cst cst - | Index i :: q, Const_array cl when Dimension.is_dimension_const i - -> simplify_cst_expr m q (List.nth cl (Dimension.size_const_dimension i)) - | Index i :: q, Const_array cl - -> unfold_expr_offset m [Index i] (Array (List.map (simplify_cst_expr m q) cl)) - | Field f :: q, Const_struct fl - -> simplify_cst_expr m q (List.assoc f fl) - | _ -> (Format.eprintf "internal error: Optimize_machine.simplify_cst_expr %a@." Printers.pp_const cst; assert false) - -let simplify_expr_offset m expr = - let rec simplify offset expr = - match offset, expr with - | Field f ::q , _ -> failwith "not yet implemented" - | _ , Fun (id, vl) when Basic_library.is_internal_fun id - -> Fun (id, List.map (simplify offset) vl) - | _ , Fun _ - | _ , StateVar _ - | _ , LocalVar _ -> unfold_expr_offset m offset expr - | _ , Cst cst -> simplify_cst_expr m offset cst - | _ , Access (expr, i) -> simplify (Index (dimension_of_value i) :: offset) expr - | [] , _ -> expr - | Index _ :: q, Power (expr, _) -> simplify q expr - | Index i :: q, Array vl when Dimension.is_dimension_const i - -> simplify q (List.nth vl (Dimension.size_const_dimension i)) - | Index i :: q, Array vl -> unfold_expr_offset m [Index i] (Array (List.map (simplify q) vl)) - | _ -> (Format.eprintf "internal error: Optimize_machine.simplify_expr_offset %a@." pp_val expr; assert false) - (*Format.eprintf "simplify_expr %a %a = %a@." pp_val expr (Utils.fprintf_list ~sep:"" Printers.pp_offset) offset pp_val res; res) - with e -> (Format.eprintf "simplify_expr %a %a = <FAIL>@." pp_val expr (Utils.fprintf_list ~sep:"" Printers.pp_offset) offset; raise e*) - in simplify [] expr - -let rec simplify_instr_offset m instr = - match instr with - | MLocalAssign (v, expr) -> MLocalAssign (v, simplify_expr_offset m expr) - | MStateAssign (v, expr) -> MStateAssign (v, simplify_expr_offset m expr) - | MReset id -> instr - | MStep (outputs, id, inputs) -> MStep (outputs, id, List.map (simplify_expr_offset m) inputs) - | MBranch (cond, brl) - -> MBranch(simplify_expr_offset m cond, List.map (fun (l, il) -> l, simplify_instrs_offset m il) brl) - -and simplify_instrs_offset m instrs = - List.map (simplify_instr_offset m) instrs + | Fun (id, vl) -> {expr with value_desc = Fun (id, List.map (eliminate_expr elim) vl)} + | Array(vl) -> {expr with value_desc = Array(List.map (eliminate_expr elim) vl)} + | Access(v1, v2) -> { expr with value_desc = Access(eliminate_expr elim v1, eliminate_expr elim v2)} + | Power(v1, v2) -> { expr with value_desc = Power(eliminate_expr elim v1, eliminate_expr elim v2)} + | Cst _ | StateVar _ -> expr let is_scalar_const c = match c with - | Const_int _ | Const_real _ - | Const_float _ + | Const_int _ | Const_tag _ -> true | _ -> false -(* An instruction v = expr may (and will) be unfolded iff: - - either expr is atomic - (no complex expressions, only const, vars and array/struct accesses) - - or v has a fanin <= 1 (used at most once) -*) -let is_unfoldable_expr fanin expr = - let rec unfold_const offset cst = - match offset, cst with - | _ , Const_int _ - | _ , Const_real _ - | _ , Const_float _ - | _ , Const_tag _ -> true - | Field f :: q, Const_struct fl -> unfold_const q (List.assoc f fl) - | [] , Const_struct _ -> false - | Index i :: q, Const_array cl when Dimension.is_dimension_const i - -> unfold_const q (List.nth cl (Dimension.size_const_dimension i)) - | _ , Const_array _ -> false - | _ -> assert false in - let rec unfold offset expr = - match offset, expr with - | _ , Cst cst -> unfold_const offset cst - | _ , LocalVar _ - | _ , StateVar _ -> true - | [] , Power _ - | [] , Array _ -> false - | Index i :: q, Power (v, _) -> unfold q v - | Index i :: q, Array vl when Dimension.is_dimension_const i - -> unfold q (List.nth vl (Dimension.size_const_dimension i)) - | _ , Array _ -> false - | _ , Access (v, i) -> unfold (Index (dimension_of_value i) :: offset) v - | _ , Fun (id, vl) when fanin < 2 && Basic_library.is_internal_fun id - -> List.for_all (unfold offset) vl - | _ , Fun _ -> false - | _ -> assert false - in unfold [] expr +let basic_unfoldable_expr expr = + match expr.value_desc with + | Cst c when is_scalar_const c -> true + | LocalVar _ + | StateVar _ -> true + | _ -> false -let unfoldable_assign fanin v expr = +let rec basic_unfoldable_assign fanin v expr = try let d = Hashtbl.find fanin v.var_id - in is_unfoldable_expr d expr - with Not_found -> false -(* -let unfoldable_assign fanin v expr = - try - let d = Hashtbl.find fanin v.var_id - in is_basic_expr expr || - match expr with - | Cst c when d < 2 -> true - | Fun (id, _) when d < 2 && Basic_library.is_internal_fun id -> true + in match expr.value_desc with + | Cst c when is_scalar_const c -> true + | Cst c when d < 2 -> true + | LocalVar _ + | StateVar _ -> true + | Fun (id, [a]) when d < 2 && Basic_library.is_value_internal_fun expr -> basic_unfoldable_assign fanin v a | _ -> false with Not_found -> false -*) + +let unfoldable_assign fanin v expr = + (if !Options.mpfr then Mpfr.unfoldable_value expr else true) +&& basic_unfoldable_assign fanin v expr + let merge_elim elim1 elim2 = let merge k e1 e2 = match e1, e2 with @@ -189,8 +108,8 @@ and instr_unfold fanin instrs elim instr = (* Format.eprintf "SHOULD WE STORE THE EXPRESSION IN INSTR %a TO ELIMINATE IT@." pp_instr instr;*) match instr with (* Simple cases*) - | MStep([v], id, vl) when Basic_library.is_internal_fun id - -> instr_unfold fanin instrs elim (MLocalAssign (v, Fun (id, vl))) + | MStep([v], id, vl) when Basic_library.is_value_internal_fun (mk_val (Fun (id, vl)) v.var_type) + -> instr_unfold fanin instrs elim (MLocalAssign (v, mk_val (Fun (id, vl)) v.var_type)) | MLocalAssign(v, expr) when unfoldable_assign fanin v expr -> (IMap.add v.var_id expr elim, instrs) | MBranch(g, hl) when false @@ -225,7 +144,7 @@ let machine_unfold fanin elim machine = (*Log.report ~level:1 (fun fmt -> Format.fprintf fmt "machine_unfold %a@." pp_elim elim);*) let elim_consts, mconst = instrs_unfold fanin elim machine.mconst in let elim_vars, instrs = instrs_unfold fanin elim_consts machine.mstep.step_instrs in - let instrs = simplify_instrs_offset machine instrs in + (*let instrs = simplify_instrs_offset machine instrs in*) let checks = List.map (fun (loc, check) -> loc, eliminate_expr elim_vars check) machine.mstep.step_checks in let locals = List.filter (fun v -> not (IMap.mem v.var_id elim_vars)) machine.mstep.step_locals in let minstances = List.map (static_call_unfold elim_consts) machine.minstances in @@ -242,26 +161,29 @@ let machine_unfold fanin elim machine = mconst = mconst; minstances = minstances; mcalls = mcalls; - } + }, + elim_vars let instr_of_const top_const = let const = const_of_top top_const in let vdecl = mkvar_decl Location.dummy_loc (const.const_id, mktyp Location.dummy_loc Tydec_any, mkclock Location.dummy_loc Ckdec_any, true, None) in let vdecl = { vdecl with var_type = const.const_type } - in MLocalAssign (vdecl, Cst const.const_value) + in MLocalAssign (vdecl, mk_val (Cst const.const_value) vdecl.var_type) let machines_unfold consts node_schs machines = - List.map - (fun m -> - let fanin = (IMap.find m.mname.node_id node_schs).Scheduling.fanin_table in - let elim_consts, _ = instrs_unfold fanin IMap.empty (List.map instr_of_const consts) - in machine_unfold fanin elim_consts m) + List.fold_right (fun m (machines, removed) -> + let fanin = (IMap.find m.mname.node_id node_schs).Scheduling.fanin_table in + let elim_consts, _ = instrs_unfold fanin IMap.empty (List.map instr_of_const consts) in + let (m, removed_m) = machine_unfold fanin elim_consts m in + (m::machines, IMap.add m.mname.node_id removed_m removed) + ) machines + ([], IMap.empty) let get_assign_lhs instr = match instr with - | MLocalAssign(v, _) -> LocalVar v - | MStateAssign(v, _) -> StateVar v + | MLocalAssign(v, e) -> mk_val (LocalVar v) e.value_type + | MStateAssign(v, e) -> mk_val (StateVar v) e.value_type | _ -> assert false let get_assign_rhs instr = @@ -277,7 +199,7 @@ let is_assign instr = | _ -> false let mk_assign v e = - match v with + match v.value_desc with | LocalVar v -> MLocalAssign(v, e) | StateVar v -> MStateAssign(v, e) | _ -> assert false @@ -315,18 +237,20 @@ let subst_instr subst instrs instr = let e = get_assign_rhs instr in try let instr' = List.find (fun instr' -> is_assign instr' && get_assign_rhs instr' = e) instrs in - match v with + match v.value_desc with | LocalVar v -> IMap.add v.var_id (get_assign_lhs instr') subst, instrs | StateVar v -> - (match get_assign_lhs instr' with + let lhs' = get_assign_lhs instr' in + let typ' = lhs'.value_type in + (match lhs'.value_desc with | LocalVar v' -> - let instr = eliminate subst (mk_assign (StateVar v) (LocalVar v')) in + let instr = eliminate subst (mk_assign (mk_val (StateVar v) typ') (mk_val (LocalVar v') typ')) in subst, instr :: instrs | StateVar v' -> - let subst_v' = IMap.add v'.var_id (StateVar v) IMap.empty in - let instrs' = snd (List.fold_right (fun instr (ok, instrs) -> (ok || instr = instr', if ok then instr :: instrs else if instr = instr' then instrs else eliminate subst_v' instr :: instrs)) instrs (false, [])) in - IMap.add v'.var_id (StateVar v) subst, instr :: instrs' + let subst_v' = IMap.add v'.var_id (mk_val (StateVar v) typ') IMap.empty in +let instrs' = snd (List.fold_right (fun instr (ok, instrs) -> (ok || instr = instr', if ok then instr :: instrs else if instr = instr' then instrs else eliminate subst_v' instr :: instrs)) instrs (false, [])) in + IMap.add v'.var_id (mk_val (StateVar v) typ') subst, instr :: instrs' | _ -> assert false) | _ -> assert false with Not_found -> subst, instr :: instrs @@ -341,9 +265,9 @@ let subst_instr subst instrs instr = let rec instr_cse (subst, instrs) instr = match instr with (* Simple cases*) - | MStep([v], id, vl) when Basic_library.is_internal_fun id - -> instr_cse (subst, instrs) (MLocalAssign (v, Fun (id, vl))) - | MLocalAssign(v, expr) when is_unfoldable_expr 2 expr + | MStep([v], id, vl) when Basic_library.is_value_internal_fun (mk_val (Fun (id, vl)) v.var_type) + -> instr_cse (subst, instrs) (MLocalAssign (v, (mk_val (Fun (id, vl)) v.var_type))) + | MLocalAssign(v, expr) when basic_unfoldable_expr expr -> (IMap.add v.var_id expr subst, instr :: instrs) | _ when is_assign instr -> subst_instr subst instrs instr @@ -384,8 +308,8 @@ let machines_cse machines = (* checks whether an [instr] is skip and can be removed from program *) let rec instr_is_skip instr = match instr with - | MLocalAssign (i, LocalVar v) when i = v -> true - | MStateAssign (i, StateVar v) when i = v -> true + | MLocalAssign (i, { value_desc = (LocalVar v) ; _}) when i = v -> true + | MStateAssign (i, { value_desc = StateVar v; _}) when i = v -> true | MBranch (g, hl) -> List.for_all (fun (_, il) -> instrs_are_skip il) hl | _ -> false and instrs_are_skip instrs = @@ -396,8 +320,8 @@ let instr_cons instr cont = let rec instr_remove_skip instr cont = match instr with - | MLocalAssign (i, LocalVar v) when i = v -> cont - | MStateAssign (i, StateVar v) when i = v -> cont + | MLocalAssign (i, { value_desc = LocalVar v; _ }) when i = v -> cont + | MStateAssign (i, { value_desc = StateVar v; _ }) when i = v -> cont | MBranch (g, hl) -> MBranch (g, List.map (fun (h, il) -> (h, instrs_remove_skip il [])) hl) :: cont | _ -> instr::cont @@ -405,17 +329,18 @@ and instrs_remove_skip instrs cont = List.fold_right instr_remove_skip instrs cont let rec value_replace_var fvar value = - match value with + match value.value_desc with | Cst c -> value - | LocalVar v -> LocalVar (fvar v) + | LocalVar v -> { value with value_desc = LocalVar (fvar v) } | StateVar v -> value - | Fun (id, args) -> Fun (id, List.map (value_replace_var fvar) args) - | Array vl -> Array (List.map (value_replace_var fvar) vl) - | Access (t, i) -> Access(value_replace_var fvar t, i) - | Power (v, n) -> Power(value_replace_var fvar v, n) + | Fun (id, args) -> { value with value_desc = Fun (id, List.map (value_replace_var fvar) args) } + | Array vl -> { value with value_desc = Array (List.map (value_replace_var fvar) vl)} + | Access (t, i) -> { value with value_desc = Access(value_replace_var fvar t, i)} + | Power (v, n) -> { value with value_desc = Power(value_replace_var fvar v, n)} let rec instr_replace_var fvar instr cont = match instr with + | MComment _ -> instr_cons instr cont | MLocalAssign (i, v) -> instr_cons (MLocalAssign (fvar i, value_replace_var fvar v)) cont | MStateAssign (i, v) -> instr_cons (MStateAssign (i, value_replace_var fvar v)) cont | MReset i -> instr_cons instr cont @@ -457,10 +382,10 @@ let machine_reuse_variables m reuse = with Not_found -> v in machine_replace_variables fvar m -let machines_reuse_variables prog node_schs = +let machines_reuse_variables prog reuse_tables = List.map (fun m -> - machine_reuse_variables m (Utils.IMap.find m.mname.node_id node_schs).Scheduling.reuse_table + machine_reuse_variables m (Utils.IMap.find m.mname.node_id reuse_tables) ) prog let rec instr_assign res instr = @@ -476,8 +401,8 @@ and instrs_assign res instrs = let rec instr_constant_assign var instr = match instr with - | MLocalAssign (i, Cst (Const_tag _)) - | MStateAssign (i, Cst (Const_tag _)) -> i = var + | MLocalAssign (i, { value_desc = Cst (Const_tag _); _ }) + | MStateAssign (i, { value_desc = Cst (Const_tag _); _ }) -> i = var | MBranch (g, hl) -> List.for_all (fun (h, b) -> instrs_constant_assign var b) hl | _ -> false @@ -486,8 +411,8 @@ and instrs_constant_assign var instrs = let rec instr_reduce branches instr1 cont = match instr1 with - | MLocalAssign (_, Cst (Const_tag c)) -> instr1 :: (List.assoc c branches @ cont) - | MStateAssign (_, Cst (Const_tag c)) -> instr1 :: (List.assoc c branches @ cont) + | MLocalAssign (_, { value_desc = Cst (Const_tag c); _}) -> instr1 :: (List.assoc c branches @ cont) + | MStateAssign (_, { value_desc = Cst (Const_tag c); _}) -> instr1 :: (List.assoc c branches @ cont) | MBranch (g, hl) -> MBranch (g, List.map (fun (h, b) -> (h, instrs_reduce branches b [])) hl) :: cont | _ -> instr1 :: cont @@ -502,9 +427,9 @@ let rec instrs_fusion instrs = | [] | [_] -> instrs - | i1::(MBranch (LocalVar v, hl))::q when instr_constant_assign v i1 -> + | i1::(MBranch ({ value_desc = LocalVar v; _}, hl))::q when instr_constant_assign v i1 -> instr_reduce (List.map (fun (h, b) -> h, instrs_fusion b) hl) i1 (instrs_fusion q) - | i1::(MBranch (StateVar v, hl))::q when instr_constant_assign v i1 -> + | i1::(MBranch ({ value_desc = StateVar v; _}, hl))::q when instr_constant_assign v i1 -> instr_reduce (List.map (fun (h, b) -> h, instrs_fusion b) hl) i1 (instrs_fusion q) | i1::i2::q -> i1 :: instrs_fusion (i2::q) diff --git a/src/options.ml b/src/options.ml index 16ada24b..99744c53 100755 --- a/src/options.ml +++ b/src/options.ml @@ -33,41 +33,60 @@ let witnesses = ref false let optimization = ref 2 let lusi = ref false let print_reuse = ref false -let traces = ref false +let const_unfold = ref false +let mpfr = ref false +let mpfr_prec = ref 0 let horntraces = ref false let horn_cex = ref false -let horn_query = ref true +let horn_queries = ref false +let salsa_enabled = ref false +let set_mpfr prec = + if prec > 0 then ( + mpfr := true; + mpfr_prec := prec; + salsa_enabled := false; (* We deactivate salsa *) + ) + else + failwith "mpfr requires a positive integer" + let options = [ "-d", Arg.Set_string dest_dir, - "uses the specified directory as root for generated/imported object and C files (default: .)"; - "-node", Arg.Set_string main_node, "specifies the main node"; + "uses the specified directory \x1b[4mdir\x1b[0m as root for generated/imported object and C files <default: .>"; + "-node", Arg.Set_string main_node, "specifies the \x1b[4mmain\x1b[0m node"; "-init", Arg.Set delay_calculus, "performs an initialisation analysis for Lustre nodes <default: no analysis>"; "-dynamic", Arg.Clear static_mem, "specifies a dynamic allocation scheme for main Lustre node <default: static>"; - "-ansi", Arg.Set ansi, "specifies that generated C code is ansi C90 compliant <default: C99>"; "-check-access", Arg.Set check, "checks at runtime that array accesses always lie within bounds <default: no check>"; + "-mpfr", Arg.Int set_mpfr, "replaces FP numbers by the MPFR library multiple precision numbers with a precision of \x1b[4mprec\x1b[0m bits <default: keep FP numbers>"; "-lusi", Arg.Set lusi, "only generates a .lusi interface source file from a Lustre source <default: no generation>"; "-no-spec", Arg.Unit (fun () -> spec := "no"), "do not generate any specification"; - "-acsl-spec", Arg.Unit (fun () -> spec := "acsl"), "generates an ACSL encoding of the specification. Only meaningful for the C backend (default)"; + "-acsl-spec", Arg.Unit (fun () -> spec := "acsl"), "generates an ACSL encoding of the specification. Only meaningful for the C backend <default>"; "-c-spec", Arg.Unit (fun () -> spec := "c"), "generates a C encoding of the specification instead of ACSL contracts and annotations. Only meaningful for the C backend"; "-java", Arg.Unit (fun () -> output := "java"), "generates Java output instead of C"; "-horn", Arg.Unit (fun () -> output := "horn"), "generates Horn clauses encoding output instead of C"; - "-horn-traces", Arg.Unit (fun () -> output := "horn"; traces:=true), "produce traceability file for Horn backend. Enable the horn backend."; + "-horn-traces", Arg.Unit (fun () -> output := "horn"; horntraces:=true), "produce traceability file for Horn backend. Enable the horn backend."; "-horn-cex", Arg.Unit (fun () -> output := "horn"; horn_cex:=true), "generate cex enumeration. Enable the horn backend (work in progress)"; - "-horn-query", Arg.Unit (fun () -> output := "horn"; horn_query:=true), "generate queries in generated Horn file. Enable the horn backend (work in progress)"; + "-horn-queries", Arg.Unit (fun () -> output := "horn"; horn_queries:=true), "generate queries in generated Horn file. Enable the horn backend (work in progress)"; + "-salsa", Arg.Set salsa_enabled, "activate Salsa optimization <default>"; + "-no-salsa", Arg.Clear salsa_enabled, "deactivate Salsa optimization"; "-print_reuse", Arg.Set print_reuse, "prints variable reuse policy"; "-lustre", Arg.Unit (fun () -> output := "lustre"), "generates Lustre output, performing all active optimizations"; - "-inline", Arg.Set global_inline, "inline all node calls (require a main node)"; + "-inline", Arg.Unit (fun () -> global_inline := true; const_unfold := true), "inline all node calls (require a main node). Implies constant unfolding"; "-witnesses", Arg.Set witnesses, "enable production of witnesses during compilation"; "-print_types", Arg.Set print_types, "prints node types"; "-print_clocks", Arg.Set print_clocks, "prints node clocks"; - "-O", Arg.Set_int optimization, " changes optimization level <default: 2>"; - "-verbose", Arg.Set_int verbose_level, " changes verbose level <default: 1>"; + "-O", Arg.Set_int optimization, "changes optimization \x1b[4mlevel\x1b[0m <default: 2>"; + "-verbose", Arg.Set_int verbose_level, "changes verbose \x1b[4mlevel\x1b[0m <default: 1>"; "-version", Arg.Unit print_version, " displays the version";] +let plugin_opt (name, activate, options) = + ( "-" ^ name , Arg.Unit activate, "activate plugin " ^ name ) :: + (List.map (fun (opt, act, desc) -> "-" ^ name ^ opt, act, desc) options) + + let get_witness_dir filename = (* Make sure the directory exists *) let dir = !dest_dir ^ "/" ^ (Filename.basename filename) ^ "_witnesses" in diff --git a/src/parser_lustre.mly b/src/parser_lustre.mly index ab4b1766..40f02458 100755 --- a/src/parser_lustre.mly +++ b/src/parser_lustre.mly @@ -18,9 +18,10 @@ open Parse let get_loc () = Location.symbol_rloc () +let mkident x = x, get_loc () let mktyp x = mktyp (get_loc ()) x let mkclock x = mkclock (get_loc ()) x -let mkvar_decl x = mkvar_decl (get_loc ()) ~orig:true x +let mkvar_decl x loc = mkvar_decl loc ~orig:true x let mkexpr x = mkexpr (get_loc ()) x let mkeexpr x = mkeexpr (get_loc ()) x let mkeq x = mkeq (get_loc ()) x @@ -52,8 +53,8 @@ let rec fby expr n init = %} %token <int> INT -%token <string> REAL -%token <float> FLOAT +%token <Num.num * int * string> REAL + %token <string> STRING %token AUTOMATON STATE UNTIL UNLESS RESTART RESUME LAST %token STATELESS ASSERT OPEN QUOTE FUNCTION @@ -69,7 +70,7 @@ let rec fby expr n init = %token MERGE FBY WHEN WHENNOT EVERY %token NODE LET TEL RETURNS VAR IMPORTED SENSOR ACTUATOR WCET TYPE CONST %token STRUCT ENUM -%token TINT TFLOAT TREAL TBOOL TCLOCK +%token TINT TREAL TBOOL TCLOCK %token RATE DUE %token EQ LT GT LTE GTE NEQ %token AND OR XOR IMPL @@ -116,6 +117,9 @@ let rec fby expr n init = %start lustre_spec %type <LustreSpec.node_annot> lustre_spec +%start signed_const +%type <LustreSpec.constant> signed_const + %% module_ident: @@ -135,8 +139,8 @@ node_ident_decl: node_ident { push_node $1; $1 } vdecl_ident: - UIDENT { $1 } -| IDENT { $1 } + UIDENT { mkident $1 } +| IDENT { mkident $1 } const_ident: UIDENT { $1 } @@ -180,7 +184,7 @@ state_annot: top_decl_header: | CONST cdecl_list { List.rev ($2 true) } -| nodespec_list state_annot node_ident LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR prototype_opt in_lib_opt SCOL +| nodespec_list state_annot node_ident LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR prototype_opt in_lib_list SCOL {let nd = mktop_decl true (ImportedNode {nodei_id = $3; nodei_type = Types.new_var (); @@ -198,9 +202,9 @@ prototype_opt: { None } | PROTOTYPE node_ident { Some $2} -in_lib_opt: -{ None } -| LIB module_ident {Some $2} +in_lib_list: +{ [] } +| LIB module_ident in_lib_list { $2::$3 } top_decl: | CONST cdecl_list { List.rev ($2 false) } @@ -232,8 +236,8 @@ top_decl: in pop_node (); (*add_node $3 nd;*) [nd] } - - nodespec_list: + +nodespec_list: { None } | NODESPEC nodespec_list { (function @@ -264,7 +268,7 @@ typeconst: TINT array_typ_decl { $2 Tydec_int } | TBOOL array_typ_decl { $2 Tydec_bool } | TREAL array_typ_decl { $2 Tydec_real } -| TFLOAT array_typ_decl { $2 Tydec_float } +/* | TFLOAT array_typ_decl { $2 Tydec_float } */ | type_ident array_typ_decl { $2 (Tydec_const $1) } | TBOOL TCLOCK { Tydec_clock Tydec_bool } | IDENT TCLOCK { Tydec_clock (Tydec_const $1) } @@ -313,8 +317,8 @@ assert_: | ASSERT expr SCOL {mkassert ($2)} eq: - ident_list EQ expr SCOL {mkeq (List.rev $1,$3)} -| LPAR ident_list RPAR EQ expr SCOL {mkeq (List.rev $2,$5)} + ident_list EQ expr SCOL {mkeq (List.rev (List.map fst $1), $3)} +| LPAR ident_list RPAR EQ expr SCOL {mkeq (List.rev (List.map fst $2), $5)} lustre_spec: | contract EOF { $1 } @@ -369,8 +373,8 @@ dim_list: expr: /* constants */ INT {mkexpr (Expr_const (Const_int $1))} -| REAL {mkexpr (Expr_const (Const_real $1))} -| FLOAT {mkexpr (Expr_const (Const_float $1))} +| REAL {let c,e,s = $1 in mkexpr (Expr_const (Const_real (c,e,s)))} +/* | FLOAT {mkexpr (Expr_const (Const_float $1))}*/ /* Idents or type enum tags */ | IDENT { mkexpr (Expr_ident $1) } | tag_ident { mkexpr (Expr_ident $1) (*(Expr_const (Const_tag $1))*) } @@ -395,13 +399,13 @@ expr: {(*mkexpr (Expr_fby ($1,$3))*) mkexpr (Expr_arrow ($1, mkexpr (Expr_pre $3)))} | expr WHEN vdecl_ident - {mkexpr (Expr_when ($1,$3,tag_true))} + {mkexpr (Expr_when ($1,fst $3,tag_true))} | expr WHENNOT vdecl_ident - {mkexpr (Expr_when ($1,$3,tag_false))} + {mkexpr (Expr_when ($1,fst $3,tag_false))} | expr WHEN tag_ident LPAR vdecl_ident RPAR - {mkexpr (Expr_when ($1, $5, $3))} + {mkexpr (Expr_when ($1, fst $5, $3))} | MERGE vdecl_ident handler_expr_list - {mkexpr (Expr_merge ($2,$3))} + {mkexpr (Expr_merge (fst $2,$3))} /* Applications */ | node_ident LPAR expr RPAR @@ -409,28 +413,9 @@ expr: | node_ident LPAR expr RPAR EVERY expr {mkexpr (Expr_appl ($1, $3, Some $6))} | node_ident LPAR tuple_expr RPAR - { - let id=$1 in - let args=List.rev $3 in - match id, args with - | "fbyn", [expr;n;init] -> - let n = match n.expr_desc with - | Expr_const (Const_int n) -> n - | _ -> assert false - in - fby expr n init - | _ -> mkexpr (Expr_appl ($1, mkexpr (Expr_tuple args), None)) - } + {mkexpr (Expr_appl ($1, mkexpr (Expr_tuple (List.rev $3)), None))} | node_ident LPAR tuple_expr RPAR EVERY expr - { - let id=$1 in - let args=List.rev $3 in - let clock=$6 in - if id="fby" then - assert false (* TODO Ca veut dire quoi fby (e,n,init) every c *) - else - mkexpr (Expr_appl (id, mkexpr (Expr_tuple args), Some clock)) - } + {mkexpr (Expr_appl ($1, mkexpr (Expr_tuple (List.rev $3)), Some $6)) } /* Boolean expr */ | expr AND expr @@ -497,12 +482,12 @@ signed_const_struct: signed_const: INT {Const_int $1} -| REAL {Const_real $1} -| FLOAT {Const_float $1} +| REAL {let c,e,s =$1 in Const_real (c,e,s)} +/* | FLOAT {Const_float $1} */ | tag_ident {Const_tag $1} | MINUS INT {Const_int (-1 * $2)} -| MINUS REAL {Const_real ("-" ^ $2)} -| MINUS FLOAT {Const_float (-1. *. $2)} +| MINUS REAL {let c,e,s = $2 in Const_real (Num.minus_num c, e, "-" ^ s)} +/* | MINUS FLOAT {Const_float (-1. *. $2)} */ | LCUR signed_const_struct RCUR { Const_struct $2 } | LBRACKET signed_const_array RBRACKET { Const_array $2 } @@ -567,11 +552,11 @@ vdecl_list: vdecl: ident_list COL typeconst clock - { List.map (fun id -> mkvar_decl (id, mktyp $3, $4, false, None)) $1 } + { List.map (fun (id, loc) -> mkvar_decl (id, mktyp $3, $4, false, None) loc) $1 } | CONST ident_list /* static parameters don't have clocks */ - { List.map (fun id -> mkvar_decl (id, mktyp Tydec_any, mkclock Ckdec_any, true, None)) $2 } + { List.map (fun (id, loc) -> mkvar_decl (id, mktyp Tydec_any, mkclock Ckdec_any, true, None) loc) $2 } | CONST ident_list COL typeconst /* static parameters don't have clocks */ - { List.map (fun id -> mkvar_decl (id, mktyp $4, mkclock Ckdec_any, true, None)) $2 } + { List.map (fun (id, loc) -> mkvar_decl (id, mktyp $4, mkclock Ckdec_any, true, None) loc) $2 } local_vdecl_list: local_vdecl {$1} @@ -579,13 +564,13 @@ local_vdecl_list: local_vdecl: /* Useless no ?*/ ident_list - { List.map (fun id -> mkvar_decl (id, mktyp Tydec_any, mkclock Ckdec_any, false, None)) $1 } + { List.map (fun (id, loc) -> mkvar_decl (id, mktyp Tydec_any, mkclock Ckdec_any, false, None) loc) $1 } | ident_list COL typeconst clock - { List.map (fun id -> mkvar_decl (id, mktyp $3, $4, false, None)) $1 } + { List.map (fun (id, loc) -> mkvar_decl (id, mktyp $3, $4, false, None) loc) $1 } | CONST vdecl_ident EQ expr /* static parameters don't have clocks */ - { [ mkvar_decl ($2, mktyp Tydec_any, mkclock Ckdec_any, true, Some $4) ] } + { let (id, loc) = $2 in [ mkvar_decl (id, mktyp Tydec_any, mkclock Ckdec_any, true, Some $4) loc ] } | CONST vdecl_ident COL typeconst EQ expr /* static parameters don't have clocks */ - { [ mkvar_decl ($2, mktyp $4, mkclock Ckdec_any, true, Some $6) ] } + { let (id, loc) = $2 in [ mkvar_decl (id, mktyp $4, mkclock Ckdec_any, true, Some $6) loc ] } cdecl_list: cdecl SCOL { (fun itf -> [$1 itf]) } diff --git a/src/plugins/salsa/machine_salsa_opt.ml b/src/plugins/salsa/machine_salsa_opt.ml new file mode 100644 index 00000000..6900a332 --- /dev/null +++ b/src/plugins/salsa/machine_salsa_opt.ml @@ -0,0 +1,572 @@ + +(* We try to avoid opening modules here *) +module ST = Salsa.SalsaTypes +module SDT = SalsaDatatypes +module LT = LustreSpec +module MC = Machine_code + +(* Datatype for Salsa: FormalEnv, Ranges, Var set ... *) +open SalsaDatatypes +(******************************************************************) +(* TODO Xavier: should those functions be declared more globally? *) + +let fun_types node = + try + match node.LT.top_decl_desc with + | LT.Node nd -> + let tin, tout = Types.split_arrow nd.LT.node_type in + Types.type_list_of_type tin, Types.type_list_of_type tout + | _ -> Format.eprintf "%a is not a node@.@?" Printers.pp_decl node; assert false + with Not_found -> Format.eprintf "Unable to find type def for function %s@.@?" (Corelang.node_name node); assert false + +let called_node_id m id = + let td, _ = + try + List.assoc id m.MC.mcalls (* TODO Xavier: mcalls or minstances ? *) + with Not_found -> assert false + in + td +(******************************************************************) + +(* Returns the set of vars that appear in the expression *) +let rec get_expr_real_vars e = + match e.LT.value_desc with + | LT.LocalVar v | LT.StateVar v when Types.is_real_type v.LT.var_type -> Vars.singleton v + | LT.LocalVar _| LT.StateVar _ + | LT.Cst _ -> Vars.empty + | LT.Fun (_, args) -> + List.fold_left + (fun acc e -> Vars.union acc (get_expr_real_vars e)) + Vars.empty args + | LT.Array _ + | LT.Access _ + | LT.Power _ -> assert false + +(* Extract the variables to appear as free variables in expressions (lhs) *) +let rec get_read_vars instrs = + match instrs with + [] -> Vars.empty + | i::tl -> ( + let vars_tl = get_read_vars tl in + match i with + | LT.MLocalAssign(_,e) + | LT.MStateAssign(_,e) -> Vars.union (get_expr_real_vars e) vars_tl + | LT.MStep(_, _, el) -> List.fold_left (fun accu e -> Vars.union (get_expr_real_vars e) accu) vars_tl el + | LT.MBranch(e, branches) -> ( + let vars = Vars.union (get_expr_real_vars e) vars_tl in + List.fold_left (fun vars (_, b) -> Vars.union vars (get_read_vars b) ) vars branches + ) + | LT.MReset _ + | LT.MComment _ -> Vars.empty + ) + +let rec get_written_vars instrs = + match instrs with + [] -> Vars.empty + | i::tl -> ( + let vars_tl = get_written_vars tl in + match i with + | LT.MLocalAssign(v,_) + | LT.MStateAssign(v,_) -> Vars.add v vars_tl + | LT.MStep(vdl, _, _) -> List.fold_left (fun accu v -> Vars.add v accu) vars_tl vdl + | LT.MBranch(_, branches) -> ( + List.fold_left (fun vars (_, b) -> Vars.union vars (get_written_vars b) ) vars_tl branches + ) + | LT.MReset _ + | LT.MComment _ -> Vars.empty + ) + + +(* Optimize a given expression. It returns another expression and a computed range. *) +let optimize_expr nodename constEnv printed_vars vars_env ranges formalEnv e : LT.value_t * RangesInt.t option = + let rec opt_expr ranges formalEnv e = + match e.LT.value_desc with + | LT.Cst cst -> + Format.eprintf "optmizing constant expr ? @ "; + (* the expression is a constant, we optimize it directly if it is a real + constant *) + let typ = Typing.type_const Location.dummy_loc cst in + if Types.is_real_type typ then + opt_num_expr ranges formalEnv e + else e, None + | LT.LocalVar v + | LT.StateVar v -> + if not (Vars.mem v printed_vars) && + (* TODO xAvier: comment recuperer le type de l'expression? Parfois e.value_type vaut 'd *) + (Types.is_real_type e.LT.value_type || Types.is_real_type v.LT.var_type) + then + opt_num_expr ranges formalEnv e + else + e, None (* Nothing to optimize for expressions containing a single non real variable *) + (* (\* optimize only numerical vars *\) *) + (* if Type_predef.is_real_type v.LT.var_type then opt_num_expr ranges formalEnv e *) + (* else e, None *) + | LT.Fun (fun_id, args) -> ( + (* necessarily, this is a basic function (ie. + - * / && || mod ... ) *) + (* if the return type is real then optimize it, otherwise call recusrsively on arguments *) + if Types.is_real_type e.LT.value_type then + opt_num_expr ranges formalEnv e + else ( + (* We do not care for computed local ranges. *) + let args' = List.map (fun arg -> let arg', _ = opt_expr ranges formalEnv arg in arg') args in + { e with LT.value_desc = LT.Fun(fun_id, args')}, None + ) + ) + | LT.Array _ + | LT.Access _ + | LT.Power _ -> assert false + and opt_num_expr ranges formalEnv e = + if debug then Format.eprintf "Optimizing expression %a@ " MC.pp_val e; + let fresh_id = "toto" in (* TODO more meaningful name *) + (* Convert expression *) + List.iter (fun (l,c) -> Format.eprintf "%s -> %a@ " l Printers.pp_const c) constEnv; + let e_salsa : Salsa.SalsaTypes.expression = value_t2salsa_expr constEnv e in + Format.eprintf "apres deplaige constantes ok%a @." MC.pp_val (salsa_expr2value_t vars_env [](* constEnv *) e_salsa) ; + + (* Convert formalEnv *) + if debug then Format.eprintf "Formal env is [%a]@ " FormalEnv.pp formalEnv; + let formalEnv_salsa = + FormalEnv.fold (fun id expr accu -> + (id, value_t2salsa_expr constEnv expr)::accu + ) formalEnv [] in + if debug then Format.eprintf "Formal env converted to salsa@ "; + (* Substitute all occurences of variables by their definition in env *) + let (e_salsa: Salsa.SalsaTypes.expression), _ = + Salsa.Rewrite.substVars + e_salsa + formalEnv_salsa + 0 (* TODO: Nasrine, what is this integer value for ? *) + in + if debug then Format.eprintf "Substituted def in expr@ "; + let abstractEnv = Hashtbl.fold + (fun id value accu -> (id,value)::accu) + ranges + [] + in + (* List.iter (fun (id, _) -> Format.eprintf "absenv: %s@." id) abstractEnv; *) + (* The expression is partially evaluated by the available ranges + valEnv2ExprEnv remplce les paires id, abstractVal par id, Cst itv - on + garde evalPartExpr remplace les variables e qui sont dans env par la cst + - on garde *) + if debug then Format.eprintf "avant avant eval part@ "; + Format.eprintf "avant evalpart: %a@." MC.pp_val (salsa_expr2value_t vars_env constEnv e_salsa); + let e_salsa = + Salsa.Float.evalPartExpr + e_salsa + (Salsa.Float.valEnv2ExprEnv abstractEnv) + ([] (* no blacklisted variables *)) + in + Format.eprintf "apres evalpart: %a@." MC.pp_val (salsa_expr2value_t vars_env constEnv e_salsa); + (* Checking if we have all necessary information *) + + let free_vars = get_salsa_free_vars vars_env constEnv abstractEnv e_salsa in + + if Vars.cardinal free_vars > 0 then ( + Format.eprintf "Warning: unbounded free vars (%a) in expression %a. We do not optimize it.@ " + Vars.pp (Vars.fold (fun v accu -> let v' = {v with LT.var_id = nodename ^ "." ^ v.LT.var_id } in Vars.add v' accu) free_vars Vars.empty) + MC.pp_val (salsa_expr2value_t vars_env constEnv e_salsa); + if debug then Format.eprintf "Some free vars, not optimizing@."; + let new_e = try salsa_expr2value_t vars_env constEnv e_salsa with Not_found -> assert false in + new_e, None + ) + else ( + try + if debug then + Format.eprintf "Analyzing expression %a with env: @[<v>%a@ @]@ " + MC.pp_val (salsa_expr2value_t vars_env constEnv e_salsa) + (Utils.fprintf_list ~sep:",@ "(fun fmt (l,r) -> Format.fprintf fmt "%s -> %a" l FloatIntSalsa.pp r)) abstractEnv + ; + + let new_e_salsa, e_val = + Salsa.MainEPEG.transformExpression fresh_id e_salsa abstractEnv + in + let new_e = try salsa_expr2value_t vars_env constEnv new_e_salsa with Not_found -> assert false in + if debug then Format.eprintf "@ @[<v>old: %a@ new: %a@ range: %a@]" MC.pp_val e MC.pp_val new_e RangesInt.pp_val e_val; + new_e, Some e_val + with Not_found -> assert false + | Salsa.Epeg_types.EPEGError _ -> ( + Format.eprintf "BECAUSE OF AN ERROR, Expression %a was not optimized@ " MC.pp_val e; + e, None + ) + ) + + + + in + if debug then + Format.eprintf "@[<v 2>Optimizing expression %a in environment %a and ranges %a@ " + MC.pp_val e + FormalEnv.pp formalEnv + RangesInt.pp ranges; + let res = opt_expr ranges formalEnv e in + Format.eprintf "@]@ "; + res + + + +(* Returns a list of assign, for each var in vars_to_print, that produce the + definition of it according to formalEnv, and driven by the ranges. *) +let assign_vars nodename constEnv vars_env printed_vars ranges formalEnv vars_to_print = + (* We print thhe expression in the order of definition *) + + let ordered_vars = + List.stable_sort + (FormalEnv.get_sort_fun formalEnv) + (Vars.elements vars_to_print) + in + Format.eprintf "Printing vars in the following order: [%a]@ " (Utils.fprintf_list ~sep:", " Printers.pp_var) ordered_vars ; + List.fold_right ( + fun v (accu_instr, accu_ranges) -> + if debug then Format.eprintf "Printing assign for variable %s@ " v.LT.var_id; + try + (* Obtaining unfold expression of v in formalEnv *) + let v_def = FormalEnv.get_def formalEnv v in + let e, r = optimize_expr nodename constEnv printed_vars vars_env ranges formalEnv v_def in + let instr = + if try (get_var vars_env v.LT.var_id).is_local with Not_found -> assert false then + LT.MLocalAssign(v, e) + else + LT.MStateAssign(v, e) + in + instr::accu_instr, + (match r with + | None -> ranges + | Some v_r -> RangesInt.add_def ranges v.LT.var_id v_r) + with FormalEnv.NoDefinition _ -> ( + (* It should not happen with C backend, but may happen with Lustre backend *) + if !Options.output = "lustre" then accu_instr, ranges else (Format.eprintf "@?"; assert false) + ) + ) ordered_vars ([], ranges) + +(* Main recursive function: modify the instructions list while preserving the + order of assigns for state variables. Returns a quintuple: (new_instrs, + ranges, formalEnv, printed_vars, and remaining vars to be printed) *) +let rec rewrite_instrs nodename constEnv vars_env m instrs ranges formalEnv printed_vars vars_to_print = + let assign_vars = assign_vars nodename constEnv vars_env in + if debug then ( + Format.eprintf "------------@ "; + Format.eprintf "Current printed_vars: [%a]@ " Vars.pp printed_vars; + Format.eprintf "Formal env is [%a]@ " FormalEnv.pp formalEnv; + ); + match instrs with + | [] -> + (* End of instruction list: we produce the definition of each variable that + appears in vars_to_print. Each of them should be defined in formalEnv *) + if debug then Format.eprintf "Producing definitions %a@ " Vars.pp vars_to_print; + let instrs, ranges' = assign_vars printed_vars ranges formalEnv vars_to_print in + instrs, + ranges', + formalEnv, + Vars.union printed_vars vars_to_print, (* We should have printed all required vars *) + [] (* No more vars to be printed *) + + | hd_instr::tl_instrs -> + (* We reformulate hd_instr, producing or not a fresh instruction, updating + formalEnv, possibly ranges and vars_to_print *) + begin + let hd_instrs, ranges, formalEnv, printed_vars, vars_to_print = + match hd_instr with + | LT.MLocalAssign(vd,vt) when Types.is_real_type vd.LT.var_type && not (Vars.mem vd vars_to_print) -> + (* LocalAssign are injected into formalEnv *) + if debug then Format.eprintf "Registering local assign %a@ " MC.pp_instr hd_instr; + let formalEnv' = FormalEnv.def formalEnv vd vt in (* formelEnv updated with vd = vt *) + [], (* no instr generated *) + ranges, (* no new range computed *) + formalEnv', + printed_vars, (* no new printed vars *) + vars_to_print (* no more or less variables to print *) + + | LT.MLocalAssign(vd,vt) when Types.is_real_type vd.LT.var_type && Vars.mem vd vars_to_print -> + + if debug then Format.eprintf "Registering and producing state assign %a@ " MC.pp_instr hd_instr; + let formalEnv' = FormalEnv.def formalEnv vd vt in (* formelEnv updated with vd = vt *) + let instrs', ranges' = (* printing vd = optimized vt *) + assign_vars printed_vars ranges formalEnv' (Vars.singleton vd) + in + instrs', + ranges', (* no new range computed *) + formalEnv', (* formelEnv already updated *) + Vars.add vd printed_vars, (* adding vd to new printed vars *) + Vars.remove vd vars_to_print (* removed vd from variables to print *) + + | LT.MStateAssign(vd,vt) when Types.is_real_type vd.LT.var_type && Vars.mem vd vars_to_print -> + + (* StateAssign are produced since they are required by the function. We still + keep their definition in the formalEnv in case it can optimize later + outputs. vd is removed from remaining vars_to_print *) + if debug then Format.eprintf "Registering and producing state assign %a@ " MC.pp_instr hd_instr; + let formalEnv' = FormalEnv.def formalEnv vd vt in (* formelEnv updated with vd = vt *) + let instrs', ranges' = (* printing vd = optimized vt *) + assign_vars printed_vars ranges formalEnv (Vars.singleton vd) + in + instrs', + ranges', (* no new range computed *) + formalEnv, (* formelEnv already updated *) + Vars.add vd printed_vars, (* adding vd to new printed vars *) + Vars.remove vd vars_to_print (* removed vd from variables to print *) + + | (LT.MLocalAssign(vd,vt) | LT.MStateAssign(vd,vt)) -> + (* We have to produce the instruction. But we may have to produce as + well its dependencies *) + let required_vars = get_expr_real_vars vt in + let required_vars = Vars.diff required_vars printed_vars in (* remove + already + produced + variables *) + let prefix_instr, ranges = + assign_vars printed_vars ranges formalEnv required_vars in + let vt', _ = optimize_expr nodename constEnv (Vars.union required_vars printed_vars) vars_env ranges formalEnv vt in + let new_instr = + match hd_instr with + | LT.MLocalAssign _ -> LT.MLocalAssign(vd,vt') + | _ -> LT.MStateAssign(vd,vt') + in + let written_vars = Vars.add vd required_vars in + prefix_instr@[new_instr], + ranges, (* no new range computed *) + formalEnv, (* formelEnv untouched *) + Vars.union written_vars printed_vars, (* adding vd + dependencies to + new printed vars *) + Vars.diff vars_to_print written_vars (* removed vd + dependencies from + variables to print *) + + | LT.MStep(vdl,id,vtl) -> + if debug then Format.eprintf "Call to a node %a@ " MC.pp_instr hd_instr; + (* Call of an external function. Input expressions have to be + optimized, their free variables produced. A fresh range has to be + computed for each output variable in vdl. Output of the function + call are removed from vars to be printed *) + let node = called_node_id m id in + let node_id = Corelang.node_name node in + let tin, tout = (* special care for arrow *) + if node_id = "_arrow" then + match vdl with + | [v] -> let t = v.LT.var_type in + [t; t], [t] + | _ -> assert false (* should not happen *) + else + fun_types node + in + if debug then Format.eprintf "@[<v 2>... optimizing arguments@ "; + let vtl', vtl_ranges = List.fold_right2 ( + fun e typ_e (exprl, range_l)-> + if Types.is_real_type typ_e then + let e', r' = optimize_expr nodename constEnv printed_vars vars_env ranges formalEnv e in + e'::exprl, r'::range_l + else + e::exprl, None::range_l + ) vtl tin ([], []) + in + if debug then Format.eprintf "... done@ @]@ "; + let required_vars = + List.fold_left2 + (fun accu e typ_e -> + if Types.is_real_type typ_e then + Vars.union accu (get_expr_real_vars e) + else (* we do not consider non real expressions *) + accu + ) + Vars.empty + vtl' tin + in + if debug then Format.eprintf "Required vars: [%a]@ Printed vars: [%a]@ Remaining required vars: [%a]@ " + Vars.pp required_vars + Vars.pp printed_vars + Vars.pp (Vars.diff required_vars printed_vars) + ; + let required_vars = Vars.diff required_vars printed_vars in (* remove + already + produced + variables *) + let written_vars = Vars.union required_vars (Vars.of_list vdl) in + let instrs', ranges' = assign_vars (Vars.union written_vars printed_vars) ranges formalEnv required_vars in + instrs' @ [LT.MStep(vdl,id,vtl')], (* New instrs *) + RangesInt.add_call ranges' vdl id vtl_ranges, (* add information bounding each vdl var *) + formalEnv, + Vars.union written_vars printed_vars, (* adding vdl to new printed vars *) + Vars.diff vars_to_print written_vars + + | LT.MBranch(vt, branches) -> + (* Required variables to compute vt are introduced. + Then each branch is refactored specifically + *) + if debug then Format.eprintf "Branching %a@ " MC.pp_instr hd_instr; + let required_vars = get_expr_real_vars vt in + let required_vars = Vars.diff required_vars printed_vars in (* remove + already + produced + variables *) + let prefix_instr, ranges = + assign_vars (Vars.union required_vars printed_vars) ranges formalEnv required_vars in + + let printed_vars = Vars.union printed_vars required_vars in + + let vt', _ = optimize_expr nodename constEnv printed_vars vars_env ranges formalEnv vt in + + let read_vars_tl = get_read_vars tl_instrs in + if debug then Format.eprintf "@[<v 2>Dealing with branches@ "; + let branches', written_vars, merged_ranges = List.fold_right ( + fun (b_l, b_instrs) (new_branches, written_vars, merged_ranges) -> + let b_write_vars = get_written_vars b_instrs in + let b_vars_to_print = Vars.inter b_write_vars (Vars.union read_vars_tl vars_to_print) in + let b_fe = formalEnv in (* because of side effect + data, we copy it for + each branch *) + let b_instrs', b_ranges, b_formalEnv, b_printed, b_vars = + rewrite_instrs nodename constEnv vars_env m b_instrs ranges b_fe printed_vars b_vars_to_print + in + (* b_vars should be empty *) + let _ = if b_vars != [] then assert false in + + (* Producing the refactored branch *) + (b_l, b_instrs') :: new_branches, + Vars.union b_printed written_vars, (* They should coincides. We + use union instead of + inter to ease the + bootstrap *) + RangesInt.merge merged_ranges b_ranges + + ) branches ([], required_vars, ranges) in + if debug then Format.eprintf "dealing with branches done@ @]@ "; + prefix_instr@[LT.MBranch(vt', branches')], + merged_ranges, (* Only step functions call within branches + may have produced new ranges. We merge this data by + computing the join per variable *) + formalEnv, (* Thanks to the computation of var_to_print in each + branch, no new definition should have been computed + without being already printed *) + Vars.union written_vars printed_vars, + Vars.diff vars_to_print written_vars (* We remove vars that have been + produced within branches *) + + + | LT.MReset(_) | LT.MComment _ -> + if debug then Format.eprintf "Untouched %a (non real)@ " MC.pp_instr hd_instr; + + (* Untouched instruction *) + [ hd_instr ], (* unmodified instr *) + ranges, (* no new range computed *) + formalEnv, (* no formelEnv update *) + printed_vars, + vars_to_print (* no more or less variables to print *) + + in + let tl_instrs, ranges, formalEnv, printed_vars, vars_to_print = + rewrite_instrs + nodename + constEnv + vars_env + m + tl_instrs + ranges + formalEnv + printed_vars + vars_to_print + in + hd_instrs @ tl_instrs, + ranges, + formalEnv, + printed_vars, + vars_to_print + end + + + + + + +(* TODO: deal with new variables, ie. tmp *) +let salsaStep constEnv m s = + let ranges = RangesInt.empty (* empty for the moment, should be build from + machine annotations or externally provided information *) in + let annots = List.fold_left ( + fun accu annl -> + List.fold_left ( + fun accu (key, range) -> + match key with + | ["salsa"; "ranges"; var] -> (var, range)::accu + | _ -> accu + ) accu annl.LT.annots + ) [] m.MC.mannot + in + let ranges = + List.fold_left (fun ranges (v, value) -> + match value.LT.eexpr_qfexpr.LT.expr_desc with + | LT.Expr_tuple [minv; maxv] -> ( + let get_cst e = match e.LT.expr_desc with + | LT.Expr_const (LT.Const_real (c,e,s)) -> + (* calculer la valeur c * 10^e *) + Num.float_of_num (Num.div_num c (Num.power_num (Num.num_of_int 10) (Num.num_of_int e))) + | _ -> + Format.eprintf + "Invalid scala range: %a. It should be a pair of constant floats.@." + Printers.pp_expr value.LT.eexpr_qfexpr; + assert false + in + let minv, maxv = get_cst minv, get_cst maxv in + if debug then Format.eprintf "%s in [%f, %f]@ " v minv maxv; + RangesInt.enlarge ranges v (Salsa.SalsaTypes.I(minv, maxv),Salsa.SalsaTypes.J(0.,0.)) + ) + | _ -> + Format.eprintf + "Invalid scala range: %a. It should be a pair of floats.@." + Printers.pp_expr value.LT.eexpr_qfexpr; + assert false + ) ranges annots + in + let formal_env = FormalEnv.empty () in + let vars_to_print = + Vars.real_vars + ( + Vars.union + (Vars.of_list m.MC.mmemory) + (Vars.of_list s.MC.step_outputs) + ) + in + (* TODO: should be at least step output + may be memories *) + let vars_env = compute_vars_env m in + let new_instrs, _, _, printed_vars, _ = + rewrite_instrs + m.MC.mname.LT.node_id + constEnv + vars_env + m + s.MC.step_instrs + ranges + formal_env + (Vars.real_vars (Vars.of_list s.MC.step_inputs (* printed_vars : real + inputs are considered as + already printed *))) + vars_to_print + in + let all_local_vars = Vars.real_vars (Vars.of_list s.MC.step_locals) in + let unused = (Vars.diff all_local_vars printed_vars) in + let locals = + if not (Vars.is_empty unused) then ( + Format.eprintf "Unused local vars: [%a]. Removing them.@.@?" + Vars.pp unused; + List.filter (fun v -> not (Vars.mem v unused)) s.MC.step_locals + ) + else + s.MC.step_locals + in + { s with MC.step_instrs = new_instrs; MC.step_locals = locals } (* we have also to modify local variables to declare new vars *) + + +let machine_t2machine_t_optimized_by_salsa constEnv mt = + try + if debug then Format.eprintf "@[<v 2>------------------ Optimizing machine %s@ " mt.MC.mname.LT.node_id; + let new_step = salsaStep constEnv mt mt.MC.mstep in + if debug then Format.eprintf "@]@."; + { mt with MC.mstep = new_step } + + + with FormalEnv.NoDefinition v as exp -> + Format.eprintf "No definition for variable %a@.@?" Printers.pp_var v; + raise exp + + +(* Local Variables: *) +(* compile-command:"make -C ../../.." *) +(* End: *) + diff --git a/src/plugins/salsa/salsaDatatypes.ml b/src/plugins/salsa/salsaDatatypes.ml new file mode 100644 index 00000000..e169354e --- /dev/null +++ b/src/plugins/salsa/salsaDatatypes.ml @@ -0,0 +1,337 @@ +module LT = LustreSpec +module MC = Machine_code +module ST = Salsa.SalsaTypes +module Float = Salsa.Float + +let debug = true + +let pp_hash ~sep f fmt r = + Format.fprintf fmt "[@[<v>"; + Hashtbl.iter (fun k v -> Format.fprintf fmt "%t%s@ " (f k v) sep) r; + Format.fprintf fmt "]@]"; + +module FormalEnv = +struct + type fe_t = (LT.ident, (int * LT.value_t)) Hashtbl.t + let cpt = ref 0 + + exception NoDefinition of LT.var_decl + (* Returns the expression associated to v in env *) + let get_def (env: fe_t) v = + try + snd (Hashtbl.find env v.LT.var_id) + with Not_found -> raise (NoDefinition v) + + let def (env: fe_t) d expr = + incr cpt; + let fresh = Hashtbl.copy env in + Hashtbl.add fresh d.LT.var_id (!cpt, expr); fresh + + let empty (): fe_t = Hashtbl.create 13 + + let pp fmt env = pp_hash ~sep:";" (fun k (_,v) fmt -> Format.fprintf fmt "%s -> %a" k MC.pp_val v) fmt env + + let fold f = Hashtbl.fold (fun k (_,v) accu -> f k v accu) + + let get_sort_fun env = + let order = Hashtbl.fold (fun k (cpt, _) accu -> (k,cpt)::accu) env [] in + fun v1 v2 -> + if List.mem_assoc v1.LT.var_id order && List.mem_assoc v2.LT.var_id order then + if (List.assoc v1.LT.var_id order) <= (List.assoc v2.LT.var_id order) then + -1 + else + 1 + else + assert false + +end + +module Ranges = + functor (Value: sig type t val union: t -> t -> t val pp: Format.formatter -> t -> unit end) -> +struct + type t = Value.t + type r_t = (LT.ident, Value.t) Hashtbl.t + + let empty: r_t = Hashtbl.create 13 + + (* Look for def of node i with inputs living in vtl_ranges, reinforce ranges + to bound vdl: each output of node i *) + let add_call ranges vdl id vtl_ranges = ranges (* TODO assert false. On est + pas obligé de faire + qqchose. On peut supposer + que les ranges sont donnés + pour chaque noeud *) + + + let pp = pp_hash ~sep:";" (fun k v fmt -> Format.fprintf fmt "%s -> %a" k Value.pp v) + let pp_val = Value.pp + + let add_def ranges name r = + (* Format.eprintf "%s: declare %a@." *) + (* x.LT.var_id *) + (* Value.pp r ; *) + + let fresh = Hashtbl.copy ranges in + Hashtbl.add fresh name r; fresh + + let enlarge ranges name r = + let fresh = Hashtbl.copy ranges in + if Hashtbl.mem fresh name then + Hashtbl.replace fresh name (Value.union r (Hashtbl.find fresh name)) + else + Hashtbl.add fresh name r; + fresh + + + (* Compute a join per variable *) + let merge ranges1 ranges2 = + Format.eprintf "Mergeing rangesint %a with %a@." pp ranges1 pp ranges2; + let ranges = Hashtbl.copy ranges1 in + Hashtbl.iter (fun k v -> + if Hashtbl.mem ranges k then ( + (* Format.eprintf "%s: %a union %a = %a@." *) + (* k *) + (* Value.pp v *) + (* Value.pp (Hashtbl.find ranges k) *) + (* Value.pp (Value.union v (Hashtbl.find ranges k)); *) + Hashtbl.replace ranges k (Value.union v (Hashtbl.find ranges k)) + ) + else + Hashtbl.add ranges k v + ) ranges2; + Format.eprintf "Merge result %a@." pp ranges; + ranges + +end + +module FloatIntSalsa = +struct + type t = ST.abstractValue + + let pp fmt (f,r) = + match f, r with + | ST.I(a,b), ST.J(c,d) -> + Format.fprintf fmt "[%f, %f] + [%f, %f]" a b c d + | ST.I(a,b), ST.JInfty -> Format.fprintf fmt "[%f, %f] + oo" a b + | ST.Empty, _ -> Format.fprintf fmt "???" + + | _ -> assert false + + let union v1 v2 = + match v1, v2 with + |(ST.I(x1, x2), ST.J(y1, y2)), (ST.I(x1', x2'), ST.J(y1', y2')) -> + ST.(I(min x1 x1', max x2 x2'), J(min y1 y1', max y2 y2')) + | _ -> Format.eprintf "%a cup %a failed@.@?" pp v1 pp v2; assert false + + let inject cst = match cst with + | LT.Const_int(i) -> Salsa.Builder.mk_cst (ST.I(float_of_int i,float_of_int i),ST.J(0.0,0.0)) + | LT.Const_real (c,e,s) -> (* TODO: this is incorrect. We should rather + compute the error associated to the float *) + let r = float_of_string s in + if r = 0. then + Salsa.Builder.mk_cst (ST.I(-. min_float, min_float),Float.ulp (ST.I(-. min_float, min_float))) + else + Salsa.Builder.mk_cst (ST.I(r*.(1.-.epsilon_float),r*.(1.+.epsilon_float)),Float.ulp (ST.I(r,r))) + | _ -> assert false +end + +module RangesInt = Ranges (FloatIntSalsa) + +module Vars = +struct + module VarSet = Set.Make (struct type t = LT.var_decl let compare x y = compare x.LT.var_id y.LT.var_id end) + let real_vars vs = VarSet.filter (fun v -> Types.is_real_type v.LT.var_type) vs + let of_list = List.fold_left (fun s e -> VarSet.add e s) VarSet.empty + + include VarSet + + let remove_list (set:t) (v_list: elt list) : t = List.fold_right VarSet.remove v_list set + let pp fmt vs = Utils.fprintf_list ~sep:", " Printers.pp_var fmt (VarSet.elements vs) +end + + + + + + + + + + +(*************************************************************************************) +(* Converting values back and forth *) +(*************************************************************************************) + +let rec value_t2salsa_expr constEnv vt = + let value_t2salsa_expr = value_t2salsa_expr constEnv in + let res = + match vt.LT.value_desc with + (* | LT.Cst(LT.Const_tag(t) as c) -> *) + (* Format.eprintf "v2s: cst tag@."; *) + (* if List.mem_assoc t constEnv then ( *) + (* Format.eprintf "trouvé la constante %s: %a@ " t Printers.pp_const c; *) + (* FloatIntSalsa.inject (List.assoc t constEnv) *) + (* ) *) + (* else ( *) + (* Format.eprintf "Const tag %s unhandled@.@?" t ; *) + (* raise (Salsa.Prelude.Error ("Entschuldigung6, constant tag not yet implemented")) *) + (* ) *) + | LT.Cst(cst) -> Format.eprintf "v2s: cst tag 2: %a@." Printers.pp_const cst; FloatIntSalsa.inject cst + | LT.LocalVar(v) + | LT.StateVar(v) -> Format.eprintf "v2s: var %s@." v.LT.var_id; + let sel_fun = (fun (vname, _) -> v.LT.var_id = vname) in + if List.exists sel_fun constEnv then + let _, cst = List.find sel_fun constEnv in + FloatIntSalsa.inject cst + else + let id = v.LT.var_id in + Salsa.Builder.mk_id id + | LT.Fun(binop, [x;y]) -> let salsaX = value_t2salsa_expr x in + let salsaY = value_t2salsa_expr y in + let op = ( + let pred f x y = Salsa.Builder.mk_int_of_bool (f x y) in + match binop with + | "+" -> Salsa.Builder.mk_plus + | "-" -> Salsa.Builder.mk_minus + | "*" -> Salsa.Builder.mk_times + | "/" -> Salsa.Builder.mk_div + | "=" -> pred Salsa.Builder.mk_eq + | "<" -> pred Salsa.Builder.mk_lt + | ">" -> pred Salsa.Builder.mk_gt + | "<=" -> pred Salsa.Builder.mk_lte + | ">=" -> pred Salsa.Builder.mk_gte + | _ -> assert false + ) + in + op salsaX salsaY + | LT.Fun(unop, [x]) -> let salsaX = value_t2salsa_expr x in + Salsa.Builder.mk_uminus salsaX + + | LT.Fun(f,_) -> raise (Salsa.Prelude.Error + ("Unhandled function "^f^" in conversion to salsa expression")) + + | LT.Array(_) + | LT.Access(_) + | LT.Power(_) -> raise (Salsa.Prelude.Error ("Unhandled construct in conversion to salsa expression")) + in + (* if debug then *) + (* Format.eprintf "value_t2salsa_expr: %a -> %a@ " *) + (* MC.pp_val vt *) + (* (fun fmt x -> Format.fprintf fmt "%s" (Salsa.Print.printExpression x)) res; *) + res + +type var_decl = { vdecl: LT.var_decl; is_local: bool } +module VarEnv = Map.Make (struct type t = LT.ident let compare = compare end ) + +(* let is_local_var vars_env v = *) +(* try *) +(* (VarEnv.find v vars_env).is_local *) +(* with Not_found -> Format.eprintf "Impossible to find var %s@.@?" v; assert false *) + +let get_var vars_env v = +try + VarEnv.find v vars_env + with Not_found -> Format.eprintf "Impossible to find var %s@.@?" v; assert false + +let compute_vars_env m = + let env = VarEnv.empty in + let env = + List.fold_left + (fun accu v -> VarEnv.add v.LT.var_id {vdecl = v; is_local = false; } accu) + env + m.MC.mmemory + in + let env = + List.fold_left ( + fun accu v -> VarEnv.add v.LT.var_id {vdecl = v; is_local = true; } accu + ) + env + MC.(m.mstep.step_inputs@m.mstep.step_outputs@m.mstep.step_locals) + in +env + +let rec salsa_expr2value_t vars_env cst_env e = + let salsa_expr2value_t = salsa_expr2value_t vars_env cst_env in + let binop op e1 e2 t = + let x = salsa_expr2value_t e1 in + let y = salsa_expr2value_t e2 in + MC.mk_val (LT.Fun (op, [x;y])) t + in + match e with + ST.Cst((ST.I(f1,f2),_),_) -> (* We project ranges into constants. We + forget about errors and provide the + mean/middle value of the interval + *) + let new_float = + if f1 = f2 then + f1 + else + (f1 +. f2) /. 2.0 + in + Format.eprintf "Converting [%.45f, %.45f] in %.45f@." f1 f2 new_float; + let cst = + let s = + if new_float = 0. then "0." else + (* We have to convert it into our format: int * int * real *) + let _ = Format.flush_str_formatter () in + Format.fprintf Format.str_formatter "%.50f" new_float; + Format.flush_str_formatter () + in + Parser_lustre.signed_const Lexer_lustre.token (Lexing.from_string s) + in + MC.mk_val (LT.Cst(cst)) Type_predef.type_real + | ST.Id(id, _) -> + Format.eprintf "Looking for id=%s@.@?" id; + if List.mem_assoc id cst_env then ( + let cst = List.assoc id cst_env in + Format.eprintf "Found cst = %a@.@?" Printers.pp_const cst; + MC.mk_val (LT.Cst cst) Type_predef.type_real + ) + else + (* if is_const salsa_label then *) + (* MC.Cst(LT.Const_tag(get_const salsa_label)) *) + (* else *) + let var_id = try get_var vars_env id with Not_found -> assert false in + if var_id.is_local then + MC.mk_val (LT.LocalVar(var_id.vdecl)) var_id.vdecl.LT.var_type + else + MC.mk_val (LT.StateVar(var_id.vdecl)) var_id.vdecl.LT.var_type + | ST.Plus(x, y, _) -> binop "+" x y Type_predef.type_real + | ST.Minus(x, y, _) -> binop "-" x y Type_predef.type_real + | ST.Times(x, y, _) -> binop "*" x y Type_predef.type_real + | ST.Div(x, y, _) -> binop "/" x y Type_predef.type_real + | ST.Uminus(x,_) -> let x = salsa_expr2value_t x in + MC.mk_val (LT.Fun("uminus",[x])) Type_predef.type_real + | ST.IntOfBool(ST.Eq(x, y, _),_) -> binop "=" x y Type_predef.type_bool + | ST.IntOfBool(ST.Lt(x,y,_),_) -> binop "<" x y Type_predef.type_bool + | ST.IntOfBool(ST.Gt(x,y,_),_) -> binop ">" x y Type_predef.type_bool + | ST.IntOfBool(ST.Lte(x,y,_),_) -> binop "<=" x y Type_predef.type_bool + | ST.IntOfBool(ST.Gte(x,y,_),_) -> binop ">=" x y Type_predef.type_bool + | _ -> raise (Salsa.Prelude.Error "Entschuldigung, salsaExpr2value_t case not yet implemented") + + +let rec get_salsa_free_vars vars_env constEnv absenv e = + let f = get_salsa_free_vars vars_env constEnv absenv in + match e with + | ST.Id (id, _) -> + if not (List.mem_assoc id absenv) && not (List.mem_assoc id constEnv) then + Vars.singleton ((try VarEnv.find id vars_env with Not_found -> assert false).vdecl) + else + Vars.empty + | ST.Plus(x, y, _) + | ST.Minus(x, y, _) + | ST.Times(x, y, _) + | ST.Div(x, y, _) + | ST.IntOfBool(ST.Eq(x, y, _),_) + | ST.IntOfBool(ST.Lt(x,y,_),_) + | ST.IntOfBool(ST.Gt(x,y,_),_) + | ST.IntOfBool(ST.Lte(x,y,_),_) + | ST.IntOfBool(ST.Gte(x,y,_),_) + -> Vars.union (f x) (f y) + | ST.Uminus(x,_) -> f x + | ST.Cst _ -> Vars.empty + | _ -> assert false + +(* Local Variables: *) +(* compile-command:"make -C ../../.." *) +(* End: *) diff --git a/src/plugins/scopes/scopes.ml b/src/plugins/scopes/scopes.ml new file mode 100644 index 00000000..1f1920bd --- /dev/null +++ b/src/plugins/scopes/scopes.ml @@ -0,0 +1,319 @@ +open LustreSpec +open Corelang +open Machine_code + +(* (variable, node name, node instance) *) +type scope_t = (var_decl * string * string option) list * var_decl + + +let scope_to_sl ((sl, v) : scope_t) : string list= + List.fold_right ( + fun (v, nodename, _) accu -> + v.var_id :: nodename :: accu + ) sl [v.var_id] + +let get_node name prog = + let node_opt = List.fold_left + (fun res top -> + match res, top.top_decl_desc with + | Some _, _ -> res + | None, Node nd -> + (* Format.eprintf "Checking node %s = %s: %b@." nd.node_id name (nd.node_id = name); *) + if nd.node_id = name then Some nd else res + | _ -> None) + None prog + in + try + Utils.desome node_opt + with Utils.DeSome -> raise Not_found + +let get_machine name machines = + try + List.find (fun m -> m.mname.node_id = name) machines + with Not_found -> raise Not_found + +let rec compute_scopes prog main_node : scope_t list = + + (* Format.eprintf "Compute scope of %s@." main_node; *) + try + let node = get_node main_node prog in + let all_vars = node.node_inputs @ node.node_locals @ node.node_outputs in + let local_vars = node.node_inputs @ node.node_locals in + let local_scopes = List.map (fun x -> [], x) local_vars in + let sub_scopes = + let sub_nodes = + List.fold_left + (fun res s -> + match s with + | Eq ({ eq_rhs ={ expr_desc = Expr_appl (nodeid, _, _); _}; _ } as eq) -> + (* Obtaining the var_del associated to the first var of eq_lhs *) + ( + try + let query v = v.var_id = List.hd eq.eq_lhs in + let vid = List.find query all_vars in + (nodeid, vid)::res + with Not_found -> Format.eprintf "eq=%a@.local_vars=%a@." Printers.pp_node_eq eq (Utils.fprintf_list ~sep:"," Printers.pp_var) local_vars; assert false + ) + | Eq _ -> res + | _ -> assert false (* TODO deal with Automaton *) + ) [] node.node_stmts + in + List.map (fun (nodeid, vid) -> + let scopes = compute_scopes prog nodeid in + List.map (fun (sl,v) -> (vid, nodeid, None)::sl, v) scopes (* instances are not yet known, hence the None *) + ) sub_nodes + in + local_scopes @ (List.flatten sub_scopes) + with Not_found -> [] + + +let print_scopes = + Utils.fprintf_list ~sep:"@ " + (fun fmt ((_, v) as s) -> Format.fprintf fmt "%a: %a" + (Utils.fprintf_list ~sep:"." Format.pp_print_string )(scope_to_sl s) + Types.print_ty v.var_type) + + + + +(* let print_path fmt p = *) +(* Utils.fprintf_list ~sep:"." (fun fmt (id, _) -> Format.pp_print_string fmt id) fmt p *) + +let get_node_vdecl_of_name name node = + try + List.find + (fun v -> v.var_id = name) + (node.node_inputs @ node.node_outputs @ node.node_locals ) + with Not_found -> + Format.eprintf "Cannot find variable %s in node %s@." name node.node_id; + assert false + +let scope_path main_node_name prog machines all_scopes sl : scope_t = + let rec get_path node id_list accu = + match id_list, accu with + | [id], (_, last_node, _)::_ -> (* last item, it should denote a local + memory variable (local var, memory or input *) + let id_vdecl = + get_node_vdecl_of_name id (get_node last_node prog) + in + List.rev accu, id_vdecl + | varid::nodename::id_list_tl, _ -> ( + let e_machine = get_machine node.node_id machines in + (* Format.eprintf "Looking for def %s in call %s in machine %a@." *) + (* varid nodename *) + (* Machine_code.pp_machine e_machine; *) + let find_var = (fun v -> v.var_id = varid) in + let instance = + List.find + (fun i -> match i with + | MStep(p, o, _) -> List.exists find_var p + | _ -> false + ) + e_machine.mstep.step_instrs + in + try + let variable, instance_node, instance_id = + match instance with + | MStep(p, o, _) -> + (* Format.eprintf "Looking for machine %s@.@?" o; *) + let o_fun, _ = List.assoc o e_machine.mcalls in + if node_name o_fun = nodename then + List.hd p, o_fun, o + else + assert false + | _ -> assert false + in + let next_node = node_of_top instance_node in + let accu = (variable, nodename, Some instance_id)::accu in + (* Format.eprintf "Calling get path on %s@.@?" next_node.node_id; *) + get_path next_node id_list_tl accu + with Not_found -> Format.eprintf "toto@."; assert false + ) + | _ -> assert false + in + let all_scopes_as_sl = List.map scope_to_sl all_scopes in + if not (List.mem sl all_scopes_as_sl) then ( + Format.eprintf "%s is an invalid scope.@." (String.concat "." sl); + exit 1 + ) + else ( + (* Format.eprintf "@.@.Required path: %s@." (String.concat "." sl) ; *) + let main_node = get_node main_node_name prog in + let path, flow = (* Special treatment of first level flow *) + match sl with + | [flow] -> let flow_var = get_node_vdecl_of_name flow main_node in + [], flow_var + | _ -> get_path main_node sl [] + + in + (* Format.eprintf "computed path: %a.%s@." print_path path flow.var_id; *) + path, flow + + ) + +let check_scopes main_node_name prog machines all_scopes scopes = + List.map + (fun sl -> + sl, scope_path main_node_name prog machines all_scopes sl + ) scopes + +let scopes_def : string list list ref = ref [] +let inputs = ref [] + +let option_show_scopes = ref false +let option_scopes = ref false +let option_all_scopes = ref true +let option_mem_scopes = ref false +let option_input_scopes = ref false + +let scopes_map : (LustreSpec.ident list * scope_t) list ref = ref [] + +let register_scopes s = + option_all_scopes:=false; + let scope_list = Str.split (Str.regexp ", *") s in + let scope_list = List.map (fun scope -> Str.split (Str.regexp "\\.") scope) scope_list in + scopes_def := scope_list + +let register_inputs s = + let input_list = Str.split (Str.regexp "[;]") s in + let input_list = List.map (fun s -> match Str.split (Str.regexp "=") s with | [v;e] -> v, e | _ -> raise (Invalid_argument ("Input list error: " ^ s))) input_list in + let input_list = List.map (fun (v, e) -> v, Str.split (Str.regexp "[;]") e) input_list in + inputs := input_list + + +(* TODO: recuperer le type de "flow" et appeler le print correspondant + iterer sur path pour construire la suite des xx_mem._reg.yy_mem._reg......flow +par ex main_mem->n8->n9->_reg.flow +*) +let pp_scopes fmt scopes = + let rec scope_path (path, flow) accu = + match path with + | [] -> accu ^ "_reg." ^ flow.var_id, flow.var_type + | (_, _, Some instance_id)::tl -> scope_path (tl, flow) ( accu ^ instance_id ^ "->" ) + | _ -> assert false + in + let scopes_vars = + List.map + (fun (sl, scope) -> + String.concat "." sl, scope_path scope "main_mem.") + scopes + in + List.iter (fun (id, (var, typ)) -> + match (Types.repr typ).Types.tdesc with + | Types.Tint -> Format.fprintf fmt "_put_int(\"%s\", %s);@ " id var + | Types.Tbool -> Format.fprintf fmt "_put_bool(\"%s\", %s);@ " id var + | Types.Treal when !Options.mpfr -> + Format.fprintf fmt "_put_double(\"%s\", mpfr_get_d(%s, %s));@ " id var (Mpfr.mpfr_rnd ()) + | Types.Treal -> Format.fprintf fmt "_put_double(\"%s\", %s);@ " id var + | _ -> Format.eprintf "Impossible to print the _put_xx for type %a@.@?" Types.print_ty typ; assert false + ) scopes_vars + +let update_machine machine = + let stateassign vdecl = + MStateAssign (vdecl, mk_val (LocalVar vdecl) vdecl.var_type) + in + let local_decls = machine.mstep.step_inputs + (* @ machine.mstep.step_outputs *) + @ machine.mstep.step_locals + in + { machine with + mmemory = machine.mmemory @ local_decls; + mstep = { + machine.mstep with + step_instrs = machine.mstep.step_instrs + @ (MComment "Registering all flows")::(List.map stateassign local_decls) + + } + } + + +module Plugin = +struct + let name = "scopes" + let is_active () = + !option_scopes + + let show_scopes () = + !option_show_scopes && ( + Compiler_common.check_main (); + true) + + let options = [ + "-select", Arg.String register_scopes, "specifies which variables to log"; + "-input", Arg.String register_inputs, "specifies the simulation input"; + "-show-possible-scopes", Arg.Set option_show_scopes, "list possible variables to log"; + "-select-all", Arg.Set option_all_scopes, "select all possible variables to log"; + "-select-mem", Arg.Set option_mem_scopes, "select all memory variables to log"; + "-select-inputs", Arg.Set option_input_scopes, "select all input variables to log"; + ] + + let activate () = + option_scopes := true; + Options.optimization := 0; (* no optimization *) + Options.salsa_enabled := false; (* No salsa *) + () + + let rec is_valid_path path nodename prog machines = + let nodescopes = compute_scopes prog nodename in + let m = get_machine nodename machines in + match path with + | [] -> assert false + | [vid] -> let res = List.exists (fun v -> v.var_id = vid) (m.mmemory @ m.mstep.step_inputs @ m.mstep.step_locals) in + (* if not res then *) + (* Format.eprintf "Variable %s cannot be found in machine %s@.Local vars are %a@." vid m.mname.node_id *) + (* (Utils.fprintf_list ~sep:", " Printers.pp_var) (m.mmemory @ m.mstep.step_inputs @ m.mstep.step_locals) *) + (* ; *) + res + + | inst::nodename::path' -> (* We use the scopes computed on the prog artifact *) + (* Format.eprintf "Path is %a@ Local scopes: @[<v>%a@ @]@." *) + (* (Utils.fprintf_list ~sep:"." Format.pp_print_string) path *) + (* (Utils.fprintf_list ~sep:";@ " *) + (* (fun fmt scope -> *) + (* Utils.fprintf_list ~sep:"." Format.pp_print_string fmt (scope_to_sl scope)) *) + (* ) *) + (* nodescopes; *) + if List.mem path (List.map scope_to_sl nodescopes) then ( + (* Format.eprintf "Valid local path, checking underneath@."; *) + is_valid_path path' nodename prog machines + ) + else + false + + (* let instok = List.exists (fun (inst', node) -> inst' = inst) m.minstances in *) + (* if not instok then Format.eprintf "inst = %s@." inst; *) + (* instok && *) + (* let instnode = fst (snd (List.find (fun (inst', node) -> inst' = inst) m.minstances)) in *) + (* is_valid_path path' (Corelang.node_of_top instnode).node_id prog machines *) + + let process_scopes main_node prog machines = + let all_scopes = compute_scopes prog !Options.main_node in + let selected_scopes = if !option_all_scopes then + List.map (fun s -> scope_to_sl s) all_scopes + else + !scopes_def + in + (* Making sure all scopes are defined and were not removed by various + optmizationq *) + let selected_scopes = + List.filter + (fun sl -> + let res = is_valid_path sl main_node prog machines in + if not res then + Format.eprintf "Scope %a is cancelled due to variable removal@." (Utils.fprintf_list ~sep:"." Format.pp_print_string) sl; + res + ) + selected_scopes + in + scopes_map := check_scopes main_node prog machines all_scopes selected_scopes; + (* Each machine is updated with fresh memories and declared as stateful *) + let machines = List.map update_machine machines in + machines + + let pp fmt = pp_scopes fmt !scopes_map + +end + +(* Local Variables: *) +(* compile-command:"make -C ../.." *) +(* End: *) diff --git a/src/printers.ml b/src/printers.ml index f2cb78de..2dbe35ed 100644 --- a/src/printers.ml +++ b/src/printers.ml @@ -29,9 +29,8 @@ let rec print_dec_struct_ty_field fmt (label, cty) = and print_dec_ty fmt cty = match (*get_repr_type*) cty with | Tydec_any -> fprintf fmt "Any" - | Tydec_int -> fprintf fmt "int" - | Tydec_real - | Tydec_float -> fprintf fmt "real" + | Tydec_int -> fprintf fmt "int" + | Tydec_real -> fprintf fmt "real" | Tydec_bool -> fprintf fmt "bool" | Tydec_clock cty' -> fprintf fmt "%a clock" print_dec_ty cty' | Tydec_const c -> fprintf fmt "%s" c @@ -57,8 +56,8 @@ let rec pp_struct_const_field fmt (label, c) = and pp_const fmt c = match c with | Const_int i -> pp_print_int fmt i - | Const_real r -> pp_print_string fmt r - | Const_float r -> pp_print_float fmt r + | Const_real (c,e,s) -> pp_print_string fmt s (*if e = 0 then pp_print_int fmt c else if e < 0 then Format.fprintf fmt "%ie%i" c (-e) else Format.fprintf fmt "%ie-%i" c e *) + (* | Const_float r -> pp_print_float fmt r *) | Const_tag t -> pp_print_string fmt t | Const_array ca -> Format.fprintf fmt "[%a]" (Utils.fprintf_list ~sep:"," pp_const) ca | Const_struct fl -> Format.fprintf fmt "{%a }" (Utils.fprintf_list ~sep:" " pp_struct_const_field) fl @@ -131,7 +130,7 @@ and pp_eexpr fmt e = and pp_expr_annot fmt expr_ann = let pp_annot fmt (kwds, ee) = - Format.fprintf fmt "(*! %t: %a *)" + Format.fprintf fmt "(*! %t: %a; *)" (fun fmt -> match kwds with | [] -> assert false | [x] -> Format.pp_print_string fmt x | _ -> Format.fprintf fmt "/%a/" (fprintf_list ~sep:"/" Format.pp_print_string) kwds) pp_eexpr ee in @@ -205,7 +204,7 @@ and pp_var_type_dec_desc fmt tdesc = | Tydec_any -> fprintf fmt "<any>" | Tydec_int -> fprintf fmt "int" | Tydec_real -> fprintf fmt "real" - | Tydec_float -> fprintf fmt "float" + (* | Tydec_float -> fprintf fmt "float" *) | Tydec_bool -> fprintf fmt "bool" | Tydec_clock t -> fprintf fmt "%a clock" pp_var_type_dec_desc t | Tydec_const t -> fprintf fmt "%s" t diff --git a/src/scheduling.ml b/src/scheduling.ml index 8238aa7c..e73540c2 100644 --- a/src/scheduling.ml +++ b/src/scheduling.ml @@ -17,14 +17,18 @@ open Causality type schedule_report = { + (* the scheduled node *) + node : node_desc; (* a schedule computed wrt the dependency graph *) schedule : ident list list; (* the set of unused variables (no output or mem depends on them) *) unused_vars : ISet.t; (* the table mapping each local var to its in-degree *) fanin_table : (ident, int) Hashtbl.t; + (* the dependency graph *) + dep_graph : IdentDepGraph.t; (* the table mapping each assignment to a reusable variable *) - reuse_table : (ident, var_decl) Hashtbl.t + (*reuse_table : (ident, var_decl) Hashtbl.t*) } (* Topological sort with a priority for variables belonging in the same equation lhs. @@ -125,7 +129,7 @@ let filter_original n vl = if vdecl.var_orig then v :: res else res) vl [] let schedule_node n = - let node_vars = get_node_vars n in + (* let node_vars = get_node_vars n in *) try let eq_equiv = ExprDep.node_eq_equiv n in let eq_equiv v1 v2 = @@ -134,13 +138,6 @@ let schedule_node n = with Not_found -> false in let n', g = global_dependency n in - Log.report ~level:5 - (fun fmt -> - Format.fprintf fmt - "dependency graph for node %s: %a" - n'.node_id - pp_dep_graph g - ); (* TODO X: extend the graph with inputs (adapt the causality analysis to deal with inputs compute: coi predecessors of outputs @@ -152,17 +149,17 @@ let schedule_node n = let sort = topological_sort eq_equiv g in let unused = Liveness.compute_unused_variables n gg in let fanin = Liveness.compute_fanin n gg in + { node = n'; schedule = sort; unused_vars = unused; fanin_table = fanin; dep_graph = gg; } - let (disjoint, reuse) = - if !Options.optimization >= 3 - then - let disjoint = Disjunction.clock_disjoint_map node_vars in - (disjoint, - Liveness.compute_reuse_policy n sort disjoint gg) - else - (Hashtbl.create 1, - Hashtbl.create 1) in + with (Causality.Cycle vl) as exc -> + let vl = filter_original n vl in + pp_error Format.err_formatter vl; + raise exc +let compute_node_reuse_table report = + let disjoint = Disjunction.clock_disjoint_map (get_node_vars report.node) in + let reuse = Liveness.compute_reuse_policy report.node report.schedule disjoint report.dep_graph in +(* if !Options.print_reuse then begin @@ -186,24 +183,44 @@ let schedule_node n = Liveness.pp_reuse_policy reuse ); end; - n', { schedule = sort; unused_vars = unused; fanin_table = fanin; reuse_table = reuse } - with (Causality.Cycle vl) as exc -> - let vl = filter_original n vl in - pp_error Format.err_formatter vl; - raise exc +*) + reuse + let schedule_prog prog = List.fold_right ( fun top_decl (accu_prog, sch_map) -> match top_decl.top_decl_desc with | Node nd -> - let nd', report = schedule_node nd in - {top_decl with top_decl_desc = Node nd'}::accu_prog, + let report = schedule_node nd in + {top_decl with top_decl_desc = Node report.node}::accu_prog, IMap.add nd.node_id report sch_map | _ -> top_decl::accu_prog, sch_map ) prog ([],IMap.empty) + + +let compute_prog_reuse_table report = + IMap.map compute_node_reuse_table report + +(* removes inlined local variables from schedule report, + which are now useless *) +let remove_node_inlined_locals locals report = + let is_inlined v = IMap.exists (fun l _ -> v = l) locals in + let schedule' = + List.fold_right (fun heads q -> let heads' = List.filter (fun v -> not (is_inlined v)) heads + in if heads' = [] then q else heads'::q) + report.schedule [] in + begin + IMap.iter (fun v _ -> Hashtbl.remove report.fanin_table v) locals; + IMap.iter (fun v _ -> let iv = ExprDep.mk_instance_var v + in Liveness.replace_in_dep_graph v iv report.dep_graph) locals; + { report with schedule = schedule' } + end + +let remove_prog_inlined_locals removed reuse = + IMap.mapi (fun id -> remove_node_inlined_locals (IMap.find id removed)) reuse let pp_eq_schedule fmt vl = match vl with @@ -222,7 +239,13 @@ let pp_schedule fmt node_schs = let pp_fanin_table fmt node_schs = IMap.iter (fun nd report -> - Format.fprintf fmt "%s : %a" nd Liveness.pp_fanin report.fanin_table) + Format.fprintf fmt "%s: %a" nd Liveness.pp_fanin report.fanin_table) + node_schs + +let pp_dep_graph fmt node_schs = + IMap.iter + (fun nd report -> + Format.fprintf fmt "%s dependency graph: %a" nd pp_dep_graph report.dep_graph) node_schs let pp_warning_unused fmt node_schs = @@ -241,6 +264,7 @@ let pp_warning_unused fmt node_schs = ) node_schs + (* Local Variables: *) (* compile-command:"make -C .." *) (* End: *) diff --git a/src/splitting.ml b/src/splitting.ml index e830b614..a41001d3 100644 --- a/src/splitting.ml +++ b/src/splitting.ml @@ -21,7 +21,7 @@ let rec tuple_split_expr expr = | Expr_ident _ -> [expr] | Expr_tuple elist -> elist | Expr_appl (id, args, r) -> - if Basic_library.is_internal_fun id + if Basic_library.is_homomorphic_fun id then let args_list = List.map tuple_split_expr (expr_list_of_expr args) in List.map diff --git a/src/stateless.ml b/src/stateless.ml index 3148e032..5ec30d4f 100755 --- a/src/stateless.ml +++ b/src/stateless.ml @@ -34,7 +34,7 @@ let rec check_expr expr = | Expr_merge (i, hl) -> List.for_all (fun (t, h) -> check_expr h) hl | Expr_appl (i, e', i') -> check_expr e' && - (Basic_library.is_internal_fun i || check_node (node_from_name i)) + (Basic_library.is_stateless_fun i || check_node (node_from_name i)) and compute_node nd = List.for_all (fun eq -> check_expr eq.eq_rhs) (get_node_eqs nd) and check_node td = @@ -56,6 +56,17 @@ and check_node td = let check_prog decls = List.iter (fun td -> ignore (check_node td)) decls + +let force_prog decls = + let force_node td = + match td.top_decl_desc with + | Node nd -> ( + nd.node_dec_stateless <- false; + nd.node_stateless <- Some false) + | _ -> () + in + List.iter (fun td -> ignore (force_node td)) decls + let check_compat_decl decl = match decl.top_decl_desc with | ImportedNode nd -> diff --git a/src/type_predef.ml b/src/type_predef.ml index 0690be22..e9c1cfb2 100755 --- a/src/type_predef.ml +++ b/src/type_predef.ml @@ -26,7 +26,6 @@ let type_arrow ty1 ty2 = new_ty (Tarrow (ty1, ty2)) let type_array d ty = new_ty (Tarray (d, ty)) let type_static d ty = new_ty (Tstatic (d, ty)) - let type_unary_bool_op = new_ty (Tarrow (type_bool, type_bool)) diff --git a/src/types.ml b/src/types.ml index 299131ff..efa4aaa0 100755 --- a/src/types.ml +++ b/src/types.ml @@ -198,14 +198,30 @@ let get_field_type ty label = | Tstruct fl -> (try Some (List.assoc label fl) with Not_found -> None) | _ -> None -let is_numeric_type ty = +let rec is_scalar_type ty = + match (repr ty).tdesc with + | Tstatic (_, ty) -> is_scalar_type ty + | Tbool + | Tint + | Treal -> true + | _ -> false + +let rec is_numeric_type ty = match (repr ty).tdesc with + | Tstatic (_, ty) -> is_numeric_type ty | Tint | Treal -> true | _ -> false -let is_bool_type ty = +let rec is_real_type ty = + match (repr ty).tdesc with + | Tstatic (_, ty) -> is_real_type ty + | Treal -> true + | _ -> false + +let rec is_bool_type ty = match (repr ty).tdesc with + | Tstatic (_, ty) -> is_bool_type ty | Tbool -> true | _ -> false @@ -288,7 +304,7 @@ let rec array_base_type ty = | _ -> ty let is_address_type ty = - is_array_type ty || is_struct_type ty + is_array_type ty || is_struct_type ty || (is_real_type ty && !Options.mpfr) let rec is_generic_type ty = match (dynamic_type ty).tdesc with @@ -313,10 +329,11 @@ let type_of_type_list tyl = else List.hd tyl -let type_list_of_type ty = +let rec type_list_of_type ty = match (repr ty).tdesc with - | Ttuple tl -> tl - | _ -> [ty] + | Tstatic (_, ty) -> type_list_of_type ty + | Ttuple tl -> tl + | _ -> [ty] (** [is_polymorphic ty] returns true if [ty] is polymorphic. *) let rec is_polymorphic ty = diff --git a/src/typing.ml b/src/typing.ml index 747240ee..a1cc9fe3 100755 --- a/src/typing.ml +++ b/src/typing.ml @@ -103,7 +103,7 @@ let rec type_coretype type_dim cty = | Tydec_any -> new_var () | Tydec_int -> Type_predef.type_int | Tydec_real -> Type_predef.type_real - | Tydec_float -> Type_predef.type_real + (* | Tydec_float -> Type_predef.type_real *) | Tydec_bool -> Type_predef.type_bool | Tydec_clock ty -> Type_predef.type_clock (type_coretype type_dim ty) | Tydec_const c -> Type_predef.type_const c @@ -261,7 +261,7 @@ and type_const loc c = match c with | Const_int _ -> Type_predef.type_int | Const_real _ -> Type_predef.type_real - | Const_float _ -> Type_predef.type_real + (* | Const_float _ -> Type_predef.type_real *) | Const_array ca -> let d = Dimension.mkdim_int loc (List.length ca) in let ty = new_var () in List.iter (fun e -> try_unify ty (type_const loc e) loc) ca; @@ -344,7 +344,7 @@ and type_subtyping_arg env in_main ?(sub=true) const real_arg formal_type = *) and type_appl env in_main loc const f args = let targs = List.map (type_expr env in_main const) args in - if Basic_library.is_internal_fun f && List.exists is_tuple_type targs + if Basic_library.is_homomorphic_fun f && List.exists is_tuple_type targs then try let targs = Utils.transpose_list (List.map type_list_of_type targs) in @@ -367,10 +367,8 @@ and type_dependent_call env in_main loc const f targs = begin List.iter2 (fun (a,t) ti -> let t' = type_add_const env (const || Types.get_static_value ti <> None) a t - in try_unify ~sub:true ti t' a.expr_loc; - ) targs tins; -(*Format.eprintf "Typing.type_dependent_call END@.";*) - touts; + in try_unify ~sub:true ti t' a.expr_loc) targs tins; + touts end (* type a simple call without dependent types @@ -575,14 +573,13 @@ let type_var_decl vd_env env vdecl = let type_dim d = begin type_subtyping_arg (env, vd_env) false true (expr_of_dimension d) Type_predef.type_int; - Dimension.eval Basic_library.eval_env eval_const d; end in let ty = type_coretype type_dim vdecl.var_dec_type.ty_dec_desc in let ty_static = if vdecl.var_dec_const - then Type_predef.type_static (Dimension.mkdim_var ()) ty + then Type_predef.type_static (Dimension.mkdim_var ()) ty else ty in (match vdecl.var_dec_value with | None -> () @@ -680,19 +677,19 @@ let type_top_consts env clist = let rec type_top_decl env decl = match decl.top_decl_desc with | Node nd -> ( - try - type_node env nd decl.top_decl_loc - with Error (loc, err) as exc -> ( - (*if !Options.global_inline then - Format.eprintf "Type error: failing node@.%a@.@?" - Printers.pp_node nd - ;*) - raise exc) + try + type_node env nd decl.top_decl_loc + with Error (loc, err) as exc -> ( + if !Options.global_inline then + Format.eprintf "Type error: failing node@.%a@.@?" + Printers.pp_node nd + ; + raise exc) ) | ImportedNode nd -> - type_imported_node env nd decl.top_decl_loc + type_imported_node env nd decl.top_decl_loc | Const c -> - type_top_const env c + type_top_const env c | TypeDef _ -> List.fold_left type_top_decl env (consts_of_enum_type decl) | Open _ -> env diff --git a/src/utils.ml b/src/utils.ml index 7be8bb14..ee8d9dd6 100755 --- a/src/utils.ml +++ b/src/utils.ml @@ -36,7 +36,8 @@ module IMap = Map.Make(IdentModule) module ISet = Set.Make(IdentModule) -let desome x = match x with Some x -> x | None -> failwith "desome" +exception DeSome +let desome x = match x with Some x -> x | None -> raise DeSome let option_map f o = match o with diff --git a/test/tests_ok_dev.list b/test/tests_ok_dev.list deleted file mode 100644 index 527cdab1..00000000 --- a/test/tests_ok_dev.list +++ /dev/null @@ -1,32 +0,0 @@ -./tests/tuples/tuples1.lus -./tests/tuples/tuples2.lus -./tests/arrays_arnaud/dummy_lib.lusi -./tests/arrays_arnaud/arrays.lus,,-check-access -./tests/arrays_arnaud/RelOpMatrix.lus -./tests/arrays_arnaud/access1.lus,,-check-access -./tests/arrays_arnaud/generic1.lus,,-lusi -./tests/arrays_arnaud/generic1.lusi -./tests/arrays_arnaud/generic1.lus -./tests/arrays_arnaud/generic2.lus -./tests/arrays_arnaud/generic3.lus,top,-dynamic -check-access -./tests/clocks/clocks1.lus,,-lusi -./tests/clocks/clocks1.lusi -./tests/clocks/clocks1.lus -./tests/clocks/clocks2.lus -./tests/clocks/clocks6.lus -./tests/clocks/clocks7.lus -./tests/clocks/clocks8.lus -./tests/clocks/clocks9.lus -./tests/clocks/oversampling0.lus,,-lusi -./tests/clocks/oversampling0.lusi -./tests/clocks/oversampling0.lus -./tests/lusic/test2.lusi -./tests/lusic/test1.lusi -./tests/lusic/test1.lus,as_soon_as -./tests/lusic/test2.lus -./tests/automata/aut1.lus -./tests/automata/heater3.lus -./tests/automata/heater4.lus -./tests/linear_ctl/libarrays.lusi -./tests/linear_ctl/ex1_mat.lus -./tests/linear_ctl/ex1_mat_xt.lus -- GitLab