From be68e8c5395f50ff5862e4610bacd33a41bffcd0 Mon Sep 17 00:00:00 2001 From: Erwan Jahier <jahier@imag.fr> Date: Mon, 23 Jun 2008 15:52:34 +0200 Subject: [PATCH] A new implementation of uniqueOutput that is more efficient, more compact, cleaner, and correct wrt negative steps in array slices. --- src/Makefile | 1 + src/test/should_work/Pascal/left.lus | 6 +- src/test/test.res.exp | 2 +- src/uniqueOutput.ml | 497 ++++++++++----------------- src/uniqueOutput.mli | 6 + 5 files changed, 195 insertions(+), 317 deletions(-) create mode 100644 src/uniqueOutput.mli diff --git a/src/Makefile b/src/Makefile index 982f1974..e5f82a44 100644 --- a/src/Makefile +++ b/src/Makefile @@ -50,6 +50,7 @@ SOURCES = \ ./evalClock.ml \ ./getEff.mli \ ./getEff.ml \ + ./uniqueOutput.mli \ ./uniqueOutput.ml \ ./lazyCompiler.ml \ ./lazyCompiler.mli \ diff --git a/src/test/should_work/Pascal/left.lus b/src/test/should_work/Pascal/left.lus index aa21f4f5..8d9d5415 100644 --- a/src/test/should_work/Pascal/left.lus +++ b/src/test/should_work/Pascal/left.lus @@ -7,12 +7,12 @@ type truc = struct { node toto(x : bool) returns (t : truc^3); let --- t[0].a[0..98 step 2][48..0 step -2] = true^25; - t[0].a[0..98 step 2][0..48 step 2] = true^25; + t[0].a[0..98 step 2][48..0 step -2] = true^25; +-- t[0].a[0..98 step 2][0..48 step 2] = true^25; t[0].a[0..98 step 2][1..49 step 2] = false^25; t[0].a[1..99 step 2][0] = true; t[0].a[1..99 step 2][1] = true; t[0].a[5..99 step 2] = false^48; - t[0].b = 42; + t[0].b = 42; t[1..2] = (truc { a = true^100; b = 0 })^2; tel diff --git a/src/test/test.res.exp b/src/test/test.res.exp index 3f22b15b..ad14bdd3 100644 --- a/src/test/test.res.exp +++ b/src/test/test.res.exp @@ -2960,7 +2960,7 @@ Opening file /home/jahier/lus2lic/src/test/should_work/Pascal/left.lus type left::truc = left::truc {a : bool^100; b : int}; node left::toto(x:bool) returns (t:left::truc^3); let - t[0].a[0..98 step 2][0..48 step 2] = true^25; + t[0].a[0..98 step 2][48..0 step -2] = true^25; t[0].a[0..98 step 2][1..49 step 2] = false^25; t[0].a[1..99 step 2][0] = true; t[0].a[1..99 step 2][1] = true; diff --git a/src/uniqueOutput.ml b/src/uniqueOutput.ml index 8c322759..e2adcb25 100644 --- a/src/uniqueOutput.ml +++ b/src/uniqueOutput.ml @@ -1,334 +1,205 @@ - +(** Time-stamp: <modified the 23/06/2008 (at 15:51) by Erwan Jahier> *) open CompiledData open Lxm open Errors -(* given a list of (output) [var_info_eff] [vl], and a list of - [left_eff] [lel], we want to check that each variable is defined - exactly once by the [left_eff]. To do that, we first transform - [vl] into a list of variable atoms. -*) -type atom = string -(* | Var of (var_info_eff) *) -(* | Field of (atom * Ident.t) *) -(* | Array of (atom * int) *) - -let rec (var_info_eff_to_atoms: var_info_eff -> atom list) = - fun v -> - type_eff_to_atoms (Ident.to_string v.var_name_eff) v.var_type_eff - -and make_int_list acc i = if i < 0 then acc else make_int_list (i::acc) (i-1) - -and slice_info_to_int_list si i = - if ( - (si.se_step > 0 && i > si.se_last) - || - (si.se_step < 0 && i < si.se_last) - ) - then - [] - else - i::(slice_info_to_int_list si (i + si.se_step)) - - -and (type_eff_to_atoms : atom -> type_eff -> atom list) = - fun str type_eff -> - let res = - match type_eff with - | Bool_type_eff - | Int_type_eff - | Real_type_eff - | Any - | Overload - | External_type_eff _ - | Enum_type_eff(_,_) -> [str] - | Array_type_eff(te,i) -> - List.flatten - (List.map - (fun i -> type_eff_to_atoms (str ^ "[" ^(string_of_int i)^"]") te) - (make_int_list [] (i-1))) - - | Struct_type_eff(name, fl) -> - List.flatten - (List.map - (fun (id,(te,_)) -> - type_eff_to_atoms (str ^ "." ^(Ident.to_string id)) te - ) - fl) - in - res - -(* Generates the string corresponding to the [left_eff]. It returns - a list of strings because of the slices. Indeed, when generating - the string for the [left_eff] corresponding to "expr[2..3].a", we - actually want to generate 2 strings: - - - "expr[2].a", and - - "expr[3].a" - - le pb, c'est que pour "t[1..10][2..4]", je veux pas générer - - "t[1][2]", etc., - mais "t[3]", "t[4]", "t[5]" ! arg... il me faut faire quelque chose - pour les tranches de tranches. - - - meme chose pour "T[1..4 step 2][1]" en fait (qui vaut t[3]) - - il faudrait que je normalise tout ca avant. Il y a plein de facon de dire - la même chose. - - -> rendre le step positif - -> mise a plat des tranches de tranches - -> mise a plat d'une tranche suivis d'un acces à un tableau - +(*********************************************************************************) +(** [left_eff] is a kind of list, but which is in a « bad » order for + easy checking; [eff_left] contains just the same information, but + the list is made explicit and the infomartion (struct or array + accesses) is ordered in the « good » way. *) -and (left_eff_to_string : left_eff -> atom list) = - fun le -> - let res = - match le with - | LeftVarEff (v,_) -> [Ident.to_string v.var_name_eff] - | LeftFieldEff(le,id,te) -> - let strl = left_eff_to_string le in - List.map (fun str -> str^"."^(Ident.to_string id)) strl - | LeftArrayEff(le,i,te) -> - let strl = left_eff_to_string le in - List.map (fun str -> (str ^ "[" ^ (string_of_int i) ^ "]")) strl - - | LeftSliceEff(LeftSliceEff(le,si1,te1),si2,te2) -> - assert false + +type eff_left = var_info_eff * Lxm.t * filter list +and filter = + | Slice of int * int * int * type_eff + | Faccess of Ident.t * type_eff + | Aaccess of int * type_eff + +let rec (left_eff_to_eff_left: left_eff -> eff_left) = + fun le -> + let rec (aux : type_eff -> filter list -> left_eff -> eff_left) = + fun te_top acc le -> match le with + | LeftVarEff (v,lxm) -> v, lxm, acc + | LeftFieldEff(le,id,te) -> aux te (Faccess(id,te)::acc) le + | LeftArrayEff(le,i,te) -> aux te (Aaccess(i,te)::acc) le | LeftSliceEff(le,si,te) -> - let strl = left_eff_to_string le in - List.flatten - (List.map - (fun i -> List.map - (fun str -> (str^"["^(string_of_int i) ^ "]")) - strl) - (slice_info_to_int_list si si.se_first) - ) + aux te (Slice(si.se_first,si.se_last,si.se_step,te)::acc) le in - res - -and (left_eff_to_atoms : left_eff * Lxm.t -> (atom * Lxm.t) list) = - fun (le,lxm) -> - let res = - match le with - | LeftVarEff (v,lxm) -> List.map (fun a -> a,lxm) (var_info_eff_to_atoms v) - - | LeftArrayEff(LeftSliceEff(le1,si1,te1),l,te2) -> - (* le1[i..j step k][l] = le1[i + k*l] *) - let (i,j,k) = - if si1.se_step > 0 - then (si1.se_first, si1.se_last,si1.se_step) - else (si1.se_last, si1.se_first, - si1.se_step) - in - let new_l = i + l * k in - left_eff_to_atoms (LeftArrayEff(le1, new_l, te2), lxm) - - | LeftSliceEff(LeftSliceEff(le1,si1,te1),si2,te2) -> - (* we need we interpret on-the-fly slices of slices... - le1[i1..j1 step k1][i2..j2 step k2] = - le1[ i1+i2*k1 - .. i1+(j2+i2*(k1-1))*k2 - step k1*k2 ] - if k1 and k2 are positive. - - k1<0, k2>0 - - what happens if one (or both) are negative ? - - *) - let (i1,j1,k1) = (si1.se_first, si1.se_last,si1.se_step) in - let (i2,j2,k2) = (si2.se_first, si2.se_last,si2.se_step) in - let si12 = { - se_first = i1+i2*k1; - se_last = i1+(j2+i2*(k1-1))*k2; - se_step = k1 * k2; (* bug si < 0 ! *) - se_width = -1; - } - in - left_eff_to_atoms(LeftSliceEff(le1, si12,te2), lxm) - | LeftFieldEff(le2,_,te) - | LeftArrayEff(le2,_,te) - | LeftSliceEff(le2,_,Array_type_eff(te,_)) -> - let al = left_eff_to_string le in - List.map (fun a -> a,lxm) - (List.flatten (List.map (fun a -> type_eff_to_atoms a te) al)) + let te_top = (var_info_of_left_eff le).var_type_eff in + let (v,lxm,f) = aux te_top [] le in + let (_,f) = + (* we don't want to associate to each accessors the type of the + « accessed elements », but its own type. E.g., if "t" is an + array of bool, we want to associate 't[0]' and an array of + bool, and not to a bool. *) + List.fold_left + (fun (te_top,acc) el -> + match el with + | Slice(i,j,k,te) -> te, (Slice(i,j,k,te_top))::acc + | Faccess(id,te) -> te, (Faccess(id,te_top))::acc + | Aaccess(i,te) -> te, (Aaccess(i,te_top))::acc + ) + (te_top,[]) + f + in + (v,lxm, List.rev f) + +(*********************************************************************************) +(** Used to represent how much « defined » a variable is. *) +type var_def_state = + | VDS_def + | VDS_undef + | VDS_struct of (Ident.t * var_def_state) list + | VDS_array of var_def_state array + +let id2str = Ident.to_string +let int2str = string_of_int + +(* Returns the list of undefined variables *) +let (vds_to_string : var_info_eff -> var_def_state -> string list) = + fun v vds -> + let rec aux vds v acc = match vds with + | VDS_def -> acc + | VDS_undef -> v::acc + | VDS_struct(fl) -> + List.fold_left (fun acc (id,vds) -> aux vds (v^"."^(id2str id)) acc) acc fl + | VDS_array(a) -> fst + (Array.fold_left + (fun (acc,i) vds -> aux vds (v^"["^(int2str i) ^"]") acc, i+1) (acc,0) a) in - res - -module StrSet = Set.Make(String) + aux vds (id2str v.var_name_eff) [] + +(*********************************************************************************) +(** This is main function: it computes a new [var_def_state] from an + [var_def_state] and a [eff_left]. *) +let (update_var_def_state : var_def_state -> eff_left -> var_def_state) = + fun vds (v, lxm, filters) -> + let rec (update_vds : string -> var_def_state -> filter list -> var_def_state) = + fun v vds filters -> + if vds = VDS_def then + let msg = "\n*** Variable " ^ v ^ " is defined twice." in + raise (Compile_error(lxm, msg)) + else + match filters with + | [] -> VDS_def + | Slice(i,j,k,te)::tail -> update_slice v i j k tail + (match vds with + | VDS_array(a) -> a + | VDS_undef -> undef_array_of_type te + | _ -> assert false (* by type checking *) + ) + | Aaccess(i,te)::tail -> update_array_access v i tail + (match vds with + | VDS_array(a) -> a + | VDS_undef -> undef_array_of_type te + | _ -> assert false (* by type checking *) + ) + | Faccess(id,te)::tail -> update_field_access v id tail + (match vds with + | VDS_struct(fl) -> fl + | VDS_undef -> undef_struct_of_type te + | _ -> assert false (* by type checking *) + ) -let (check_aux : Lxm.t -> var_info_eff list -> (left_eff * Lxm.t) list -> unit) = - fun lxm vl lel -> - let l1 = List.flatten (List.map var_info_eff_to_atoms vl) in - let l2 = List.flatten (List.map left_eff_to_atoms lel) in -(* let _ = *) -(* print_string ("\noutputs to be defined: "); *) -(* List.iter (fun str -> print_string (str ^ ", ")) l1; *) -(* print_string ("\ndefined left eff: "); *) -(* List.iter (fun (str,_) -> print_string (str ^ ", ")) l2; *) -(* print_string "\n checking...\n"; *) -(* flush stdout *) -(* in *) - let set1 = List.fold_left (fun set x -> StrSet.add x set) StrSet.empty l1 in - let set2 = - List.fold_left - (fun set (elt,lxm) -> - if StrSet.mem elt set then StrSet.remove elt set else - let msg = - if StrSet.mem elt set1 then - "\n*** This variable is defined twice: " ^ elt - else - "\n*** This variable is undeclared: " ^ elt - in - raise (Compile_error(lxm, msg)) - ) - set1 - l2 + and undef_array_of_type = function + | Array_type_eff(_,size) -> Array.make size VDS_undef + | _ -> assert false + + and undef_struct_of_type = function + | Struct_type_eff(_,fl) -> List.map (fun (id,_) -> id, VDS_undef) fl + | _ -> assert false + + and array_for_all pred a = Array.fold_left (fun acc x -> acc && (pred x)) true a + and update_slice v i j k filters a = + let v = v^"["^(int2str i)^ ".."^(int2str j)^ + (if k=1 then "]" else " step "^(int2str k)^"]") + in + let sub_size = ((j-i)/k+1) in + let sub_a = Array.make sub_size VDS_undef in + let vds = + for l=0 to sub_size-1 do sub_a.(l) <- a.(i+l*k) done; + update_vds v (VDS_array sub_a) filters + in + (match vds with + | VDS_undef + | VDS_struct(_) -> assert false + | VDS_def -> for l=0 to sub_size-1 do a.(i+l*k) <- VDS_def done + | VDS_array sa -> for l=0 to sub_size-1 do a.(i+l*k) <- sub_a.(l) done + ); + if array_for_all (fun elt -> elt = VDS_def) a then VDS_def else VDS_array(a) + + and update_array_access v i filters a = + let v = v ^ "[" ^ (int2str i) ^ "]" in + let vds_i = update_vds v a.(i) filters in + a.(i) <- vds_i; + if array_for_all (fun elt -> elt = VDS_def) a then VDS_def else VDS_array(a) + + and update_field_access v id filters fl = + let vds_id = List.assoc id fl in + let v = v ^ "." ^ (id2str id) in + let vds = update_vds v vds_id filters in + let fl = replace_field id vds fl in + if List.for_all (fun (_,vds) -> vds = VDS_def) fl + then VDS_def + else VDS_struct(fl) + + and replace_field fn new_fv l = + match l with + | (id,fv)::tail -> + if id = fn then (id,new_fv)::tail + else (id,fv)::(replace_field fn new_fv tail) + | [] -> assert false (* fn is necessarily in l *) in - if not (StrSet.is_empty set2) then - let vars = (StrSet.fold (fun elt acc -> elt::acc) set2 []) in - let msg = - (if List.length vars = 1 then - "\n*** The following variable is undefined: " - else - "\n*** The following variables are undefined: ") ^ - (String.concat ", " vars) - in - raise (Compile_error(lxm, msg)) + let res = update_vds (id2str v.var_name_eff) vds filters in + res -(* Check that each output is defined exactly once *) +(*********************************************************************************) +(** Sort out the left_eff according to the variable they define. *) +module VarMap = Map.Make(struct type t = var_info_eff let compare = compare end) +let (partition_var : left_eff list -> (left_eff list) VarMap.t) = + fun l -> + let f tab le = + let v = var_info_of_left_eff le in + try VarMap.add v (le::(VarMap.find v tab)) tab + with Not_found -> VarMap.add v [le] tab + in + List.fold_left f VarMap.empty l + +(*********************************************************************************) +(* exported *) let (check : CompiledData.node_exp_eff -> Lxm.t -> unit) = fun node lxm -> - let ol = match node.loclist_eff with - None -> node.outlist_eff + let vars_to_check = match node.loclist_eff with + | None -> node.outlist_eff | Some l -> node.outlist_eff @ l + in + let (check_one_var : Lxm.t -> var_info_eff -> left_eff list -> unit) = + fun lxm v lel -> + let ell = List.map left_eff_to_eff_left lel in + match List.fold_left update_var_def_state VDS_undef ell with + | VDS_def -> () + | vds -> + let msg = "\n*** Undefined variable(s): " ^ + (String.concat ", " (vds_to_string v vds)) + in + raise (Compile_error(lxm, msg)) in match node.def_eff with | ExternEff | AbstractEff -> () - | BodyEff{ eqs_eff = eql } -> - let lpll = - List.map - (fun ({ it = (lel,_); src = lxm }) -> - List.map (fun le -> (le,lxm)) lel - ) - eql - in - let (lpl:(left_eff * Lxm.t) list) = List.flatten lpll in - - check_aux lxm ol lpl - - -(* Then, for each chunk, we search in [lel] a element [le] that - concerns v, and we compute the list of chunks that correspond to - the part of a that is not covered by [le]. That new list of chunks - is added to the list of chunks to be checked. And we continue - until there are no more chunks or no more [left_eff]. - - In order to reduce the complexity of that algo, we first partition - the [lel] according to the variable they define. -*) - - -(* module VarMap = Map.Make(struct type t=var_info_eff let compare = compare end) *) -(* *) -(* let (partition_var : left_eff list -> (left_eff list) VarMap.t) = *) -(* fun l -> *) -(* List.fold_left *) -(* (fun tab le -> *) -(* let v = var_info_of_left_eff le in *) -(* try VarMap.add v (le::(VarMap.find v tab)) tab *) -(* with Not_found -> VarMap.add v [le] tab *) -(* ) *) -(* VarMap.empty *) -(* l *) -(* *) -(* *) -(* (* Hence, given a list of [left_eff] that all concerns v, it is less *) -(* expensive (we operate on smaller lists) to check that [v] is *) -(* completety and uniquely defined. *) -(* *) -(* The idea to perform that check is to, from a left_eff and from a var_info_eff, *) -(* generates the list of atomic variables they defined. e.g., an array of *) -(* size 10 will generated 10 atomic variables. Then, we just have to *) -(* compare the list that comes from the left_eff, and the one that comes from *) -(* the output variables. *) -(* *) -(* *) *) -(* *) -(* type atom = *) -(* | Var of *) -(* | Field of *) -(* let ( *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* *) -(* let (check_one_chunk : chunk -> left_eff -> chunk list) = *) -(* fun c le -> match c,le with *) -(* | Var v -> ( *) -(* match le with *) -(* | LeftVarEff (_,_) -> [] *) -(* | LeftFieldEff(le,id,Struct_type_eff(_,fl)) -> *) -(* let fl = List.filter (fun f -> f <> id) fl in *) -(* List.map (fun (id, (te,_)) -> ) fl *) -(* | LeftArrayEff(le,i,_) -> *) -(* | LeftSliceEff(le,si,_) -> *) -(* ) *) -(* *) -(* | Field(c,id), LeftFieldEff(le,id,_) -> *) -(* | Array(c,i), LeftArrayEff(le,i,_) -> *) -(* | Slice(c, low, high),LeftSliceEff(le,si,_) -> *) -(* ddd *) -(* *) -(* *) -(* let (reconstruct : var_info_eff -> chunk list -> *) -(* *) -(* *) -(* *) -(* let (check_one_var : var_info_eff -> left_eff_list -> unit) = *) -(* fun v lel -> *) -(* let rec (aux : chunk list -> left_eff_list -> unit) = *) -(* fun cl lel -> *) -(* match cl with *) -(* | [] -> if lel <> [] then failwith "output variable defined twice" *) -(* | x::cl -> let new_chunks = check_one_chunk x lel *) -(* in *) -(* aux [Var v] lel *) -(* *) -(* let (check_one_chunk : chunk -> left_eff list -> chunk list) = *) -(* fun al le -> *) -(* (* checks that le is used in [lel]: *) -(* Removes the part that is used, but it migth add some chunks *) *) -(* match le with *) -(* | LeftVarEff(v,_) -> ( *) -(* let (al1, al2) = List.partition (fun a -> a = Var(v)) al in *) -(* match al1 with *) -(* | [] -> failwith "output variable undefined" *) -(* | _::_ -> failwith "output variable defined twice" *) -(* | [_] -> al2 *) -(* ) *) -(* | LeftFieldEff(le,id,_) -> ( *) -(* let (al1, al2) = List.partition (fun a -> a = Field(_,id)) al in *) -(* match al1 with *) -(* | [] -> failwith "output variable undefined" *) -(* | _::_ -> failwith "output variable defined twice" *) -(* | [_] -> al2 *) -(* ) *) -(* | LeftArrayEff(le,i,_) -> *) -(* | LeftSliceEff(le, si,_) -> *) -(* *) -(* (* (* (* *) *) *) *) + | BodyEff{ eqs_eff = eql } -> + let lel = List.flatten (List.map (fun {it=(left,_)} -> left) eql) in + let lel_map = partition_var lel in + List.iter + (fun v -> + try check_one_var lxm v (VarMap.find v lel_map) + with Not_found -> + let msg = "\n*** Undefined variable: " ^ (id2str v.var_name_eff) + in + raise (Compile_error(lxm, msg)) + ) + vars_to_check diff --git a/src/uniqueOutput.mli b/src/uniqueOutput.mli new file mode 100644 index 00000000..5bbab223 --- /dev/null +++ b/src/uniqueOutput.mli @@ -0,0 +1,6 @@ +(** Time-stamp: <modified the 23/06/2008 (at 15:48) by Erwan Jahier> *) + +(** Check that each output and each local variable is defined at most + and at least once. *) + +val check : CompiledData.node_exp_eff -> Lxm.t -> unit -- GitLab