Skip to content

Commit f53758d

Browse files
committed
WASI: support for separate compilation
1 parent 20169aa commit f53758d

File tree

8 files changed

+262
-53
lines changed

8 files changed

+262
-53
lines changed

compiler/lib-wasm/generate.ml

+33
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,35 @@ module Generate (Target : Target_sig.S) = struct
10781078
:: context.other_fields;
10791079
name
10801080

1081+
let add_missing_primitives ~context l =
1082+
let failwith_desc = W.Fun { params = [ Value.value ]; result = [] } in
1083+
List.iter l ~f:(fun (exported_name, arity) ->
1084+
let name = Code.Var.fresh_n exported_name in
1085+
let locals, body =
1086+
function_body
1087+
~context
1088+
~param_names:[]
1089+
~body:
1090+
(let* failwith =
1091+
register_import ~import_module:"env" ~name:"caml_failwith" failwith_desc
1092+
in
1093+
let* msg =
1094+
Constant.translate (String (exported_name ^ " not implemented"))
1095+
in
1096+
let* () = instr (CallInstr (failwith, [ msg ])) in
1097+
push Value.unit)
1098+
in
1099+
context.other_fields <-
1100+
W.Function
1101+
{ name
1102+
; exported_name = Some exported_name
1103+
; typ = func_type arity
1104+
; param_names = []
1105+
; locals
1106+
; body
1107+
}
1108+
:: context.other_fields)
1109+
10811110
let entry_point context toplevel_fun entry_name =
10821111
let typ, param_names, body = entry_point ~toplevel_fun in
10831112
let locals, body = function_body ~context ~param_names ~body in
@@ -1238,6 +1267,10 @@ let add_init_function =
12381267
let module G = Generate (Gc_target) in
12391268
G.add_init_function
12401269

1270+
let add_missing_primitives =
1271+
let module G = Generate (Gc_target) in
1272+
G.add_missing_primitives
1273+
12411274
let output ch ~context =
12421275
let module G = Generate (Gc_target) in
12431276
let fields = G.output ~context in

compiler/lib-wasm/generate.mli

+3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ val add_start_function : context:Code_generation.context -> Wasm_ast.var -> unit
3434

3535
val add_init_function : context:Code_generation.context -> to_link:string list -> unit
3636

37+
val add_missing_primitives :
38+
context:Code_generation.context -> (string * int) list -> unit
39+
3740
val output : out_channel -> context:Code_generation.context -> unit
3841

3942
val wasm_output : out_channel -> context:Code_generation.context -> unit

compiler/lib-wasm/link.ml

+188-38
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,13 @@ module Wasm_binary = struct
181181

182182
let reftype ch = reftype' (input_byte ch) ch
183183

184-
let valtype ch =
185-
let i = read_uint ch in
184+
let valtype' i ch =
186185
match i with
187-
| 0x7b | 0x7c | 0x7d | 0x7e | 0x7f -> ()
186+
| 0x7B | 0x7C | 0x7D | 0x7E | 0x7F -> ()
188187
| _ -> reftype' i ch
189188

189+
let valtype ch = valtype' (read_uint ch) ch
190+
190191
let limits ch =
191192
match input_byte ch with
192193
| 0 -> ignore (read_uint ch)
@@ -201,32 +202,95 @@ module Wasm_binary = struct
201202
reftype ch;
202203
limits ch
203204

205+
type comptype =
206+
| Func of { arity : int }
207+
| Struct
208+
| Array
209+
210+
let supertype ch =
211+
match input_byte ch with
212+
| 0 -> ()
213+
| 1 -> ignore (read_uint ch)
214+
| _ -> assert false
215+
216+
let storagetype ch =
217+
let i = read_uint ch in
218+
match i with
219+
| 0x78 | 0x77 -> ()
220+
| _ -> valtype' i ch
221+
222+
let fieldtype ch =
223+
storagetype ch;
224+
ignore (input_byte ch)
225+
226+
let comptype i ch =
227+
match i with
228+
| 0x5E ->
229+
fieldtype ch;
230+
Array
231+
| 0x5F ->
232+
ignore (vec fieldtype ch);
233+
Struct
234+
| 0x60 ->
235+
let params = vec valtype ch in
236+
let _ = vec valtype ch in
237+
Func { arity = List.length params }
238+
| c -> failwith (Printf.sprintf "Unknown comptype %d" c)
239+
240+
let subtype i ch =
241+
match i with
242+
| 0x50 ->
243+
supertype ch;
244+
comptype (input_byte ch) ch
245+
| 0x4F ->
246+
supertype ch;
247+
comptype (input_byte ch) ch
248+
| _ -> comptype i ch
249+
250+
let rectype ch =
251+
match input_byte ch with
252+
| 0x4E -> vec (fun ch -> subtype (input_byte ch) ch) ch
253+
| i -> [ subtype i ch ]
254+
255+
type importdesc =
256+
| Func of int
257+
| Table
258+
| Mem
259+
| Global
260+
| Tag
261+
204262
type import =
205263
{ module_ : string
206264
; name : string
265+
; desc : importdesc
207266
}
208267

209268
let import ch =
210269
let module_ = name ch in
211270
let name = name ch in
212271
let d = read_uint ch in
213-
let _ =
272+
let desc =
214273
match d with
215-
| 0 -> ignore (read_uint ch)
216-
| 1 -> tabletype ch
217-
| 2 -> memtype ch
274+
| 0 -> Func (read_uint ch)
275+
| 1 ->
276+
tabletype ch;
277+
Table
278+
| 2 ->
279+
memtype ch;
280+
Mem
218281
| 3 ->
219282
let _typ = valtype ch in
220283
let _mut = input_byte ch in
221-
()
284+
Global
222285
| 4 ->
223286
assert (read_uint ch = 0);
224-
ignore (read_uint ch)
287+
ignore (read_uint ch);
288+
Tag
225289
| _ ->
226290
Format.eprintf "Unknown import %x@." d;
227291
assert false
228292
in
229-
{ module_; name }
293+
{ module_; name; desc }
230294

231295
let export ch =
232296
let name = name ch in
@@ -256,22 +320,27 @@ module Wasm_binary = struct
256320
type interface =
257321
{ imports : import list
258322
; exports : string list
323+
; types : comptype array
259324
}
260325

261326
let read_interface ch =
262327
let rec find_sections i =
263328
match next_section ch with
264329
| None -> i
265330
| Some s ->
266-
if s.id = 2
331+
if s.id = 1
332+
then
333+
find_sections
334+
{ i with types = Array.of_list (List.flatten (vec rectype ch.ch)) }
335+
else if s.id = 2
267336
then find_sections { i with imports = vec import ch.ch }
268337
else if s.id = 7
269338
then { i with exports = vec export ch.ch }
270339
else (
271340
skip_section ch s;
272341
find_sections i)
273342
in
274-
find_sections { imports = []; exports = [] }
343+
find_sections { imports = []; exports = []; types = [||] }
275344

276345
let append_source_map_section ~file ~url =
277346
let ch = open_out_gen [ Open_wronly; Open_append; Open_binary ] 0o666 file in
@@ -405,6 +474,13 @@ let generate_start_function ~to_link ~out_file =
405474
Generate.wasm_output ch ~context;
406475
if times () then Format.eprintf " generate start: %a@." Timer.print t1
407476

477+
let generate_missing_primitives ~missing_primitives ~out_file =
478+
Filename.gen_file out_file
479+
@@ fun ch ->
480+
let context = Generate.start () in
481+
Generate.add_missing_primitives ~context missing_primitives;
482+
Generate.wasm_output ch ~context
483+
408484
let output_js js =
409485
Code.Var.reset ();
410486
let b = Buffer.create 1024 in
@@ -665,17 +741,20 @@ let compute_dependencies ~files_to_link ~files =
665741

666742
let compute_missing_primitives (runtime_intf, intfs) =
667743
let provided_primitives = StringSet.of_list runtime_intf.Wasm_binary.exports in
668-
StringSet.elements
744+
StringMap.bindings
669745
@@ List.fold_left
670-
~f:(fun s { Wasm_binary.imports; _ } ->
746+
~f:(fun s { Wasm_binary.imports; types; _ } ->
671747
List.fold_left
672-
~f:(fun s { Wasm_binary.module_; name; _ } ->
673-
if String.equal module_ "env" && not (StringSet.mem name provided_primitives)
674-
then StringSet.add name s
675-
else s)
748+
~f:(fun s { Wasm_binary.module_; name; desc } ->
749+
match module_, desc with
750+
| "env", Func idx when not (StringSet.mem name provided_primitives) -> (
751+
match types.(idx) with
752+
| Func { arity } -> StringMap.add name arity s
753+
| _ -> s)
754+
| _ -> s)
676755
~init:s
677756
imports)
678-
~init:StringSet.empty
757+
~init:StringMap.empty
679758
intfs
680759

681760
let load_information files =
@@ -711,6 +790,72 @@ let gen_dir dir f =
711790
remove_directory d_tmp;
712791
raise exc
713792

793+
let link_to_module ~to_link ~files_to_link ~files ~enable_source_maps:_ ~dir =
794+
let process_file ~name ~module_name file =
795+
Zip.with_open_in file
796+
@@ fun z ->
797+
let intf =
798+
let ch, pos, len, _ = Zip.get_entry z ~name in
799+
Wasm_binary.read_interface (Wasm_binary.from_channel ~name ch pos len)
800+
in
801+
( { Wasm_link.module_name
802+
; file
803+
; code = Some (Zip.read_entry z ~name)
804+
; opt_source_map = None
805+
}
806+
, intf )
807+
in
808+
let runtime_file = fst (List.hd files) in
809+
let z = Zip.open_in runtime_file in
810+
let runtime, runtime_intf =
811+
process_file ~name:"runtime.wasm" ~module_name:"env" runtime_file
812+
in
813+
let prelude =
814+
{ Wasm_link.module_name = "OCaml"
815+
; file = runtime_file
816+
; code = Some (Zip.read_entry z ~name:"prelude.wasm")
817+
; opt_source_map = None
818+
}
819+
in
820+
Zip.close_in z;
821+
let lst =
822+
List.tl files
823+
|> List.filter_map ~f:(fun (file, _) ->
824+
if StringSet.mem file files_to_link
825+
then Some (process_file ~name:"code.wasm" ~module_name:"OCaml" file)
826+
else None)
827+
in
828+
let missing_primitives =
829+
if Config.Flag.genprim ()
830+
then compute_missing_primitives (runtime_intf, List.map ~f:snd lst)
831+
else []
832+
in
833+
Fs.with_intermediate_file (Filename.temp_file "start" ".wasm")
834+
@@ fun start_module ->
835+
generate_start_function ~to_link ~out_file:start_module;
836+
let start =
837+
{ Wasm_link.module_name = "OCaml"
838+
; file = start_module
839+
; code = None
840+
; opt_source_map = None
841+
}
842+
in
843+
Fs.with_intermediate_file (Filename.temp_file "stubs" ".wasm")
844+
@@ fun stubs_module ->
845+
generate_missing_primitives ~missing_primitives ~out_file:stubs_module;
846+
let missing_primitives =
847+
{ Wasm_link.module_name = "env"
848+
; file = stubs_module
849+
; code = None
850+
; opt_source_map = None
851+
}
852+
in
853+
ignore
854+
(Wasm_link.f
855+
(runtime :: prelude :: missing_primitives :: start :: List.map ~f:fst lst)
856+
~filter_export:(fun nm -> String.equal nm "_start" || String.equal nm "memory")
857+
~output_file:(Filename.concat dir "code.wasm"))
858+
714859
let link ~output_file ~linkall ~enable_source_maps ~files =
715860
if times () then Format.eprintf "linking@.";
716861
let t = Timer.make () in
@@ -801,30 +946,35 @@ let link ~output_file ~linkall ~enable_source_maps ~files =
801946
if times () then Format.eprintf " finding what to link: %a@." Timer.print t1;
802947
if times () then Format.eprintf " scan: %a@." Timer.print t;
803948
let t = Timer.make () in
804-
let interfaces, wasm_dir, link_spec =
949+
let missing_primitives, wasm_dir, link_spec =
805950
let dir = Filename.chop_extension output_file ^ ".assets" in
806951
gen_dir dir
807952
@@ fun tmp_dir ->
808953
Sys.mkdir tmp_dir 0o777;
809-
let start_module =
810-
"start-"
811-
^ String.sub
812-
(Digest.to_hex (Digest.string (String.concat ~sep:"/" to_link)))
813-
~pos:0
814-
~len:8
815-
in
816-
generate_start_function
817-
~to_link
818-
~out_file:(Filename.concat tmp_dir (start_module ^ ".wasm"));
819-
let module_names, interfaces =
820-
link_to_directory ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir
821-
in
822-
( interfaces
823-
, dir
824-
, let to_link = compute_dependencies ~files_to_link ~files in
825-
List.combine module_names (None :: None :: to_link) @ [ start_module, None ] )
954+
if not (Config.Flag.wasi ())
955+
then (
956+
let start_module =
957+
"start-"
958+
^ String.sub
959+
(Digest.to_hex (Digest.string (String.concat ~sep:"/" to_link)))
960+
~pos:0
961+
~len:8
962+
in
963+
let module_names, interfaces =
964+
link_to_directory ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir
965+
in
966+
let missing_primitives = compute_missing_primitives interfaces in
967+
generate_start_function
968+
~to_link
969+
~out_file:(Filename.concat tmp_dir (start_module ^ ".wasm"));
970+
( List.map ~f:fst missing_primitives
971+
, dir
972+
, let to_link = compute_dependencies ~files_to_link ~files in
973+
List.combine module_names (None :: None :: to_link) @ [ start_module, None ] ))
974+
else (
975+
link_to_module ~to_link ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir;
976+
[], dir, [ "code", None ])
826977
in
827-
let missing_primitives = compute_missing_primitives interfaces in
828978
if times () then Format.eprintf " copy wasm files: %a@." Timer.print t;
829979
let t1 = Timer.make () in
830980
let js_runtime =

compiler/lib-wasm/link.mli

+8
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,17 @@
1919
open Stdlib
2020

2121
module Wasm_binary : sig
22+
type importdesc =
23+
| Func of int
24+
| Table
25+
| Mem
26+
| Global
27+
| Tag
28+
2229
type import =
2330
{ module_ : string
2431
; name : string
32+
; desc : importdesc
2533
}
2634

2735
val check : contents:string -> bool

0 commit comments

Comments
 (0)