summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBrian Campbell2018-02-01 10:35:54 +0000
committerBrian Campbell2018-02-01 18:40:34 +0000
commit190c3b41a3ea4dfba2958b236e6a0c15f511f942 (patch)
treedfe47cd8074a60be5482570e09b096adc46c67a9 /src
parent70bb3838838a222dd008a443bd941d5487fe20c7 (diff)
Make mono add case expressions for size tyvars without a corresponding arg
Diffstat (limited to 'src')
-rw-r--r--src/monomorphise.ml244
1 files changed, 176 insertions, 68 deletions
diff --git a/src/monomorphise.ml b/src/monomorphise.ml
index 873154cb..62ae92ce 100644
--- a/src/monomorphise.ml
+++ b/src/monomorphise.ml
@@ -2103,6 +2103,17 @@ module ArgSplits = Map.Make (struct
end)
type arg_splits = match_detail ArgSplits.t
+(* Function id, funcl loc for adding splits on sizes in the body when
+ there's no corresponding argument *)
+module ExtraSplits = Map.Make (struct
+ type t = id * Parse_ast.l
+ let compare (id,l) (id',l') =
+ let x = Id.compare id id' in
+ if x <> 0 then x else
+ compare l l'
+end)
+type extra_splits = (match_detail KBindings.t) ExtraSplits.t
+
(* Arguments that we should look at in callers *)
module CallerArgSet = Set.Make (struct
type t = id * int
@@ -2129,7 +2140,7 @@ module StringSet = Set.Make (struct
end)
type dependencies =
- | Have of arg_splits
+ | Have of arg_splits * extra_splits
| Unknown of Parse_ast.l * string
let string_of_match_detail = function
@@ -2142,6 +2153,26 @@ let string_of_argsplits s =
string_of_id id ^ "." ^ string_of_loc l ^ string_of_match_detail detail)
(ArgSplits.bindings s))
+let string_of_lx lx =
+ let open Lexing in
+ Printf.sprintf "%s,%d,%d,%d" lx.pos_fname lx.pos_lnum lx.pos_bol lx.pos_cnum
+
+let rec simple_string_of_loc = function
+ | Parse_ast.Unknown -> "Unknown"
+ | Parse_ast.Int (s,None) -> "Int(" ^ s ^ ",None)"
+ | Parse_ast.Int (s,Some l) -> "Int(" ^ s ^ ",Some("^simple_string_of_loc l^"))"
+ | Parse_ast.Generated l -> "Generated(" ^ simple_string_of_loc l ^ ")"
+ | Parse_ast.Range (lx1,lx2) -> "Range(" ^ string_of_lx lx1 ^ "->" ^ string_of_lx lx2 ^ ")"
+
+let string_of_extra_splits s =
+ String.concat ", "
+ (List.map (fun ((id,l),ks) ->
+ string_of_id id ^ "." ^ simple_string_of_loc l ^ ":" ^
+ (String.concat "," (List.map (fun (kid,detail) ->
+ string_of_kid kid ^ "." ^ string_of_match_detail detail)
+ (KBindings.bindings ks))))
+ (ExtraSplits.bindings s))
+
let string_of_callerset s =
String.concat ", " (List.map (fun (id,arg) -> string_of_id id ^ "." ^ string_of_int arg)
(CallerArgSet.elements s))
@@ -2151,8 +2182,8 @@ let string_of_callerkidset s =
(CallerKidSet.elements s))
let string_of_dep = function
- | Have args ->
- "Have (" ^ string_of_argsplits args ^ ")"
+ | Have (args,extras) ->
+ "Have (" ^ string_of_argsplits args ^ ";" ^ string_of_extra_splits extras ^ ")"
| Unknown (l,msg) -> "Unknown " ^ msg ^ " at " ^ Reporting_basic.loc_to_string l
(* If a callee uses a type variable as a size, does it need to be split in the
@@ -2163,12 +2194,16 @@ type call_dep =
| Parents of CallerKidSet.t
(* Result of analysing the body of a function. The split field gives
- the arguments to split based on the body alone, and the failures
- field where we couldn't do anything. The other fields are used at
- the end for the interprocedural phase. *)
+ the arguments to split based on the body alone, the extra_splits
+ field where we want to case split on a size type variable but
+ there's no corresponding argument so we introduce a case
+ expression, and the failures field where we couldn't do anything.
+ The other fields are used at the end for the interprocedural
+ phase. *)
type result = {
split : arg_splits;
+ extra_splits : extra_splits;
failures : StringSet.t Failures.t;
(* Dependencies for type variables of each fn called, so that
if the fn uses one for a bitvector size we can track it back *)
@@ -2178,6 +2213,7 @@ type result = {
let empty = {
split = ArgSplits.empty;
+ extra_splits = ExtraSplits.empty;
failures = Failures.empty;
split_on_call = Bindings.empty;
kid_in_caller = CallerKidSet.empty
@@ -2191,26 +2227,29 @@ let merge_detail _ x y =
when l1 = l2 && forall2 pat_eq ps1 ps2 -> x
| _ -> Some Total
+let opt_merge f _ x y =
+ match x,y with
+ | None, _ -> y
+ | _, None -> x
+ | Some x, Some y -> Some (f x y)
+
+let merge_extras = ExtraSplits.merge (opt_merge (KBindings.merge merge_detail))
+
let dmerge x y =
match x,y with
| Unknown (l,s), _ -> Unknown (l,s)
| _, Unknown (l,s) -> Unknown (l,s)
- | Have args, Have args' ->
- Have (ArgSplits.merge merge_detail args args')
+ | Have (args,extras), Have (args',extras') ->
+ Have (ArgSplits.merge merge_detail args args',
+ merge_extras extras extras')
-let dempty = Have ArgSplits.empty
-
-let dopt_merge k x y =
- match x, y with
- | None, _ -> y
- | _, None -> x
- | Some x, Some y -> Some (dmerge x y)
+let dempty = Have (ArgSplits.empty, ExtraSplits.empty)
let dep_bindings_merge a1 a2 =
- Bindings.merge dopt_merge a1 a2
+ Bindings.merge (opt_merge dmerge) a1 a2
let dep_kbindings_merge a1 a2 =
- KBindings.merge dopt_merge a1 a2
+ KBindings.merge (opt_merge dmerge) a1 a2
let call_kid_merge k x y =
match x, y with
@@ -2239,6 +2278,7 @@ let failure_merge _ x y =
let merge rs rs' = {
split = ArgSplits.merge merge_detail rs.split rs'.split;
+ extra_splits = merge_extras rs.extra_splits rs'.extra_splits;
failures = Failures.merge failure_merge rs.failures rs'.failures;
split_on_call = Bindings.merge call_arg_merge rs.split_on_call rs'.split_on_call;
kid_in_caller = CallerKidSet.union rs.kid_in_caller rs'.kid_in_caller
@@ -2374,9 +2414,9 @@ let mk_subrange_pattern vannot vstart vend =
let refine_dependency env (E_aux (e,(l,annot)) as exp) pexps =
let check_dep id ctx =
match Bindings.find id env.var_deps with
- | Have args -> begin
- match ArgSplits.bindings args with
- | [(id',loc),Total] when Id.compare id id' == 0 ->
+ | Have (args,extras) -> begin
+ match ArgSplits.bindings args, ExtraSplits.bindings extras with
+ | [(id',loc),Total], [] when Id.compare id id' == 0 ->
(match Util.map_all (function
| Pat_aux (Pat_exp (pat,_),_) -> Some (ctx pat)
| Pat_aux (Pat_when (_,_,_),_) -> None) pexps
@@ -2388,7 +2428,8 @@ let refine_dependency env (E_aux (e,(l,annot)) as exp) pexps =
(l, "No location for pattern match: " ^ string_of_exp exp));
None)
else
- Some (Have (ArgSplits.singleton (id,loc) (Partial (pats,l))))
+ Some (Have (ArgSplits.singleton (id,loc) (Partial (pats,l)),
+ ExtraSplits.empty))
| None -> None)
| _ -> None
end
@@ -2643,9 +2684,11 @@ let rec analyse_exp fn_id env assigns (E_aux (e,(l,annot)) as exp) =
{ r with kid_in_caller = CallerKidSet.add (fn_id,v) r.kid_in_caller }
| _ ->
match deps_of_nexp env.kid_deps [] size_nexp with
- | Have args ->
+ | Have (args,extras) ->
{ r with
- split = ArgSplits.merge merge_detail r.split args }
+ split = ArgSplits.merge merge_detail r.split args;
+ extra_splits = merge_extras r.extra_splits extras
+ }
| Unknown (l,msg) ->
{ r with
failures =
@@ -2689,7 +2732,7 @@ let translate_id (Id_aux (_,l) as id) =
| _ -> None
in aux l
-let initial_env fn_id (TypQ_aux (tq,_)) pat set_assertions =
+let initial_env fn_id fn_l (TypQ_aux (tq,_)) pat set_assertions =
let pats =
match pat with
| P_aux (P_tup pats,_) -> pats
@@ -2697,19 +2740,22 @@ let initial_env fn_id (TypQ_aux (tq,_)) pat set_assertions =
in
(* For the type in an annotation, produce the corresponding tyvar (if any),
and a default case split (a set if there's one, a full case split if not). *)
- let kid_and_default_split annot =
+ let kid_of_annot annot =
let env = env_of_annot annot in
let Typ_aux (typ,_) = Env.base_typ_of env (typ_of_annot annot) in
match typ with
| Typ_app (Id_aux (Id "atom",_),[Typ_arg_aux (Typ_arg_nexp (Nexp_aux (Nexp_var kid,_)),_)]) ->
- (match KBindings.find kid set_assertions with
- | (l,is) ->
- let l' = Generated l in
- let pats = List.map (fun n -> P_aux (P_lit (L_aux (L_num n,l')),(l',snd annot))) is in
- let pats = pats @ [P_aux (P_wild,(l',snd annot))] in
- Some kid, Partial (pats,l)
- | exception Not_found -> Some kid, Total)
- | _ -> None, Total
+ Some kid
+ | _ -> None
+ in
+ let default_split annot kid =
+ match KBindings.find kid set_assertions with
+ | (l,is) ->
+ let l' = Generated l in
+ let pats = List.map (fun n -> P_aux (P_lit (L_aux (L_num n,l')),(l',annot))) is in
+ let pats = pats @ [P_aux (P_wild,(l',annot))] in
+ Partial (pats,l)
+ | exception Not_found -> Total
in
let arg i pat =
let rec aux (P_aux (p,(l,annot))) =
@@ -2730,7 +2776,7 @@ let initial_env fn_id (TypQ_aux (tq,_)) pat set_assertions =
match translate_id id with
| Some id' ->
ArgSplits.add id' Total s,
- Bindings.add id (Have (ArgSplits.singleton id' Total)) v,
+ Bindings.add id (Have (ArgSplits.singleton id' Total, ExtraSplits.empty)) v,
k
| None ->
s,
@@ -2742,13 +2788,14 @@ let initial_env fn_id (TypQ_aux (tq,_)) pat set_assertions =
begin
match translate_id id with
| Some id' ->
- let kid_opt, split = kid_and_default_split (l,annot) in
+ let kid_opt = kid_of_annot (l,annot) in
+ let split = Util.option_cases kid_opt (default_split annot) (fun () -> Total) in
let s = ArgSplits.singleton id' split in
s,
- Bindings.singleton id (Have s),
+ Bindings.singleton id (Have (s, ExtraSplits.empty)),
(match kid_opt with
| None -> KBindings.empty
- | Some kid -> KBindings.singleton kid (Have s))
+ | Some kid -> KBindings.singleton kid (Have (s, ExtraSplits.empty)))
| None ->
ArgSplits.empty,
Bindings.singleton id (Unknown (l, ("Unable to give location for " ^ string_of_id id))),
@@ -2756,7 +2803,7 @@ let initial_env fn_id (TypQ_aux (tq,_)) pat set_assertions =
end
| P_var (pat,kid) ->
let s,v,k = aux pat in
- s,v,KBindings.add kid (Have s) k
+ s,v,KBindings.add kid (Have (s, ExtraSplits.empty)) k
| P_app (_,pats) -> of_list pats
| P_record (fpats,_) -> of_list (List.map (fun (FP_aux (FP_Fpat (_,p),_)) -> p) fpats)
| P_vector pats
@@ -2782,9 +2829,14 @@ let initial_env fn_id (TypQ_aux (tq,_)) pat set_assertions =
let kid_deps = List.fold_left dep_kbindings_merge KBindings.empty kid_deps in
let note_no_arg kid_deps kid =
if KBindings.mem kid kid_deps then kid_deps
- else KBindings.add kid (Unknown (kid_loc kid,
- "No argument corresponding to parameter " ^
- string_of_kid kid)) kid_deps
+ else
+ (* When there's no argument to case split on for a kid, we'll add a
+ case expression instead *)
+ let env = pat_env_of pat in
+ let split = default_split (Some (env,int_typ,no_effect)) kid in
+ let extra_splits = ExtraSplits.singleton (fn_id, fn_l)
+ (KBindings.singleton kid split) in
+ KBindings.add kid (Have (ArgSplits.empty, extra_splits)) kid_deps
in
let kid_deps = List.fold_left note_no_arg kid_deps top_kids in
{ top_kids = top_kids; var_deps = var_deps; kid_deps = kid_deps }
@@ -2889,17 +2941,24 @@ let print_result r =
(Failures.bindings r.failures)))) in
()
-let analyse_funcl debug tenv (FCL_aux (FCL_Funcl (id,pexp),_)) =
+let analyse_funcl debug tenv (FCL_aux (FCL_Funcl (id,pexp),(l,_))) =
let _ = if debug > 2 then print_endline (string_of_id id) else () in
let pat,guard,body,_ = destruct_pexp pexp in
let (tq,_) = Env.get_val_spec id tenv in
let set_assertions = find_set_assertions body in
let _ = if debug > 2 then print_set_assertions set_assertions in
- let aenv = initial_env id tq pat set_assertions in
+ let aenv = initial_env id l tq pat set_assertions in
let _,_,r = analyse_exp id aenv Bindings.empty body in
let r = match guard with
| None -> r
| Some exp -> let _,_,r' = analyse_exp id aenv Bindings.empty exp in
+ let r' =
+ if ExtraSplits.is_empty r'.extra_splits
+ then r'
+ else merge r' { empty with failures =
+ Failures.singleton l (StringSet.singleton
+ "Case splitting size tyvars in guards not supported") }
+ in
merge r r'
in
let _ = if debug > 2 then print_result r else ()
@@ -2911,55 +2970,99 @@ let analyse_def debug env = function
| _ -> empty
+
+let argset_to_list splits =
+ let l = ArgSplits.bindings splits in
+ let argelt = function
+ | ((id,(file,loc)),Total) -> ((file,loc),string_of_id id,None)
+ | ((id,(file,loc)),Partial (pats,l)) -> ((file,loc),string_of_id id,Some (pats,l))
+ in
+ List.map argelt l
+
let analyse_defs debug env (Defs defs) =
let r = List.fold_left (fun r d -> merge r (analyse_def debug env d)) empty defs in
(* Resolve the interprocedural dependencies *)
let rec separate_deps = function
- | Have splits ->
- splits, Failures.empty
+ | Have (splits, extras) ->
+ splits, extras, Failures.empty
| Unknown (l,msg) ->
- ArgSplits.empty , Failures.singleton l (StringSet.singleton ("Unable to monomorphise dependency: " ^ msg))
+ ArgSplits.empty, ExtraSplits.empty,
+ Failures.singleton l (StringSet.singleton ("Unable to monomorphise dependency: " ^ msg))
and chase_kid_caller (id,kid) =
match Bindings.find id r.split_on_call with
| kid_deps -> begin
match KBindings.find kid kid_deps with
| InFun deps -> separate_deps deps
- | Parents fns -> CallerKidSet.fold add_kid fns (ArgSplits.empty, Failures.empty)
- | exception Not_found -> ArgSplits.empty,Failures.empty
+ | Parents fns -> CallerKidSet.fold add_kid fns (ArgSplits.empty, ExtraSplits.empty, Failures.empty)
+ | exception Not_found -> ArgSplits.empty,ExtraSplits.empty,Failures.empty
end
- | exception Not_found -> ArgSplits.empty,Failures.empty
- and add_kid k (splits,fails) =
- let splits',fails' = chase_kid_caller k in
- ArgSplits.merge merge_detail splits splits', Failures.merge failure_merge fails fails'
+ | exception Not_found -> ArgSplits.empty,ExtraSplits.empty,Failures.empty
+ and add_kid k (splits,extras,fails) =
+ let splits',extras',fails' = chase_kid_caller k in
+ ArgSplits.merge merge_detail splits splits',
+ merge_extras extras extras',
+ Failures.merge failure_merge fails fails'
in
let _ = if debug > 1 then print_result r else () in
- let splits,fails = CallerKidSet.fold add_kid r.kid_in_caller (r.split,r.failures) in
+ let splits,extras,fails = CallerKidSet.fold add_kid r.kid_in_caller (r.split,r.extra_splits,r.failures) in
let _ =
if debug > 0 then
(print_endline "Final splits:";
- print_endline (string_of_argsplits splits))
+ print_endline (string_of_argsplits splits);
+ print_endline (string_of_extra_splits extras))
else ()
in
+ let splits = argset_to_list splits in
if Failures.is_empty fails
- then (true,splits) else
+ then (true,splits,extras) else
begin
Failures.iter (fun l msgs ->
Reporting_basic.print_err false false l "Monomorphisation" (String.concat "\n" (StringSet.elements msgs)))
fails;
- (false, splits)
+ (false, splits,extras)
end
-let argset_to_list splits =
- let l = ArgSplits.bindings splits in
- let argelt = function
- | ((id,(file,loc)),Total) -> ((file,loc),string_of_id id,None)
- | ((id,(file,loc)),Partial (pats,l)) -> ((file,loc),string_of_id id,Some (pats,l))
- in
- List.map argelt l
end
+let add_extra_splits extras (Defs defs) =
+ let success = ref true in
+ let add_to_body extras (E_aux (_,(l,annot)) as e) =
+ let l = Generated l in
+ KBindings.fold (fun kid detail exp ->
+ match detail with
+ | Analysis.Total ->
+ (success := false;
+ Reporting_basic.print_err false true (kid_loc kid) "Monomorphisation"
+ ("Don't know how to case split type variable " ^ string_of_kid kid);
+ e)
+ | Analysis.Partial (pats,_) ->
+ let pexps = List.map (fun p -> Pat_aux (Pat_exp (p,e),(l,annot))) pats
+ in
+ let nexp = Nexp_aux (Nexp_var kid,l) in
+ E_aux (E_case
+ (E_aux (E_sizeof nexp,
+ (l,Some (env_of e,atom_typ nexp,no_effect))),
+ pexps),(l,annot))
+ ) extras e
+ in
+ let add_to_funcl (FCL_aux (FCL_Funcl (id,Pat_aux (pexp,peannot)),(l,annot))) =
+ let pexp =
+ match Analysis.ExtraSplits.find (id,l) extras with
+ | extras ->
+ (match pexp with
+ | Pat_exp (p,e) -> Pat_exp (p,add_to_body extras e)
+ | Pat_when (p,g,e) -> Pat_when (p,g,add_to_body extras e))
+ | exception Not_found -> pexp
+ in FCL_aux (FCL_Funcl (id,Pat_aux (pexp,peannot)),(l,annot))
+ in
+ let add_to_def = function
+ | DEF_fundef (FD_aux (FD_function (re,ta,ef,funcls),annot)) ->
+ DEF_fundef (FD_aux (FD_function (re,ta,ef,List.map add_to_funcl funcls),annot))
+ | d -> d
+ in !success, Defs (List.map add_to_def defs)
+
module MonoRewrites =
struct
@@ -3202,17 +3305,22 @@ let monomorphise opts splits env defs =
else (defs,env)
in
(*let _ = Pretty_print.pp_defs stdout defs in*)
- let ok_analysis, new_splits =
+ let ok_analysis, new_splits, extra_splits =
if opts.auto
then
- let f,r = Analysis.analyse_defs opts.debug_analysis env defs in
+ let f,r,ex = Analysis.analyse_defs opts.debug_analysis env defs in
if f || opts.all_split_errors || opts.continue_anyway
- then f, Analysis.argset_to_list r
+ then f, r, ex
else raise (Reporting_basic.err_general Unknown "Unable to monomorphise program")
- else true, [] in
+ else true, [], Analysis.ExtraSplits.empty in
let splits = new_splits @ (List.map (fun (loc,id) -> (loc,id,None)) splits) in
+ let ok_extras, defs = add_extra_splits extra_splits defs in
+ let () = if ok_extras || opts.all_split_errors || opts.continue_anyway
+ then ()
+ else raise (Reporting_basic.err_general Unknown "Unable to monomorphise program")
+ in
let ok_split, defs = split_defs opts.all_split_errors splits defs in
- let () = if (ok_analysis && ok_split) || opts.continue_anyway
+ let () = if (ok_analysis && ok_extras && ok_split) || opts.continue_anyway
then ()
else raise (Reporting_basic.err_general Unknown "Unable to monomorphise program")
in