Skip to content

Commit c8a7b96

Browse files
committed
WASI: support for separate compilation
1 parent 32fb083 commit c8a7b96

File tree

9 files changed

+264
-54
lines changed

9 files changed

+264
-54
lines changed

compiler/lib-wasm/generate.ml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,6 +1370,36 @@ module Generate (Target : Target_sig.S) = struct
13701370
:: context.other_fields;
13711371
name
13721372

1373+
let add_missing_primitives ~context l =
1374+
let failwith_desc = W.Fun { params = [ Type.value ]; result = [] } in
1375+
List.iter l ~f:(fun (exported_name, arity) ->
1376+
let name = Code.Var.fresh_n exported_name in
1377+
let locals, body =
1378+
function_body
1379+
~context
1380+
~param_names:[]
1381+
~body:
1382+
(let* failwith =
1383+
register_import ~import_module:"env" ~name:"caml_failwith" failwith_desc
1384+
in
1385+
let* msg =
1386+
Constant.translate (String (exported_name ^ " not implemented"))
1387+
in
1388+
let* () = instr (CallInstr (failwith, [ msg ])) in
1389+
push Value.unit)
1390+
in
1391+
context.other_fields <-
1392+
W.Function
1393+
{ name
1394+
; exported_name = Some exported_name
1395+
; typ = None
1396+
; signature = Type.primitive_type arity
1397+
; param_names = []
1398+
; locals
1399+
; body
1400+
}
1401+
:: context.other_fields)
1402+
13731403
let entry_point context toplevel_fun entry_name =
13741404
let signature, param_names, body = entry_point ~toplevel_fun in
13751405
let locals, body = function_body ~context ~param_names ~body in
@@ -1544,6 +1574,10 @@ let add_start_function = G.add_start_function
15441574

15451575
let add_init_function = G.add_init_function
15461576

1577+
let add_missing_primitives =
1578+
let module G = Generate (Gc_target) in
1579+
G.add_missing_primitives
1580+
15471581
let output ch ~context =
15481582
let t = Timer.make () in
15491583
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 :

compiler/lib-wasm/link.ml

Lines changed: 188 additions & 38 deletions
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
@@ -397,6 +466,13 @@ let generate_start_function ~to_link ~out_file =
397466
Generate.wasm_output ch ~opt_source_map_file:None ~context;
398467
if times () then Format.eprintf " generate start: %a@." Timer.print t1
399468

469+
let generate_missing_primitives ~missing_primitives ~out_file =
470+
Filename.gen_file out_file
471+
@@ fun ch ->
472+
let context = Generate.start () in
473+
Generate.add_missing_primitives ~context missing_primitives;
474+
Generate.wasm_output ch ~opt_source_map_file:None ~context
475+
400476
let output_js js =
401477
let js = Driver.simplify_js js in
402478
let js = Driver.name_variables js in
@@ -630,17 +706,20 @@ let compute_dependencies ~files_to_link ~files =
630706

631707
let compute_missing_primitives (runtime_intf, intfs) =
632708
let provided_primitives = StringSet.of_list runtime_intf.Wasm_binary.exports in
633-
StringSet.elements
709+
StringMap.bindings
634710
@@ List.fold_left
635-
~f:(fun s { Wasm_binary.imports; _ } ->
711+
~f:(fun s { Wasm_binary.imports; types; _ } ->
636712
List.fold_left
637-
~f:(fun s { Wasm_binary.module_; name; _ } ->
638-
if String.equal module_ "env" && not (StringSet.mem name provided_primitives)
639-
then StringSet.add name s
640-
else s)
713+
~f:(fun s { Wasm_binary.module_; name; desc } ->
714+
match module_, desc with
715+
| "env", Func idx when not (StringSet.mem name provided_primitives) -> (
716+
match types.(idx) with
717+
| Func { arity } -> StringMap.add name arity s
718+
| _ -> s)
719+
| _ -> s)
641720
~init:s
642721
imports)
643-
~init:StringSet.empty
722+
~init:StringMap.empty
644723
intfs
645724

646725
let load_information files =
@@ -676,6 +755,72 @@ let gen_dir dir f =
676755
remove_directory d_tmp;
677756
raise exc
678757

758+
let link_to_module ~to_link ~files_to_link ~files ~enable_source_maps:_ ~dir =
759+
let process_file ~name ~module_name file =
760+
Zip.with_open_in file
761+
@@ fun z ->
762+
let intf =
763+
let ch, pos, len, _ = Zip.get_entry z ~name in
764+
Wasm_binary.read_interface (Wasm_binary.from_channel ~name ch pos len)
765+
in
766+
( { Wasm_link.module_name
767+
; file
768+
; code = Some (Zip.read_entry z ~name)
769+
; opt_source_map = None
770+
}
771+
, intf )
772+
in
773+
let runtime_file = fst (List.hd files) in
774+
let z = Zip.open_in runtime_file in
775+
let runtime, runtime_intf =
776+
process_file ~name:"runtime.wasm" ~module_name:"env" runtime_file
777+
in
778+
let prelude =
779+
{ Wasm_link.module_name = "OCaml"
780+
; file = runtime_file
781+
; code = Some (Zip.read_entry z ~name:"prelude.wasm")
782+
; opt_source_map = None
783+
}
784+
in
785+
Zip.close_in z;
786+
let lst =
787+
List.tl files
788+
|> List.filter_map ~f:(fun (file, _) ->
789+
if StringSet.mem file files_to_link
790+
then Some (process_file ~name:"code.wasm" ~module_name:"OCaml" file)
791+
else None)
792+
in
793+
let missing_primitives =
794+
if Config.Flag.genprim ()
795+
then compute_missing_primitives (runtime_intf, List.map ~f:snd lst)
796+
else []
797+
in
798+
Fs.with_intermediate_file (Filename.temp_file "start" ".wasm")
799+
@@ fun start_module ->
800+
generate_start_function ~to_link ~out_file:start_module;
801+
let start =
802+
{ Wasm_link.module_name = "OCaml"
803+
; file = start_module
804+
; code = None
805+
; opt_source_map = None
806+
}
807+
in
808+
Fs.with_intermediate_file (Filename.temp_file "stubs" ".wasm")
809+
@@ fun stubs_module ->
810+
generate_missing_primitives ~missing_primitives ~out_file:stubs_module;
811+
let missing_primitives =
812+
{ Wasm_link.module_name = "env"
813+
; file = stubs_module
814+
; code = None
815+
; opt_source_map = None
816+
}
817+
in
818+
ignore
819+
(Wasm_link.f
820+
(runtime :: prelude :: missing_primitives :: start :: List.map ~f:fst lst)
821+
~filter_export:(fun nm -> String.equal nm "_start" || String.equal nm "memory")
822+
~output_file:(Filename.concat dir "code.wasm"))
823+
679824
let link ~output_file ~linkall ~enable_source_maps ~files =
680825
if times () then Format.eprintf "linking@.";
681826
let t = Timer.make () in
@@ -766,30 +911,35 @@ let link ~output_file ~linkall ~enable_source_maps ~files =
766911
if times () then Format.eprintf " finding what to link: %a@." Timer.print t1;
767912
if times () then Format.eprintf " scan: %a@." Timer.print t;
768913
let t = Timer.make () in
769-
let interfaces, wasm_dir, link_spec =
914+
let missing_primitives, wasm_dir, link_spec =
770915
let dir = Filename.chop_extension output_file ^ ".assets" in
771916
gen_dir dir
772917
@@ fun tmp_dir ->
773918
Sys.mkdir tmp_dir 0o777;
774-
let start_module =
775-
"start-"
776-
^ String.sub
777-
(Digest.to_hex (Digest.string (String.concat ~sep:"/" to_link)))
778-
~pos:0
779-
~len:8
780-
in
781-
generate_start_function
782-
~to_link
783-
~out_file:(Filename.concat tmp_dir (start_module ^ ".wasm"));
784-
let module_names, interfaces =
785-
link_to_directory ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir
786-
in
787-
( interfaces
788-
, dir
789-
, let to_link = compute_dependencies ~files_to_link ~files in
790-
List.combine module_names (None :: None :: to_link) @ [ start_module, None ] )
919+
if not (Config.Flag.wasi ())
920+
then (
921+
let start_module =
922+
"start-"
923+
^ String.sub
924+
(Digest.to_hex (Digest.string (String.concat ~sep:"/" to_link)))
925+
~pos:0
926+
~len:8
927+
in
928+
let module_names, interfaces =
929+
link_to_directory ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir
930+
in
931+
let missing_primitives = compute_missing_primitives interfaces in
932+
generate_start_function
933+
~to_link
934+
~out_file:(Filename.concat tmp_dir (start_module ^ ".wasm"));
935+
( List.map ~f:fst missing_primitives
936+
, dir
937+
, let to_link = compute_dependencies ~files_to_link ~files in
938+
List.combine module_names (None :: None :: to_link) @ [ start_module, None ] ))
939+
else (
940+
link_to_module ~to_link ~files_to_link ~files ~enable_source_maps ~dir:tmp_dir;
941+
[], dir, [ "code", None ])
791942
in
792-
let missing_primitives = compute_missing_primitives interfaces in
793943
if times () then Format.eprintf " copy wasm files: %a@." Timer.print t;
794944
let t1 = Timer.make () in
795945
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)