Skip to content

Commit adb2998

Browse files
committed
Wasm_of_ocaml: implement use-js-string flag
1 parent fc2fada commit adb2998

38 files changed

+1869
-428
lines changed

compiler/bin-wasm_of_ocaml/compile.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ let with_runtime_files ~runtime_wasm_files f =
9090

9191
let build_runtime ~runtime_file =
9292
(* Keep this variables in sync with gen/gen.ml *)
93-
let variables = [] in
93+
let variables = [ "use-js-string", Config.Flag.use_js_string () ] in
9494
match
9595
List.find_opt Runtime_files.precompiled_runtimes ~f:(fun (flags, _) ->
9696
assert (

compiler/bin-wasm_of_ocaml/gen/gen.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ let read_file ic = really_input_string ic (in_channel_length ic)
55

66
let default_flags = []
77

8-
let interesting_runtimes = [ [] ]
8+
let interesting_runtimes = [ [ "use-js-string", false ]; [ "use-js-string", true ] ]
99

1010
let name_runtime l =
1111
let flags = List.filter_map (fun (k, v) -> if v then Some k else None) l in

compiler/lib-wasm/gc_target.ml

Lines changed: 93 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,31 @@ module Type = struct
3535
; typ = W.Array { mut = true; typ = Value value }
3636
})
3737

38-
let string_type =
39-
register_type "string" (fun () ->
38+
let bytes_type =
39+
register_type "bytes" (fun () ->
4040
return
4141
{ supertype = None
4242
; final = true
4343
; typ = W.Array { mut = true; typ = Packed I8 }
4444
})
4545

46+
let string_type =
47+
register_type "string" (fun () ->
48+
return
49+
(if Config.Flag.use_js_string ()
50+
then
51+
{ supertype = None
52+
; final = true
53+
; typ =
54+
W.Struct
55+
[ { mut = false; typ = Value (Ref { nullable = true; typ = Any }) } ]
56+
}
57+
else
58+
{ supertype = None
59+
; final = true
60+
; typ = W.Array { mut = true; typ = Packed I8 }
61+
}))
62+
4663
let float_type =
4764
register_type "float" (fun () ->
4865
return
@@ -794,15 +811,50 @@ module Memory = struct
794811
wasm_array_set ~ty:Type.float_array_type (load a) (load i) (unbox_float (load v)))
795812

796813
let bytes_length e =
797-
let* ty = Type.string_type in
814+
let* ty = Type.bytes_type in
798815
let* e = wasm_cast ty e in
799816
return (W.ArrayLen e)
800817

801818
let bytes_get e e' =
802-
Value.val_int (wasm_array_get ~ty:Type.string_type e (Value.int_val e'))
819+
Value.val_int (wasm_array_get ~ty:Type.bytes_type e (Value.int_val e'))
803820

804821
let bytes_set e e' e'' =
805-
wasm_array_set ~ty:Type.string_type e (Value.int_val e') (Value.int_val e'')
822+
wasm_array_set ~ty:Type.bytes_type e (Value.int_val e') (Value.int_val e'')
823+
824+
let string_value e =
825+
let* string = Type.string_type in
826+
let* e = wasm_struct_get string (wasm_cast string e) 0 in
827+
return (W.ExternConvertAny e)
828+
829+
let string_length e =
830+
if Config.Flag.use_js_string ()
831+
then
832+
let* f =
833+
register_import
834+
~import_module:"wasm:js-string"
835+
~name:"length"
836+
(Fun { W.params = [ Ref { nullable = true; typ = Extern } ]; result = [ I32 ] })
837+
in
838+
let* e = string_value e in
839+
return (W.Call (f, [ e ]))
840+
else bytes_length e
841+
842+
let string_get e e' =
843+
if Config.Flag.use_js_string ()
844+
then
845+
let* f =
846+
register_import
847+
~import_module:"wasm:js-string"
848+
~name:"charCodeAt"
849+
(Fun
850+
{ W.params = [ Ref { nullable = true; typ = Extern }; I32 ]
851+
; result = [ I32 ]
852+
})
853+
in
854+
let* e = string_value e in
855+
let* e' = Value.int_val e' in
856+
Value.val_int (return (W.Call (f, [ e; e' ])))
857+
else bytes_get e e'
806858

807859
let field e idx = wasm_array_get e (Arith.const (Int32.of_int (idx + 1)))
808860

@@ -929,6 +981,21 @@ module Constant = struct
929981
| Const_named of string
930982
| Mutated
931983

984+
let translate_js_string s =
985+
let* i = register_string s in
986+
let* x =
987+
let* name = unit_name in
988+
register_import
989+
~import_module:
990+
(match name with
991+
| None -> "strings"
992+
| Some name -> name ^ ".strings")
993+
~name:(string_of_int i)
994+
(Global { mut = false; typ = Ref { nullable = false; typ = Any } })
995+
in
996+
let* ty = Type.js_type in
997+
return (Const_named ("str_" ^ s), W.StructNew (ty, [ GlobalGet x ]))
998+
932999
let rec translate_rec c =
9331000
match c with
9341001
| Code.Int i -> return (Const, W.RefI31 (Const (I32 (Targetint.to_int32 i))))
@@ -987,38 +1054,29 @@ module Constant = struct
9871054
| Utf (Utf8 s) -> str_js_utf8 s
9881055
| Byte s -> str_js_byte s
9891056
in
990-
let* i = register_string s in
991-
let* x =
992-
let* name = unit_name in
993-
register_import
994-
~import_module:
995-
(match name with
996-
| None -> "strings"
997-
| Some name -> name ^ ".strings")
998-
~name:(string_of_int i)
999-
(Global { mut = false; typ = Ref { nullable = false; typ = Any } })
1000-
in
1001-
let* ty = Type.js_type in
1002-
return (Const_named ("str_" ^ s), W.StructNew (ty, [ GlobalGet x ]))
1057+
translate_js_string s
10031058
| String s ->
1004-
let* ty = Type.string_type in
1005-
if String.length s >= string_length_threshold
1006-
then
1007-
let name = Code.Var.fresh_n "string" in
1008-
let* () = register_data_segment name s in
1009-
return
1010-
( Mutated
1011-
, W.ArrayNewData
1012-
(ty, name, Const (I32 0l), Const (I32 (Int32.of_int (String.length s))))
1013-
)
1059+
if Config.Flag.use_js_string ()
1060+
then translate_js_string (str_js_byte s)
10141061
else
1015-
let l =
1016-
String.fold_right
1017-
~f:(fun c r -> W.Const (I32 (Int32.of_int (Char.code c))) :: r)
1018-
s
1019-
~init:[]
1020-
in
1021-
return (Const_named ("str_" ^ s), W.ArrayNewFixed (ty, l))
1062+
let* ty = Type.string_type in
1063+
if String.length s >= string_length_threshold
1064+
then
1065+
let name = Code.Var.fresh_n "string" in
1066+
let* () = register_data_segment name s in
1067+
return
1068+
( Mutated
1069+
, W.ArrayNewData
1070+
(ty, name, Const (I32 0l), Const (I32 (Int32.of_int (String.length s))))
1071+
)
1072+
else
1073+
let l =
1074+
String.fold_right
1075+
~f:(fun c r -> W.Const (I32 (Int32.of_int (Char.code c))) :: r)
1076+
s
1077+
~init:[]
1078+
in
1079+
return (Const_named ("str_" ^ s), W.ArrayNewFixed (ty, l))
10221080
| Float f ->
10231081
let* ty = Type.float_type in
10241082
return (Const, W.StructNew (ty, [ Const (F64 f) ]))

compiler/lib-wasm/generate.ml

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -291,22 +291,29 @@ module Generate (Target : Target_sig.S) = struct
291291
seq (Memory.array_set x y z) Value.unit
292292
| Extern "caml_floatarray_unsafe_set", [ x; y; z ] ->
293293
seq (Memory.float_array_set x y z) Value.unit
294-
| Extern ("caml_string_unsafe_get" | "caml_bytes_unsafe_get"), [ x; y ] ->
295-
Memory.bytes_get x y
296-
| Extern ("caml_string_unsafe_set" | "caml_bytes_unsafe_set"), [ x; y; z ] ->
294+
| Extern "caml_string_unsafe_get", [ x; y ] -> Memory.string_get x y
295+
| Extern "caml_bytes_unsafe_get", [ x; y ] -> Memory.bytes_get x y
296+
| Extern "caml_bytes_unsafe_set", [ x; y; z ] ->
297297
seq (Memory.bytes_set x y z) Value.unit
298-
| Extern ("caml_string_get" | "caml_bytes_get"), [ x; y ] ->
298+
| Extern "caml_string_get", [ x; y ] ->
299+
seq
300+
(let* cond = Arith.uge (Value.int_val y) (Memory.string_length x) in
301+
instr (W.Br_if (label_index context bound_error_pc, cond)))
302+
(Memory.string_get x y)
303+
| Extern "caml_bytes_get", [ x; y ] ->
299304
seq
300305
(let* cond = Arith.uge (Value.int_val y) (Memory.bytes_length x) in
301306
instr (W.Br_if (label_index context bound_error_pc, cond)))
302307
(Memory.bytes_get x y)
303-
| Extern ("caml_string_set" | "caml_bytes_set"), [ x; y; z ] ->
308+
| Extern "caml_bytes_set", [ x; y; z ] ->
304309
seq
305310
(let* cond = Arith.uge (Value.int_val y) (Memory.bytes_length x) in
306311
let* () = instr (W.Br_if (label_index context bound_error_pc, cond)) in
307312
Memory.bytes_set x y z)
308313
Value.unit
309-
| Extern ("caml_ml_string_length" | "caml_ml_bytes_length"), [ x ] ->
314+
| Extern "caml_ml_string_length", [ x ] ->
315+
Value.val_int (Memory.string_length x)
316+
| Extern "caml_ml_bytes_length", [ x ] ->
310317
Value.val_int (Memory.bytes_length x)
311318
| Extern "%int_add", [ x; y ] -> Value.int_add x y
312319
| Extern "%int_sub", [ x; y ] -> Value.int_sub x y
@@ -782,7 +789,6 @@ module Generate (Target : Target_sig.S) = struct
782789
( Extern
783790
( "caml_string_get"
784791
| "caml_bytes_get"
785-
| "caml_string_set"
786792
| "caml_bytes_set"
787793
| "caml_check_bound"
788794
| "caml_check_bound_gen"

compiler/lib-wasm/target_sig.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ module type S = sig
7777

7878
val bytes_set : expression -> expression -> expression -> unit Code_generation.t
7979

80+
val string_length : expression -> expression
81+
82+
val string_get : expression -> expression -> expression
83+
8084
val box_float : expression -> expression
8185

8286
val unbox_float : expression -> expression

runtime/wasm/array.wat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
(type $block (array (mut (ref eq))))
2323
(type $bytes (array (mut i8)))
24+
(type $string (struct (field anyref)))
2425
(type $float (struct (field f64)))
2526
(type $float_array (array (mut f64)))
2627

runtime/wasm/backtrace.wat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
(type $block (array (mut (ref eq))))
2323
(type $bytes (array (mut i8)))
24+
(type $string (struct (field anyref)))
2425

2526
(func (export "caml_get_exception_raw_backtrace")
2627
(param (ref eq)) (result (ref eq))

runtime/wasm/bigarray.wat

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@
7777
(func $ta_blit_to_bytes
7878
(param (ref extern)) (param i32) (param (ref $bytes)) (param i32)
7979
(param i32)))
80+
(import "bindings" "ta_blit_from_string"
81+
(func $ta_blit_from_string
82+
(param anyref) (param i32) (param (ref extern)) (param i32)
83+
(param i32)))
84+
(import "bindings" "ta_to_string"
85+
(func $ta_to_string (param (ref extern)) (result (ref any))))
8086
(import "fail" "caml_bound_error" (func $caml_bound_error))
8187
(import "fail" "caml_raise_out_of_memory" (func $caml_raise_out_of_memory))
8288
(import "fail" "caml_invalid_argument"
@@ -126,9 +132,12 @@
126132
(func $caml_deserialize_int_4 (param (ref eq)) (result i32)))
127133
(import "marshal" "caml_deserialize_int_8"
128134
(func $caml_deserialize_int_8 (param (ref eq)) (result i64)))
135+
(import "jsstring" "jsstring_length"
136+
(func $jsstring_length (param anyref) (result i32)))
129137

130138
(type $block (array (mut (ref eq))))
131139
(type $bytes (array (mut i8)))
140+
(type $string (struct (field anyref)))
132141
(type $float (struct (field f64)))
133142
(type $float_array (array (mut f64)))
134143

@@ -143,7 +152,13 @@
143152
(type $dup (func (param (ref eq)) (result (ref eq))))
144153
(type $custom_operations
145154
(struct
155+
(@if use-js-string
156+
(@then
157+
(field $id (ref $string))
158+
)
159+
(@else
146160
(field $id (ref $bytes))
161+
))
147162
(field $compare (ref null $compare))
148163
(field $compare_ext (ref null $compare))
149164
(field $hash (ref null $hash))
@@ -2121,10 +2136,23 @@
21212136
(i32.wrap_i64 (i64.shr_u (local.get $d) (i64.const 32))))
21222137
(ref.i31 (i32.const 0)))
21232138

2124-
(export "caml_bytes_of_uint8_array" (func $caml_string_of_uint8_array))
2125-
(func $caml_string_of_uint8_array (export "caml_string_of_uint8_array")
2139+
(@if use-js-string
2140+
(@then
2141+
(func (export "caml_string_of_uint8_array")
21262142
(param (ref eq)) (result (ref eq))
21272143
;; used to convert a typed array to a string
2144+
(local $a (ref extern))
2145+
(local.set $a
2146+
(ref.as_non_null (extern.convert_any (call $unwrap (local.get 0)))))
2147+
(struct.new $string (call $ta_to_string (local.get $a))))
2148+
)
2149+
(@else
2150+
(export "caml_string_of_uint8_array" (func $caml_bytes_of_uint8_array))
2151+
))
2152+
2153+
(func $caml_bytes_of_uint8_array (export "caml_bytes_of_uint8_array")
2154+
(param (ref eq)) (result (ref eq))
2155+
;; used to convert a typed array to bytes
21282156
(local $a (ref extern)) (local $len i32)
21292157
(local $s (ref $bytes))
21302158
(local.set $a
@@ -2136,8 +2164,30 @@
21362164
(local.get $len))
21372165
(local.get $s))
21382166

2139-
(export "caml_uint8_array_of_bytes" (func $caml_uint8_array_of_string))
2140-
(func $caml_uint8_array_of_string (export "caml_uint8_array_of_string")
2167+
(@if use-js-string
2168+
(@then
2169+
(func (export "caml_uint8_array_of_string")
2170+
(param (ref eq)) (result (ref eq))
2171+
;; Convert a string to a typed array
2172+
(local $ta (ref extern)) (local $len i32)
2173+
(local $s anyref)
2174+
(local.set $s
2175+
(struct.get $string 0 (ref.cast (ref $string) (local.get 0))))
2176+
(local.set $len (call $jsstring_length (local.get $s)))
2177+
(local.set $ta
2178+
(call $ta_create
2179+
(i32.const 3) ;; Uint8Array
2180+
(local.get $len)))
2181+
(call $ta_blit_from_string
2182+
(local.get $s) (i32.const 0) (local.get $ta) (i32.const 0)
2183+
(local.get $len))
2184+
(call $wrap (any.convert_extern (local.get $ta))))
2185+
)
2186+
(@else
2187+
(export "caml_uint8_array_of_string" (func $caml_uint8_array_of_bytes))
2188+
))
2189+
2190+
(func $caml_uint8_array_of_bytes (export "caml_uint8_array_of_bytes")
21412191
(param (ref eq)) (result (ref eq))
21422192
;; Convert bytes to a typed array
21432193
(local $ta (ref extern)) (local $len i32)

0 commit comments

Comments
 (0)