Skip to content

Commit 9f45679

Browse files
committed
WASI: support for separate compilation
1 parent 528d4a8 commit 9f45679

File tree

8 files changed

+262
-53
lines changed

8 files changed

+262
-53
lines changed

compiler/lib-wasm/generate.ml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,35 @@ module Generate (Target : Target_sig.S) = struct
10851085
:: context.other_fields;
10861086
name
10871087

1088+
let add_missing_primitives ~context l =
1089+
let failwith_desc = W.Fun { params = [ Value.value ]; result = [] } in
1090+
List.iter l ~f:(fun (exported_name, arity) ->
1091+
let name = Code.Var.fresh_n exported_name in
1092+
let locals, body =
1093+
function_body
1094+
~context
1095+
~param_names:[]
1096+
~body:
1097+
(let* failwith =
1098+
register_import ~import_module:"env" ~name:"caml_failwith" failwith_desc
1099+
in
1100+
let* msg =
1101+
Constant.translate (String (exported_name ^ " not implemented"))
1102+
in
1103+
let* () = instr (CallInstr (failwith, [ msg ])) in
1104+
push Value.unit)
1105+
in
1106+
context.other_fields <-
1107+
W.Function
1108+
{ name
1109+
; exported_name = Some exported_name
1110+
; typ = func_type arity
1111+
; param_names = []
1112+
; locals
1113+
; body
1114+
}
1115+
:: context.other_fields)
1116+
10881117
let entry_point context toplevel_fun entry_name =
10891118
let typ, param_names, body = entry_point ~toplevel_fun in
10901119
let locals, body = function_body ~context ~param_names ~body in
@@ -1245,6 +1274,10 @@ let add_init_function =
12451274
let module G = Generate (Gc_target) in
12461275
G.add_init_function
12471276

1277+
let add_missing_primitives =
1278+
let module G = Generate (Gc_target) in
1279+
G.add_missing_primitives
1280+
12481281
let output ch ~context =
12491282
let module G = Generate (Gc_target) in
12501283
let fields = G.output ~context in

compiler/lib-wasm/generate.mli

Lines changed: 3 additions & 0 deletions
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

Lines changed: 188 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -180,12 +180,13 @@ module Wasm_binary = struct
180180

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

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

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

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

208267
let import ch =
209268
let module_ = name ch in
210269
let name = name ch in
211270
let d = read_uint ch in
212-
let _ =
271+
let desc =
213272
match d with
214-
| 0 -> ignore (read_uint ch)
215-
| 1 -> tabletype ch
216-
| 2 -> memtype ch
273+
| 0 -> Func (read_uint ch)
274+
| 1 ->
275+
tabletype ch;
276+
Table
277+
| 2 ->
278+
memtype ch;
279+
Mem
217280
| 3 ->
218281
let _typ = valtype ch in
219282
let _mut = input_byte ch in
220-
()
283+
Global
221284
| 4 ->
222285
assert (read_uint ch = 0);
223-
ignore (read_uint ch)
286+
ignore (read_uint ch);
287+
Tag
224288
| _ ->
225289
Format.eprintf "Unknown import %x@." d;
226290
assert false
227291
in
228-
{ module_; name }
292+
{ module_; name; desc }
229293

230294
let export ch =
231295
let name = name ch in
@@ -255,22 +319,27 @@ module Wasm_binary = struct
255319
type interface =
256320
{ imports : import list
257321
; exports : string list
322+
; types : comptype array
258323
}
259324

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

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

476+
let generate_missing_primitives ~missing_primitives ~out_file =
477+
Filename.gen_file out_file
478+
@@ fun ch ->
479+
let context = Generate.start () in
480+
Generate.add_missing_primitives ~context missing_primitives;
481+
Generate.wasm_output ch ~context
482+
407483
let output_js js =
408484
let js = Driver.simplify_js js in
409485
let js = Driver.name_variables js in
@@ -641,17 +717,20 @@ let compute_dependencies ~files_to_link ~files =
641717

642718
let compute_missing_primitives (runtime_intf, intfs) =
643719
let provided_primitives = StringSet.of_list runtime_intf.Wasm_binary.exports in
644-
StringSet.elements
720+
StringMap.bindings
645721
@@ List.fold_left
646-
~f:(fun s { Wasm_binary.imports; _ } ->
722+
~f:(fun s { Wasm_binary.imports; types; _ } ->
647723
List.fold_left
648-
~f:(fun s { Wasm_binary.module_; name; _ } ->
649-
if String.equal module_ "env" && not (StringSet.mem name provided_primitives)
650-
then StringSet.add name s
651-
else s)
724+
~f:(fun s { Wasm_binary.module_; name; desc } ->
725+
match module_, desc with
726+
| "env", Func idx when not (StringSet.mem name provided_primitives) -> (
727+
match types.(idx) with
728+
| Func { arity } -> StringMap.add name arity s
729+
| _ -> s)
730+
| _ -> s)
652731
~init:s
653732
imports)
654-
~init:StringSet.empty
733+
~init:StringMap.empty
655734
intfs
656735

657736
let load_information files =
@@ -687,6 +766,72 @@ let gen_dir dir f =
687766
remove_directory d_tmp;
688767
raise exc
689768

769+
let link_to_module ~to_link ~files_to_link ~files ~enable_source_maps:_ ~dir =
770+
let process_file ~name ~module_name file =
771+
Zip.with_open_in file
772+
@@ fun z ->
773+
let intf =
774+
let ch, pos, len, _ = Zip.get_entry z ~name in
775+
Wasm_binary.read_interface (Wasm_binary.from_channel ~name ch pos len)
776+
in
777+
( { Wasm_link.module_name
778+
; file
779+
; code = Some (Zip.read_entry z ~name)
780+
; opt_source_map = None
781+
}
782+
, intf )
783+
in
784+
let runtime_file = fst (List.hd files) in
785+
let z = Zip.open_in runtime_file in
786+
let runtime, runtime_intf =
787+
process_file ~name:"runtime.wasm" ~module_name:"env" runtime_file
788+
in
789+
let prelude =
790+
{ Wasm_link.module_name = "OCaml"
791+
; file = runtime_file
792+
; code = Some (Zip.read_entry z ~name:"prelude.wasm")
793+
; opt_source_map = None
794+
}
795+
in
796+
Zip.close_in z;
797+
let lst =
798+
List.tl files
799+
|> List.filter_map ~f:(fun (file, _) ->
800+
if StringSet.mem file files_to_link
801+
then Some (process_file ~name:"code.wasm" ~module_name:"OCaml" file)
802+
else None)
803+
in
804+
let missing_primitives =
805+
if Config.Flag.genprim ()
806+
then compute_missing_primitives (runtime_intf, List.map ~f:snd lst)
807+
else []
808+
in
809+
Fs.with_intermediate_file (Filename.temp_file "start" ".wasm")
810+
@@ fun start_module ->
811+
generate_start_function ~to_link ~out_file:start_module;
812+
let start =
813+
{ Wasm_link.module_name = "OCaml"
814+
; file = start_module
815+
; code = None
816+
; opt_source_map = None
817+
}
818+
in
819+
Fs.with_intermediate_file (Filename.temp_file "stubs" ".wasm")
820+
@@ fun stubs_module ->
821+
generate_missing_primitives ~missing_primitives ~out_file:stubs_module;
822+
let missing_primitives =
823+
{ Wasm_link.module_name = "env"
824+
; file = stubs_module
825+
; code = None
826+
; opt_source_map = None
827+
}
828+
in
829+
ignore
830+
(Wasm_link.f
831+
(runtime :: prelude :: missing_primitives :: start :: List.map ~f:fst lst)
832+
~filter_export:(fun nm -> String.equal nm "_start" || String.equal nm "memory")
833+
~output_file:(Filename.concat dir "code.wasm"))
834+
690835
let link ~output_file ~linkall ~enable_source_maps ~files =
691836
if times () then Format.eprintf "linking@.";
692837
let t = Timer.make () in
@@ -777,30 +922,35 @@ let link ~output_file ~linkall ~enable_source_maps ~files =
777922
if times () then Format.eprintf " finding what to link: %a@." Timer.print t1;
778923
if times () then Format.eprintf " scan: %a@." Timer.print t;
779924
let t = Timer.make () in
780-
let interfaces, wasm_dir, link_spec =
925+
let missing_primitives, wasm_dir, link_spec =
781926
let dir = Filename.chop_extension output_file ^ ".assets" in
782927
gen_dir dir
783928
@@ fun tmp_dir ->
784929
Sys.mkdir tmp_dir 0o777;
785-
let start_module =
786-
"start-"
787-
^ String.sub
788-
(Digest.to_hex (Digest.string (String.concat ~sep:"/" to_link)))
789-
~pos:0
790-
~len:8
791-
in
792-
generate_start_function
793-
~to_link
794-
~out_file:(Filename.concat tmp_dir (start_module ^ ".wasm"));
795-
let module_names, interfaces =
796-
link_to_directory ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir
797-
in
798-
( interfaces
799-
, dir
800-
, let to_link = compute_dependencies ~files_to_link ~files in
801-
List.combine module_names (None :: None :: to_link) @ [ start_module, None ] )
930+
if not (Config.Flag.wasi ())
931+
then (
932+
let start_module =
933+
"start-"
934+
^ String.sub
935+
(Digest.to_hex (Digest.string (String.concat ~sep:"/" to_link)))
936+
~pos:0
937+
~len:8
938+
in
939+
let module_names, interfaces =
940+
link_to_directory ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir
941+
in
942+
let missing_primitives = compute_missing_primitives interfaces in
943+
generate_start_function
944+
~to_link
945+
~out_file:(Filename.concat tmp_dir (start_module ^ ".wasm"));
946+
( List.map ~f:fst missing_primitives
947+
, dir
948+
, let to_link = compute_dependencies ~files_to_link ~files in
949+
List.combine module_names (None :: None :: to_link) @ [ start_module, None ] ))
950+
else (
951+
link_to_module ~to_link ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir;
952+
[], dir, [ "code", None ])
802953
in
803-
let missing_primitives = compute_missing_primitives interfaces in
804954
if times () then Format.eprintf " copy wasm files: %a@." Timer.print t;
805955
let t1 = Timer.make () in
806956
let js_runtime =

compiler/lib-wasm/link.mli

Lines changed: 8 additions & 0 deletions
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)