Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 121 additions & 78 deletions compiler/bin-wasm_of_ocaml/compile.ml
Original file line number Diff line number Diff line change
Expand Up @@ -150,77 +150,104 @@ let link_and_optimize
| Some _ | None -> opt_sourcemap
in
let enable_source_maps = Option.is_some opt_sourcemap_file in
let run_dce = not dynlink in
let run_opt =
match (profile : Profile.t) with
| O1 -> false
| O2 | O3 -> true
in
(* [with_step_output ~is_last base] invokes its continuation with the
output file and sourcemap to pass to a Binaryen pass. If [is_last] the
pass writes directly to the final location; otherwise a pair of temp
files scoped to the continuation is used. *)
let with_step_output ~is_last base k =
if is_last
then k ~file:output_file ~opt_sm:opt_sourcemap_file
else
Fs.with_intermediate_file (Filename.temp_file base ".wasm")
@@ fun file ->
opt_with
Fs.with_intermediate_file
(if enable_source_maps then Some (Filename.temp_file base ".wasm.map") else None)
@@ fun opt_sm -> k ~file ~opt_sm
in
Fs.with_intermediate_file (Filename.temp_file "runtime" ".wasm")
@@ fun runtime_file ->
build_runtime ~runtime_file;
Fs.with_intermediate_file (Filename.temp_file "wasm-merged" ".wasm")
@@ fun temp_file ->
opt_with
Fs.with_intermediate_file
(if enable_source_maps
then Some (Filename.temp_file "wasm-merged" ".wasm.map")
else None)
@@ fun opt_temp_sourcemap ->
(with_runtime_files ~runtime_wasm_files
@@ fun runtime_inputs ->
let t = Timer.make ~get_time:Unix.time () in
Binaryen.link
~inputs:
({ Binaryen.module_name = "env"; file = runtime_file; source_map_file = None }
:: runtime_inputs
@ List.map
~f:(fun (file, source_map_file) ->
{ Binaryen.module_name = "OCaml"; file; source_map_file })
wat_files)
~opt_output_sourcemap:opt_temp_sourcemap
~output_file:temp_file
();
if binaryen_times () then Format.eprintf " binaryen link: %a@." Timer.print t);

let optimize_and_finish ~opt_input_sourcemap ~input_file primitives =
let link ~output_file ~opt_output_sourcemap =
with_runtime_files ~runtime_wasm_files
@@ fun runtime_inputs ->
let t = Timer.make ~get_time:Unix.time () in
Binaryen.link
~inputs:
({ Binaryen.module_name = "env"; file = runtime_file; source_map_file = None }
:: runtime_inputs
@ List.map
~f:(fun (file, source_map_file) ->
{ Binaryen.module_name = "OCaml"; file; source_map_file })
wat_files)
~opt_output_sourcemap
~output_file
();
if binaryen_times () then Format.eprintf " binaryen link: %a@." Timer.print t
in
let dce ~input_file ~opt_input_sourcemap ~output_file ~opt_output_sourcemap =
let t = Timer.make ~get_time:Unix.time () in
let primitives =
Binaryen.dead_code_elimination
~dependencies:Runtime_files.dependencies
~opt_input_sourcemap
~opt_output_sourcemap
~input_file
~output_file
in
if binaryen_times () then Format.eprintf " binaryen dce: %a@." Timer.print t;
primitives
in
let optimize ~input_file ~opt_input_sourcemap =
let t = Timer.make ~get_time:Unix.time () in
Binaryen.optimize
~profile
~opt_input_sourcemap
~opt_output_sourcemap:opt_sourcemap
~opt_output_sourcemap:opt_sourcemap_file
~input_file
~output_file
();
if binaryen_times () then Format.eprintf " binaryen opt: %a@." Timer.print t;
if binaryen_times () then Format.eprintf " binaryen opt: %a@." Timer.print t
in
let finish primitives =
Option.iter
~f:(update_sourcemap ~sourcemap_root ~sourcemap_don't_inline_content)
opt_sourcemap_file;
primitives
in
if dynlink
let link_is_last = (not run_dce) && not run_opt in
with_step_output ~is_last:link_is_last "wasm-merged"
@@ fun ~file:linked ~opt_sm:opt_linked_sm ->
link ~output_file:linked ~opt_output_sourcemap:opt_linked_sm;
if link_is_last
then finish (Linker.list_all ())
else if run_dce
then
optimize_and_finish
~opt_input_sourcemap:opt_temp_sourcemap
~input_file:temp_file
(Linker.list_all ())
else
Fs.with_intermediate_file (Filename.temp_file "wasm-dce" ".wasm")
@@ fun temp_file' ->
opt_with
Fs.with_intermediate_file
(if enable_source_maps
then Some (Filename.temp_file "wasm-dce" ".wasm.map")
else None)
@@ fun opt_temp_sourcemap' ->
let t = Timer.make ~get_time:Unix.time () in
let dce_is_last = not run_opt in
with_step_output ~is_last:dce_is_last "wasm-dce"
@@ fun ~file:dced ~opt_sm:opt_dced_sm ->
let primitives =
Binaryen.dead_code_elimination
~dependencies:Runtime_files.dependencies
~opt_input_sourcemap:opt_temp_sourcemap
~opt_output_sourcemap:opt_temp_sourcemap'
~input_file:temp_file
~output_file:temp_file'
dce
~input_file:linked
~opt_input_sourcemap:opt_linked_sm
~output_file:dced
~opt_output_sourcemap:opt_dced_sm
in
if binaryen_times () then Format.eprintf " binaryen dce: %a@." Timer.print t;
optimize_and_finish
~opt_input_sourcemap:opt_temp_sourcemap'
~input_file:temp_file'
primitives
if dce_is_last
then finish primitives
else (
optimize ~input_file:dced ~opt_input_sourcemap:opt_dced_sm;
finish primitives)
else (
(* [dynlink] + O2/O3: skip DCE, go straight to [wasm-opt]. *)
optimize ~input_file:linked ~opt_input_sourcemap:opt_linked_sm;
finish (Linker.list_all ()))

let link_runtime ~profile runtime_wasm_files output_file =
if List.is_empty runtime_wasm_files
Expand Down Expand Up @@ -275,6 +302,7 @@ let generate_prelude ~out_file =
let context = Generate.start () in
let _ =
Generate.f
~profile
~context
~unit_name:(Some "prelude")
~live_vars:variable_uses
Expand Down Expand Up @@ -488,6 +516,7 @@ let run
let context = Generate.start () in
let toplevel_name, generated_js =
Generate.f
~profile
~context
~unit_name
~live_vars:variable_uses
Expand Down Expand Up @@ -579,31 +608,45 @@ let run
else None)
@@ fun opt_tmp_map_file ->
let unit_data, shapes =
Fs.with_intermediate_file (Filename.temp_file unit_name ".wasm")
@@ fun input_file ->
opt_with
Fs.with_intermediate_file
(if enable_source_maps
then Some (Filename.temp_file unit_name ".wasm.map")
else None)
@@ fun opt_input_sourcemap ->
let fragments, shapes =
output
code
~wat_file:
(Filename.concat (Filename.dirname output_file) (unit_name ^ ".wat"))
~unit_name:(Some unit_name)
~file:input_file
~opt_source_map_file:opt_input_sourcemap
in
Binaryen.optimize
~profile
~opt_input_sourcemap
~opt_output_sourcemap:opt_tmp_map_file
~input_file
~output_file:tmp_wasm_file
();
{ Link.unit_name; unit_info; fragments }, shapes
match profile with
| Profile.O1 ->
(* At O1, skip Binaryen.optimize — write directly *)
let fragments, shapes =
output
code
~wat_file:
(Filename.concat (Filename.dirname output_file) (unit_name ^ ".wat"))
~unit_name:(Some unit_name)
~file:tmp_wasm_file
~opt_source_map_file:opt_tmp_map_file
in
{ Link.unit_name; unit_info; fragments }, shapes
| O2 | O3 ->
Fs.with_intermediate_file (Filename.temp_file unit_name ".wasm")
@@ fun input_file ->
opt_with
Fs.with_intermediate_file
(if enable_source_maps
then Some (Filename.temp_file unit_name ".wasm.map")
else None)
@@ fun opt_input_sourcemap ->
let fragments, shapes =
output
code
~wat_file:
(Filename.concat (Filename.dirname output_file) (unit_name ^ ".wat"))
~unit_name:(Some unit_name)
~file:input_file
~opt_source_map_file:opt_input_sourcemap
in
Binaryen.optimize
~profile
~opt_input_sourcemap
~opt_output_sourcemap:opt_tmp_map_file
~input_file
~output_file:tmp_wasm_file
();
{ Link.unit_name; unit_info; fragments }, shapes
in
cont unit_data unit_name tmp_wasm_file opt_tmp_map_file shapes cmi_files
in
Expand Down
28 changes: 27 additions & 1 deletion compiler/lib-wasm/gc_target.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2041,7 +2041,33 @@ let handle_exceptions ~result_typ ~fall_through ~context body x exn_handler =
let* () = no_event in
exn_handler ~result_typ ~fall_through ~context)

let post_process_function_body = Initialize_locals.f
let post_process_function_body ~profile ~param_names ~param_types ~locals body =
(* At [--opt 1] we skip [wasm-opt] entirely (both for .cmo/.cma and
for executables), so our own passes are the only ones tightening
the body. [Local_sink] runs first: shortening live ranges and
dropping [local.set]s simplifies the input to [Var_coalescing]. *)
let body =
match (profile : Profile.t) with
| O1 when Config.Flag.wasm_local_sink () -> Local_sink.f body
| O1 | O2 | O3 -> body
in
let locals, body =
match (profile : Profile.t) with
| O1 when Config.Flag.wasm_var_coalescing () ->
Var_coalescing.f ~param_names ~param_types ~locals body
| O1 | O2 | O3 -> locals, body
in
let locals, body = Initialize_locals.f ~param_names ~locals body in
(* Reorder locals so frequently-used ones get low Wasm indices
(one-byte LEB128 encoding for indices < 128). Gated on [O1] to
match the rest of this pipeline; at [O2]/[O3] [wasm-opt] handles
local reordering. *)
let locals =
match (profile : Profile.t) with
| O1 when Config.Flag.wasm_reorder_locals () -> Reorder_locals.f ~locals body
| O1 | O2 | O3 -> locals
in
locals, body

let entry_point ~toplevel_fun =
let code =
Expand Down
Loading
Loading