Move split_default_wrapper to Simplif

master
Mark Shinwell 2015-12-23 16:24:52 +00:00
parent 825bc346e8
commit b53e5789bc
4 changed files with 74 additions and 64 deletions

20
.depend
View File

@ -462,7 +462,7 @@ bytecomp/meta.cmi : bytecomp/instruct.cmi
bytecomp/printinstr.cmi : bytecomp/instruct.cmi
bytecomp/printlambda.cmi : bytecomp/lambda.cmi
bytecomp/runtimedef.cmi :
bytecomp/simplif.cmi : bytecomp/lambda.cmi
bytecomp/simplif.cmi : bytecomp/lambda.cmi typing/ident.cmi
bytecomp/switch.cmi :
bytecomp/symtable.cmi : utils/misc.cmi typing/ident.cmi \
bytecomp/cmo_format.cmi
@ -770,15 +770,17 @@ asmcomp/clambda.cmo : bytecomp/lambda.cmi typing/ident.cmi \
asmcomp/clambda.cmx : bytecomp/lambda.cmx typing/ident.cmx \
bytecomp/debuginfo.cmx parsing/asttypes.cmi asmcomp/clambda.cmi
asmcomp/closure.cmo : utils/warnings.cmi utils/tbl.cmi bytecomp/switch.cmi \
typing/primitive.cmi utils/misc.cmi parsing/location.cmi \
bytecomp/lambda.cmi typing/ident.cmi bytecomp/debuginfo.cmi \
asmcomp/compilenv.cmi utils/clflags.cmi asmcomp/clambda.cmi \
parsing/asttypes.cmi asmcomp/arch.cmo asmcomp/closure.cmi
bytecomp/simplif.cmi typing/primitive.cmi utils/misc.cmi \
parsing/location.cmi bytecomp/lambda.cmi typing/ident.cmi \
bytecomp/debuginfo.cmi asmcomp/compilenv.cmi utils/clflags.cmi \
asmcomp/clambda.cmi parsing/asttypes.cmi asmcomp/arch.cmo \
asmcomp/closure.cmi
asmcomp/closure.cmx : utils/warnings.cmx utils/tbl.cmx bytecomp/switch.cmx \
typing/primitive.cmx utils/misc.cmx parsing/location.cmx \
bytecomp/lambda.cmx typing/ident.cmx bytecomp/debuginfo.cmx \
asmcomp/compilenv.cmx utils/clflags.cmx asmcomp/clambda.cmx \
parsing/asttypes.cmi asmcomp/arch.cmx asmcomp/closure.cmi
bytecomp/simplif.cmx typing/primitive.cmx utils/misc.cmx \
parsing/location.cmx bytecomp/lambda.cmx typing/ident.cmx \
bytecomp/debuginfo.cmx asmcomp/compilenv.cmx utils/clflags.cmx \
asmcomp/clambda.cmx parsing/asttypes.cmi asmcomp/arch.cmx \
asmcomp/closure.cmi
asmcomp/cmmgen.cmo : typing/types.cmi bytecomp/switch.cmi \
asmcomp/strmatch.cmi asmcomp/proc.cmi typing/primitive.cmi utils/misc.cmi \
bytecomp/lambda.cmi typing/ident.cmi bytecomp/debuginfo.cmi \

View File

@ -93,60 +93,6 @@ let occurs_var var u =
true
in occurs u
(* Split a function with default parameters into a wrapper and an
inner function. The wrapper fills in missing optional parameters
with their default value and tail-calls the inner function. The
wrapper can then hopefully be inlined on most call sites to avoid
the overhead associated with boxing an optional argument with a
'Some' constructor, only to deconstruct it immediately in the
function's body. *)
let split_default_wrapper fun_id kind params body attr =
let rec aux map = function
| Llet(Strict, id, (Lifthenelse(Lvar optparam, _, _) as def), rest) when
Ident.name optparam = "*opt*" && List.mem optparam params
&& not (List.mem_assoc optparam map)
->
let wrapper_body, inner = aux ((optparam, id) :: map) rest in
Llet(Strict, id, def, wrapper_body), inner
| _ when map = [] -> raise Exit
| body ->
(* Check that those *opt* identifiers don't appear in the remaining
body. This should not appear, but let's be on the safe side. *)
let fv = Lambda.free_variables body in
List.iter (fun (id, _) -> if IdentSet.mem id fv then raise Exit) map;
let inner_id = Ident.create (Ident.name fun_id ^ "_inner") in
let map_param p = try List.assoc p map with Not_found -> p in
let args = List.map (fun p -> Lvar (map_param p)) params in
let wrapper_body =
Lapply {ap_should_be_tailcall=false;
ap_loc=Location.none;
ap_func=Lvar inner_id;
ap_args=args;
ap_inlined=Default_inline}
in
let inner_params = List.map map_param params in
let new_ids = List.map Ident.rename inner_params in
let subst = List.fold_left2
(fun s id new_id ->
Ident.add id (Lvar new_id) s)
Ident.empty inner_params new_ids
in
let body = Lambda.subst_lambda subst body in
let inner_fun = Lfunction{kind = Curried; params = new_ids; body;
attr} in
(wrapper_body, (inner_id, inner_fun))
in
try
let wrapper_body, inner = aux [] body in
[(fun_id, Lfunction{kind; params; body = wrapper_body;
attr = default_function_attribute}); inner]
with Exit ->
[(fun_id, Lfunction{kind; params; body; attr})]
(* Determine whether the estimated size of a clambda term is below
some threshold *)
@ -1113,7 +1059,7 @@ and close_functions fenv cenv fun_defs =
(List.map
(function
| (id, Lfunction{kind; params; body; attr}) ->
split_default_wrapper id kind params body attr
Simplif.split_default_wrapper id kind params body attr
| _ -> assert false
)
fun_defs)

View File

@ -610,6 +610,60 @@ and list_emit_tail_infos_fun f is_tail =
and list_emit_tail_infos is_tail =
List.iter (emit_tail_infos is_tail)
(* Split a function with default parameters into a wrapper and an
inner function. The wrapper fills in missing optional parameters
with their default value and tail-calls the inner function. The
wrapper can then hopefully be inlined on most call sites to avoid
the overhead associated with boxing an optional argument with a
'Some' constructor, only to deconstruct it immediately in the
function's body. *)
let split_default_wrapper fun_id kind params body attr =
let rec aux map = function
| Llet(Strict, id, (Lifthenelse(Lvar optparam, _, _) as def), rest) when
Ident.name optparam = "*opt*" && List.mem optparam params
&& not (List.mem_assoc optparam map)
->
let wrapper_body, inner = aux ((optparam, id) :: map) rest in
Llet(Strict, id, def, wrapper_body), inner
| _ when map = [] -> raise Exit
| body ->
(* Check that those *opt* identifiers don't appear in the remaining
body. This should not appear, but let's be on the safe side. *)
let fv = Lambda.free_variables body in
List.iter (fun (id, _) -> if IdentSet.mem id fv then raise Exit) map;
let inner_id = Ident.create (Ident.name fun_id ^ "_inner") in
let map_param p = try List.assoc p map with Not_found -> p in
let args = List.map (fun p -> Lvar (map_param p)) params in
let wrapper_body =
Lapply {
ap_func = Lvar inner_id;
ap_args = args;
ap_loc = Location.none;
ap_should_be_tailcall = false;
ap_inlined = Default_inline;
}
in
let inner_params = List.map map_param params in
let new_ids = List.map Ident.rename inner_params in
let subst = List.fold_left2
(fun s id new_id ->
Ident.add id (Lvar new_id) s)
Ident.empty inner_params new_ids
in
let body = Lambda.subst_lambda subst body in
let inner_fun =
Lfunction { kind = Curried; params = new_ids; body; attr; }
in
(wrapper_body, (inner_id, inner_fun))
in
try
let wrapper_body, inner = aux [] body in
[(fun_id, Lfunction{kind; params; body = wrapper_body; attr}); inner]
with Exit ->
[(fun_id, Lfunction{kind; params; body; attr})]
(* The entry point:
simplification + emission of tailcall annotations, if needed. *)

View File

@ -19,6 +19,14 @@ open Lambda
val simplify_lambda: lambda -> lambda
val split_default_wrapper
: Ident.t
-> function_kind
-> Ident.t list
-> lambda
-> function_attribute
-> (Ident.t * lambda) list
(* To be filled by asmcomp/selectgen.ml *)
val is_tail_native_heuristic: (int -> bool) ref
(* # arguments -> can tailcall *)