From 1ee1e7b29b098b190895cdcaa5651bcd5a587335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vouillon?= Date: Tue, 17 Dec 2024 18:23:43 +0100 Subject: [PATCH 1/5] Wasm AST: add extern.convert_any --- compiler/lib-wasm/code_generation.ml | 3 ++- compiler/lib-wasm/gc_target.ml | 3 ++- compiler/lib-wasm/initialize_locals.ml | 3 ++- compiler/lib-wasm/wasm_ast.ml | 1 + compiler/lib-wasm/wasm_output.ml | 8 +++++++- compiler/lib-wasm/wat_output.ml | 1 + 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/compiler/lib-wasm/code_generation.ml b/compiler/lib-wasm/code_generation.ml index cd23a3db7e..03f279eb6b 100644 --- a/compiler/lib-wasm/code_generation.ml +++ b/compiler/lib-wasm/code_generation.ml @@ -453,7 +453,8 @@ let rec is_smi e = | RefNull _ | Br_on_cast _ | Br_on_cast_fail _ - | Try _ -> false + | Try _ + | ExternConvertAny _ -> false | BinOp ((F32 _ | F64 _), _, _) | RefTest _ | RefEq _ -> true | IfExpr (_, _, ift, iff) -> is_smi ift && is_smi iff diff --git a/compiler/lib-wasm/gc_target.ml b/compiler/lib-wasm/gc_target.ml index cd82902c31..6ae3a48b6c 100644 --- a/compiler/lib-wasm/gc_target.ml +++ b/compiler/lib-wasm/gc_target.ml @@ -513,7 +513,8 @@ module Value = struct | ArrayLen e' | StructGet (_, _, _, e') | RefCast (_, e') - | RefTest (_, e') -> effect_free e' + | RefTest (_, e') + | ExternConvertAny e' -> effect_free e' | BinOp (_, e1, e2) | ArrayNew (_, e1, e2) | ArrayNewData (_, _, e1, e2) diff --git a/compiler/lib-wasm/initialize_locals.ml b/compiler/lib-wasm/initialize_locals.ml index 5e15235725..c4ecfe8c4e 100644 --- a/compiler/lib-wasm/initialize_locals.ml +++ b/compiler/lib-wasm/initialize_locals.ml @@ -46,7 +46,8 @@ let rec scan_expression ctx e = | RefCast (_, e') | RefTest (_, e') | Br_on_cast (_, _, _, e') - | Br_on_cast_fail (_, _, _, e') -> scan_expression ctx e' + | Br_on_cast_fail (_, _, _, e') + | ExternConvertAny e' -> scan_expression ctx e' | BinOp (_, e', e'') | ArrayNew (_, e', e'') | ArrayNewData (_, _, e', e'') diff --git a/compiler/lib-wasm/wasm_ast.ml b/compiler/lib-wasm/wasm_ast.ml index a23addc4a2..9ca66d6578 100644 --- a/compiler/lib-wasm/wasm_ast.ml +++ b/compiler/lib-wasm/wasm_ast.ml @@ -166,6 +166,7 @@ type expression = | Br_on_cast_fail of int * ref_type * ref_type * expression | IfExpr of value_type * expression * expression * expression | Try of func_type * instruction list * (var * int * value_type) list + | ExternConvertAny of expression and instruction = | Drop of expression diff --git a/compiler/lib-wasm/wasm_output.ml b/compiler/lib-wasm/wasm_output.ml index febd2c650e..78f38b9ac0 100644 --- a/compiler/lib-wasm/wasm_output.ml +++ b/compiler/lib-wasm/wasm_output.ml @@ -658,6 +658,11 @@ end = struct output_instruction st ch (Br (l + 1, Some (Pop ty)))) catches; output_byte ch 0X0B + | ExternConvertAny e' -> + Feature.require gc; + output_expression st ch e'; + output_byte ch 0xFB; + output_byte ch 0x1B and output_instruction st ch i = match i with @@ -871,7 +876,8 @@ end = struct | RefCast (_, e') | RefTest (_, e') | Br_on_cast (_, _, _, e') - | Br_on_cast_fail (_, _, _, e') -> expr_function_references e' set + | Br_on_cast_fail (_, _, _, e') + | ExternConvertAny e' -> expr_function_references e' set | BinOp (_, e', e'') | ArrayNew (_, e', e'') | ArrayNewData (_, _, e', e'') diff --git a/compiler/lib-wasm/wat_output.ml b/compiler/lib-wasm/wat_output.ml index 27c2307801..cc332d7d87 100644 --- a/compiler/lib-wasm/wat_output.ml +++ b/compiler/lib-wasm/wat_output.ml @@ -457,6 +457,7 @@ let expression_or_instructions ctx st in_function = @ instruction (Wasm_ast.Br (i + 1, Some (Pop ty)))))) catches)) ] + | ExternConvertAny e' -> [ List (Atom "extern.convert_any" :: expression e') ] and instruction i = match i with | Drop e -> [ List (Atom "drop" :: expression e) ] From 06c5e7e0b02d2d080923d8504f484b4426f9b72a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vouillon?= Date: Fri, 4 Apr 2025 16:41:16 +0200 Subject: [PATCH 2/5] Wasm AST: add unreachable --- compiler/lib-wasm/initialize_locals.ml | 2 +- compiler/lib-wasm/tail_call.ml | 1 + compiler/lib-wasm/wasm_ast.ml | 1 + compiler/lib-wasm/wasm_output.ml | 3 ++- compiler/lib-wasm/wat_output.ml | 1 + 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/lib-wasm/initialize_locals.ml b/compiler/lib-wasm/initialize_locals.ml index c4ecfe8c4e..d359f3bda3 100644 --- a/compiler/lib-wasm/initialize_locals.ml +++ b/compiler/lib-wasm/initialize_locals.ml @@ -95,7 +95,7 @@ and scan_instruction ctx i = scan_instructions ctx l; scan_instructions ctx l' | CallInstr (_, l) | Return_call (_, l) -> scan_expressions ctx l - | Br (_, None) | Return None | Rethrow _ | Nop | Event _ -> () + | Br (_, None) | Return None | Rethrow _ | Nop | Unreachable | Event _ -> () | ArraySet (_, e, e', e'') -> scan_expression ctx e; scan_expression ctx e'; diff --git a/compiler/lib-wasm/tail_call.ml b/compiler/lib-wasm/tail_call.ml index dfaadad9da..b52142d72d 100644 --- a/compiler/lib-wasm/tail_call.ml +++ b/compiler/lib-wasm/tail_call.ml @@ -59,6 +59,7 @@ let rec instruction ~tail i = | StructSet _ | Return_call _ | Return_call_ref _ + | Unreachable | Event _ -> i and instructions ~tail l = diff --git a/compiler/lib-wasm/wasm_ast.ml b/compiler/lib-wasm/wasm_ast.ml index 9ca66d6578..b1518d1f1c 100644 --- a/compiler/lib-wasm/wasm_ast.ml +++ b/compiler/lib-wasm/wasm_ast.ml @@ -188,6 +188,7 @@ and instruction = | StructSet of var * int * expression * expression | Return_call of var * expression list | Return_call_ref of var * expression * expression list + | Unreachable | Event of Parse_info.t (** Location information *) type import_desc = diff --git a/compiler/lib-wasm/wasm_output.ml b/compiler/lib-wasm/wasm_output.ml index 78f38b9ac0..1d1e84d412 100644 --- a/compiler/lib-wasm/wasm_output.ml +++ b/compiler/lib-wasm/wasm_output.ml @@ -759,6 +759,7 @@ end = struct output_expression st ch e'; output_byte ch 0x15; output_uint ch (Hashtbl.find st.type_names typ) + | Unreachable -> output_byte ch 0x00 | Event _ -> () let output_globals ch (st, global_idx, fields) = @@ -940,7 +941,7 @@ end = struct ~f:(fun set i -> expr_function_references i set) ~init:(expr_function_references e' set) l - | Event _ -> set + | Unreachable | Event _ -> set let function_references fields set = List.fold_left diff --git a/compiler/lib-wasm/wat_output.ml b/compiler/lib-wasm/wat_output.ml index cc332d7d87..00759f7e89 100644 --- a/compiler/lib-wasm/wat_output.ml +++ b/compiler/lib-wasm/wat_output.ml @@ -535,6 +535,7 @@ let expression_or_instructions ctx st in_function = :: index st.type_names typ :: List.concat (List.map ~f:expression (l @ [ e ]))) ] + | Unreachable -> [ List [ Atom "unreachable" ] ] | Event Parse_info.{ src = None | Some ""; _ } -> [ Comment "@" ] | Event Parse_info.{ src = Some src; col; line; _ } -> let loc = Format.sprintf "%s:%d:%d" src line col in From 21b546abc57354500f1ea427c74c56369e9df2af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vouillon?= Date: Fri, 28 Mar 2025 17:38:36 +0100 Subject: [PATCH 3/5] Wasm AST: add heap types 'struct', 'array' and 'none' --- compiler/lib-wasm/code_generation.ml | 31 +++++++++++++++++++++++----- compiler/lib-wasm/gc_target.ml | 2 +- compiler/lib-wasm/wasm_ast.ml | 3 +++ compiler/lib-wasm/wasm_output.ml | 4 ++++ compiler/lib-wasm/wat_output.ml | 3 +++ 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/compiler/lib-wasm/code_generation.ml b/compiler/lib-wasm/code_generation.ml index 03f279eb6b..5669b9ccb2 100644 --- a/compiler/lib-wasm/code_generation.ml +++ b/compiler/lib-wasm/code_generation.ml @@ -161,10 +161,31 @@ let heap_type_sub (ty : W.heap_type) (ty' : W.heap_type) st = match ty, ty' with | Func, Func | Extern, Extern - | (Any | Eq | I31 | Type _), Any - | (Eq | I31 | Type _), Eq - | I31, I31 -> true, st + | (Any | Eq | Struct | Array | I31 | None_ | Type _), Any + | (Eq | Struct | Array | I31 | None_ | Type _), Eq + | (None_ | Struct), Struct -> true, st + | (None_ | Array), Array -> true, st + | (None_ | I31), I31 -> true, st + | None_, None_ -> true, st + | Type t, Struct -> + ( (let type_field = Hashtbl.find st.context.types t in + match type_field.typ with + | Struct _ -> true + | Array _ | Func _ -> false) + , st ) + | Type t, Array -> + ( (let type_field = Hashtbl.find st.context.types t in + match type_field.typ with + | Array _ -> true + | Struct _ | Func _ -> false) + , st ) | Type t, Type t' -> type_index_sub t t' st + | None_, Type t -> + ( (let type_field = Hashtbl.find st.context.types t in + match type_field.typ with + | Struct _ | Array _ -> true + | Func _ -> false) + , st ) (* Func and Extern are only in suptyping relation with themselves *) | Func, _ | _, Func @@ -172,8 +193,8 @@ let heap_type_sub (ty : W.heap_type) (ty' : W.heap_type) st = | _, Extern (* Any has no supertype *) | Any, _ - (* I31, struct and arrays have no subtype (of a different kind) *) - | _, (I31 | Type _) -> false, st + (* I31, struct, array and none have no other subtype *) + | _, (I31 | Type _ | Struct | Array | None_) -> false, st let register_global name ?exported_name ?(constant = false) typ init st = st.context.other_fields <- diff --git a/compiler/lib-wasm/gc_target.ml b/compiler/lib-wasm/gc_target.ml index 6ae3a48b6c..8d44ab7630 100644 --- a/compiler/lib-wasm/gc_target.ml +++ b/compiler/lib-wasm/gc_target.ml @@ -479,7 +479,7 @@ module Value = struct | W.RefI31 _ -> ( match typ.typ with | W.I31 | Eq | Any -> return (W.Const (I32 1l)) - | Type _ | Func | Extern -> return (W.Const (I32 0l))) + | Struct | Array | Type _ | None_ | Func | Extern -> return (W.Const (I32 0l))) | GlobalGet nm -> ( let* init = get_global nm in match init with diff --git a/compiler/lib-wasm/wasm_ast.ml b/compiler/lib-wasm/wasm_ast.ml index b1518d1f1c..980acc01c2 100644 --- a/compiler/lib-wasm/wasm_ast.ml +++ b/compiler/lib-wasm/wasm_ast.ml @@ -27,7 +27,10 @@ type heap_type = | Extern | Any | Eq + | Struct + | Array | I31 + | None_ | Type of var type ref_type = diff --git a/compiler/lib-wasm/wasm_output.ml b/compiler/lib-wasm/wasm_output.ml index 1d1e84d412..35a246d493 100644 --- a/compiler/lib-wasm/wasm_output.ml +++ b/compiler/lib-wasm/wasm_output.ml @@ -172,13 +172,17 @@ end = struct res (****) + let output_heaptype type_names ch typ = match (typ : heap_type) with + | None_ -> output_byte ch 0x71 | Func -> output_byte ch 0x70 | Extern -> output_byte ch 0x6F | Any -> output_byte ch 0x6E | Eq -> output_byte ch 0x6D | I31 -> output_byte ch 0x6C + | Struct -> output_byte ch 0x6B + | Array -> output_byte ch 0x6A | Type nm -> output_sint ch (Hashtbl.find type_names nm) let output_valtype type_names ch (typ : value_type) = diff --git a/compiler/lib-wasm/wat_output.ml b/compiler/lib-wasm/wat_output.ml index 00759f7e89..e511791ec0 100644 --- a/compiler/lib-wasm/wat_output.ml +++ b/compiler/lib-wasm/wat_output.ml @@ -150,7 +150,10 @@ let heap_type st (ty : heap_type) = | Extern -> Atom "extern" | Any -> Atom "any" | Eq -> Atom "eq" + | Struct -> Atom "struct" + | Array -> Atom "array" | I31 -> Atom "i31" + | None_ -> Atom "none" | Type t -> index st.type_names t let ref_type st { nullable; typ } = From b9c03081ec750f3a7a688d37f1c8b5f25e05a261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vouillon?= Date: Fri, 4 Apr 2025 16:56:34 +0200 Subject: [PATCH 4/5] Wasm AST: possibility to provide an explicit function type --- compiler/lib-wasm/curry.ml | 63 ++++++++++++++++++++++++++++---- compiler/lib-wasm/generate.ml | 25 ++++++++++--- compiler/lib-wasm/wasm_ast.ml | 3 +- compiler/lib-wasm/wasm_output.ml | 9 +++-- compiler/lib-wasm/wat_output.ml | 11 ++++-- 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/compiler/lib-wasm/curry.ml b/compiler/lib-wasm/curry.ml index 0d96ad5cb9..f383d55da5 100644 --- a/compiler/lib-wasm/curry.ml +++ b/compiler/lib-wasm/curry.ml @@ -102,7 +102,14 @@ module Make (Target : Target_sig.S) = struct let param_names = args @ [ f ] in let locals, body = function_body ~context ~param_names ~body in W.Function - { name; exported_name = None; typ = func_type 1; param_names; locals; body } + { name + ; exported_name = None + ; typ = None + ; signature = func_type 1 + ; param_names + ; locals + ; body + } let curry_name n m = Printf.sprintf "curry_%d_%d" n m @@ -130,7 +137,14 @@ module Make (Target : Target_sig.S) = struct let param_names = [ x; f ] in let locals, body = function_body ~context ~param_names ~body in W.Function - { name; exported_name = None; typ = func_type 1; param_names; locals; body } + { name + ; exported_name = None + ; typ = None + ; signature = func_type 1 + ; param_names + ; locals + ; body + } :: functions let curry ~arity ~name = curry ~arity arity ~name @@ -174,7 +188,14 @@ module Make (Target : Target_sig.S) = struct let param_names = args @ [ f ] in let locals, body = function_body ~context ~param_names ~body in W.Function - { name; exported_name = None; typ = func_type 2; param_names; locals; body } + { name + ; exported_name = None + ; typ = None + ; signature = func_type 2 + ; param_names + ; locals + ; body + } let cps_curry_name n m = Printf.sprintf "cps_curry_%d_%d" n m @@ -206,7 +227,14 @@ module Make (Target : Target_sig.S) = struct let param_names = [ x; cont; f ] in let locals, body = function_body ~context ~param_names ~body in W.Function - { name; exported_name = None; typ = func_type 2; param_names; locals; body } + { name + ; exported_name = None + ; typ = None + ; signature = func_type 2 + ; param_names + ; locals + ; body + } :: functions let cps_curry ~arity ~name = cps_curry ~arity arity ~name @@ -243,7 +271,14 @@ module Make (Target : Target_sig.S) = struct let param_names = l @ [ f ] in let locals, body = function_body ~context ~param_names ~body in W.Function - { name; exported_name = None; typ = func_type arity; param_names; locals; body } + { name + ; exported_name = None + ; typ = None + ; signature = func_type arity + ; param_names + ; locals + ; body + } let cps_apply ~context ~arity ~name = assert (arity > 2); @@ -283,7 +318,14 @@ module Make (Target : Target_sig.S) = struct let param_names = l @ [ f ] in let locals, body = function_body ~context ~param_names ~body in W.Function - { name; exported_name = None; typ = func_type arity; param_names; locals; body } + { name + ; exported_name = None + ; typ = None + ; signature = func_type arity + ; param_names + ; locals + ; body + } let dummy ~context ~cps ~arity ~name = let arity = if cps then arity + 1 else arity in @@ -311,7 +353,14 @@ module Make (Target : Target_sig.S) = struct let param_names = l @ [ f ] in let locals, body = function_body ~context ~param_names ~body in W.Function - { name; exported_name = None; typ = func_type arity; param_names; locals; body } + { name + ; exported_name = None + ; typ = None + ; signature = func_type arity + ; param_names + ; locals + ; body + } let f ~context = IntMap.iter diff --git a/compiler/lib-wasm/generate.ml b/compiler/lib-wasm/generate.ml index 45f76c6f27..6cae2506b4 100644 --- a/compiler/lib-wasm/generate.ml +++ b/compiler/lib-wasm/generate.ml @@ -1055,8 +1055,9 @@ module Generate (Target : Target_sig.S) = struct (match name_opt with | None -> Option.map ~f:(fun name -> name ^ ".init") unit_name | Some _ -> None) + ; typ = None + ; signature = func_type param_count ; param_names - ; typ = func_type param_count ; locals ; body } @@ -1064,7 +1065,7 @@ module Generate (Target : Target_sig.S) = struct let init_function ~context ~to_link = let name = Code.Var.fresh_n "initialize" in - let typ = { W.params = []; result = [ Value.value ] } in + let signature = { W.params = []; result = [ Value.value ] } in let locals, body = function_body ~context @@ -1073,7 +1074,10 @@ module Generate (Target : Target_sig.S) = struct (List.fold_right ~f:(fun name cont -> let* f = - register_import ~import_module:"OCaml" ~name:(name ^ ".init") (Fun typ) + register_import + ~import_module:"OCaml" + ~name:(name ^ ".init") + (Fun signature) in let* () = instr (Drop (Call (f, []))) in cont) @@ -1081,17 +1085,26 @@ module Generate (Target : Target_sig.S) = struct to_link) in context.other_fields <- - W.Function { name; exported_name = None; typ; param_names = []; locals; body } + W.Function + { name + ; exported_name = None + ; typ = None + ; signature + ; param_names = [] + ; locals + ; body + } :: context.other_fields; name let entry_point context toplevel_fun entry_name = - let typ, param_names, body = entry_point ~toplevel_fun in + let signature, param_names, body = entry_point ~toplevel_fun in let locals, body = function_body ~context ~param_names ~body in W.Function { name = Var.fresh_n "entry_point" ; exported_name = Some entry_name - ; typ + ; typ = None + ; signature ; param_names ; locals ; body diff --git a/compiler/lib-wasm/wasm_ast.ml b/compiler/lib-wasm/wasm_ast.ml index 980acc01c2..d50325fff2 100644 --- a/compiler/lib-wasm/wasm_ast.ml +++ b/compiler/lib-wasm/wasm_ast.ml @@ -210,7 +210,8 @@ type module_field = | Function of { name : var ; exported_name : string option - ; typ : func_type + ; typ : var option + ; signature : func_type ; param_names : var list ; locals : (var * value_type) list ; body : instruction list diff --git a/compiler/lib-wasm/wasm_output.ml b/compiler/lib-wasm/wasm_output.ml index 35a246d493..408dc538bd 100644 --- a/compiler/lib-wasm/wasm_output.ml +++ b/compiler/lib-wasm/wasm_output.ml @@ -220,10 +220,13 @@ end = struct List.fold_left ~f:(fun acc field -> match field with - | Function { typ; _ } | Import { desc = Fun typ; _ } -> func_type acc typ + | Function { typ = None; signature; _ } | Import { desc = Fun signature; _ } -> + func_type acc signature | Import { desc = Tag typ; _ } -> func_type acc { params = [ typ ]; result = [] } | Type l -> explicit_definition acc l - | Import { desc = Global _; _ } | Data _ | Global _ | Tag _ -> acc) + | Function { typ = Some _; _ } + | Import { desc = Global _; _ } + | Data _ | Global _ | Tag _ -> acc) ~init:acc fields @@ -338,7 +341,7 @@ end = struct List.fold_left ~f:(fun acc field -> match field with - | Function { typ; _ } -> typ :: acc + | Function { signature; _ } -> signature :: acc | Type _ | Import _ | Data _ | Global _ | Tag _ -> acc) ~init:[] fields diff --git a/compiler/lib-wasm/wat_output.ml b/compiler/lib-wasm/wat_output.ml index e511791ec0..714138033f 100644 --- a/compiler/lib-wasm/wat_output.ml +++ b/compiler/lib-wasm/wat_output.ml @@ -550,7 +550,7 @@ let expression ctx st = fst (expression_or_instructions ctx st false) let instructions ctx st = snd (expression_or_instructions ctx st true) -let funct ctx st name exported_name typ param_names locals body = +let funct ctx st name exported_name typ signature param_names locals body = let st = { st with local_names = @@ -562,7 +562,10 @@ let funct ctx st name exported_name typ param_names locals body = in List ((Atom "func" :: index st.func_names name :: export exported_name) - @ func_type st ~param_names typ + @ (match typ with + | None -> [] + | Some typ -> [ List [ Atom "type"; index st.type_names typ ] ]) + @ func_type st ~param_names signature @ List.map ~f:(fun (i, t) -> List [ Atom "local"; index st.local_names i; value_type st t ]) locals @@ -617,8 +620,8 @@ let type_field st { name; typ; supertype; final } = let field ctx st f = match f with - | Function { name; exported_name; typ; param_names; locals; body } -> - [ funct ctx st name exported_name typ param_names locals body ] + | Function { name; exported_name; typ; signature; param_names; locals; body } -> + [ funct ctx st name exported_name typ signature param_names locals body ] | Global { name; exported_name; typ; init } -> [ List (Atom "global" From 926ecb3dc09a6433edff99d9cbe2ec69c03d8263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vouillon?= Date: Fri, 4 Apr 2025 16:32:13 +0200 Subject: [PATCH 5/5] Wasm linker: fix parsing of recursive groups of types The initial size of that type mapping table is the number of entry in the type section. Since a recursive group entry corresponds to one entry but can contain several types, we may have to resize it. --- compiler/lib-wasm/wasm_link.ml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/lib-wasm/wasm_link.ml b/compiler/lib-wasm/wasm_link.ml index a53fc34a4d..6026ce1683 100644 --- a/compiler/lib-wasm/wasm_link.ml +++ b/compiler/lib-wasm/wasm_link.ml @@ -697,6 +697,12 @@ module Read = struct let pos = st.type_index_count in let pos' = add_rectype types ty in let count = Array.length ty in + let len = Array.length st.type_mapping in + if pos + count > len + then ( + let m = Array.make (len + (len / 5) + count) 0 in + Array.blit ~src:st.type_mapping ~src_pos:0 ~dst:m ~dst_pos:0 ~len; + st.type_mapping <- m); for i = 0 to count - 1 do st.type_mapping.(pos + i) <- pos' + i done;