From ea924f1b853d4d3e177034b02fb6a6fba9183cdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Vouillon?= Date: Fri, 28 Mar 2025 16:12:16 +0100 Subject: [PATCH] More precise environment type Casting to the exact type should be slightly faster since we avoid the test for i31. --- compiler/lib-wasm/code_generation.ml | 25 +++++++ compiler/lib-wasm/code_generation.mli | 2 + compiler/lib-wasm/curry.ml | 17 +++-- compiler/lib-wasm/gc_target.ml | 18 ++++- compiler/lib-wasm/generate.ml | 9 ++- compiler/lib-wasm/target_sig.ml | 6 ++ runtime/wasm/effect.wat | 104 ++++++++++++++------------ runtime/wasm/obj.wat | 26 ++++--- 8 files changed, 135 insertions(+), 72 deletions(-) diff --git a/compiler/lib-wasm/code_generation.ml b/compiler/lib-wasm/code_generation.ml index 4efeb11a1b..99c2d4f0ca 100644 --- a/compiler/lib-wasm/code_generation.ml +++ b/compiler/lib-wasm/code_generation.ml @@ -503,6 +503,31 @@ let tee ?typ x e = let should_make_global x st = Var.Set.mem x st.context.globalized_variables, st +let get_constant x st = Var.Hashtbl.find_opt st.context.constants x, st + +let placeholder_value typ f = + let* c = get_constant typ in + match c with + | None -> + let x = Var.fresh () in + let* () = register_constant typ (W.GlobalGet x) in + let* () = + register_global + ~constant:true + x + { mut = false; typ = Ref { nullable = false; typ = Type typ } } + (f typ) + in + return (W.GlobalGet x) + | Some c -> return c + +let empty_struct = + let* typ = + register_type "empty_struct" (fun () -> + return { supertype = None; final = true; typ = W.Struct [] }) + in + placeholder_value typ (fun typ -> W.StructNew (typ, [])) + let value_type st = st.context.value_type, st let get_constant x st = Var.Hashtbl.find_opt st.context.constants x, st diff --git a/compiler/lib-wasm/code_generation.mli b/compiler/lib-wasm/code_generation.mli index 8655450dda..cf073d6ebc 100644 --- a/compiler/lib-wasm/code_generation.mli +++ b/compiler/lib-wasm/code_generation.mli @@ -205,3 +205,5 @@ val array_placeholder : Code.Var.t -> expression val default_value : Wasm_ast.value_type -> (Wasm_ast.expression * Wasm_ast.value_type * Wasm_ast.ref_type option) t + +val empty_struct : expression diff --git a/compiler/lib-wasm/curry.ml b/compiler/lib-wasm/curry.ml index b6d5ab0cab..fbdacc1dc8 100644 --- a/compiler/lib-wasm/curry.ml +++ b/compiler/lib-wasm/curry.ml @@ -35,7 +35,12 @@ module Make (Target : Target_sig.S) = struct let call ?typ ~cps ~arity closure args = let funct = Var.fresh () in - let* closure = tee ?typ funct closure in + let closure = tee ?typ funct closure in + let* closure = + match typ with + | None -> Memory.cast_closure ~cps ~arity closure + | Some _ -> closure + in let args = args @ [ closure ] in let* ty, funct = Memory.load_function_pointer @@ -68,7 +73,7 @@ module Make (Target : Target_sig.S) = struct let body = let* () = no_event in let* () = bind_parameters args in - let* _ = add_var f in + let* _ = add_var ~typ:Type.closure f in let* args' = expression_list load args in let* _f = load f in let rec loop m args closure closure_typ = @@ -126,7 +131,7 @@ module Make (Target : Target_sig.S) = struct let body = let* () = no_event in let* _ = add_var x in - let* _ = add_var f in + let* _ = add_var ~typ:Type.closure f in push (Closure.curry_allocate ~cps:false ~arity m ~f:name' ~closure:f ~arg:x) in let param_names = [ x; f ] in @@ -154,7 +159,7 @@ module Make (Target : Target_sig.S) = struct let body = let* () = no_event in let* () = bind_parameters args in - let* _ = add_var f in + let* _ = add_var ~typ:Type.closure f in let* args' = expression_list load args in let* _f = load f in let rec loop m args closure closure_typ = @@ -214,7 +219,7 @@ module Make (Target : Target_sig.S) = struct let* () = no_event in let* _ = add_var x in let* _ = add_var cont in - let* _ = add_var f in + let* _ = add_var ~typ:Type.closure f in let* e = Closure.curry_allocate ~cps:true ~arity m ~f:name' ~closure:f ~arg:x in let* c = call ~cps:false ~arity:1 (load cont) [ e ] in instr (W.Return (Some c)) @@ -333,7 +338,7 @@ module Make (Target : Target_sig.S) = struct let body = let* () = no_event in let* () = bind_parameters l in - let* _ = add_var f in + let* _ = add_var ~typ:Type.closure f in let* typ, closure = Memory.load_real_closure ~cps ~arity (load f) in let* l = expression_list load l in let* e = diff --git a/compiler/lib-wasm/gc_target.ml b/compiler/lib-wasm/gc_target.ml index 36ca054e4c..8248d4f092 100644 --- a/compiler/lib-wasm/gc_target.ml +++ b/compiler/lib-wasm/gc_target.ml @@ -27,6 +27,8 @@ let include_closure_arity = false module Type = struct let value = W.Ref { nullable = false; typ = Eq } + let closure = W.Ref { nullable = false; typ = Struct } + let block_type = register_type "block" (fun () -> return @@ -205,7 +207,8 @@ module Type = struct let primitive_type n = { W.params = List.init ~len:n ~f:(fun _ -> value); result = [ value ] } - let func_type n = primitive_type (n + 1) + let func_type n = + { W.params = List.init ~len:n ~f:(fun _ -> value) @ [ closure ]; result = [ value ] } let function_type ~cps n = let n = if cps then n + 1 else n in @@ -430,6 +433,8 @@ module Value = struct let* t = Type.block_type in array_placeholder t + let dummy_closure = empty_struct + let as_block e = let* t = Type.block_type in let* e = e in @@ -811,6 +816,11 @@ module Memory = struct then 1 else (if include_closure_arity then 1 else 0) + if arity = 1 then 1 else 2 + let cast_closure ~cps ~arity closure = + let arity = if cps then arity - 1 else arity in + let* ty = Type.closure_type ~usage:`Access ~cps arity in + wasm_cast ty closure + let load_function_pointer ~cps ~arity ?(skip_cast = false) closure = let arity = if cps then arity - 1 else arity in let* ty = Type.closure_type ~usage:`Access ~cps arity in @@ -1189,7 +1199,7 @@ module Closure = struct if free_variable_count = 0 then (* The closures are all constants and the environment is empty. *) - let* _ = add_var (Code.Var.fresh ()) in + let* _ = add_var ~typ:Type.closure (Code.Var.fresh ()) in return () else let env_type_id = Option.value ~default:(-1) info.id in @@ -1199,7 +1209,7 @@ module Closure = struct match info.Closure_conversion.functions with | [ _ ] -> let* typ = Type.env_type ~cps ~arity ~env_type_id ~env_type:[] in - let* _ = add_var f in + let* _ = add_var ~typ:Type.closure f in let env = Code.Var.fresh_n "env" in let* () = store @@ -1220,7 +1230,7 @@ module Closure = struct let* typ = Type.rec_closure_type ~cps ~arity ~function_count ~env_type_id ~env_type:[] in - let* _ = add_var f in + let* _ = add_var ~typ:Type.closure f in let env = Code.Var.fresh_n "env" in let* env_typ = Type.rec_env_type ~function_count ~env_type_id ~env_type:[] in let* () = diff --git a/compiler/lib-wasm/generate.ml b/compiler/lib-wasm/generate.ml index fa196e8571..5f7d425eb1 100644 --- a/compiler/lib-wasm/generate.ml +++ b/compiler/lib-wasm/generate.ml @@ -793,7 +793,12 @@ module Generate (Target : Target_sig.S) = struct | [] -> ( let arity = List.length args in let funct = Var.fresh () in - let* closure = tee funct (load f) in + let* closure = + Memory.cast_closure + ~cps:(Var.Set.mem x ctx.in_cps) + ~arity + (tee funct (load f)) + in let* ty, funct = Memory.load_function_pointer ~cps:(Var.Set.mem x ctx.in_cps) @@ -809,7 +814,7 @@ module Generate (Target : Target_sig.S) = struct (* Functions with constant closures ignore their environment. In case of partial application, we still need the closure. *) - let* cl = if exact then Value.unit else return closure in + let* cl = if exact then Value.dummy_closure else return closure in return (W.Call (g, List.rev (cl :: acc))) | _ -> ( match diff --git a/compiler/lib-wasm/target_sig.ml b/compiler/lib-wasm/target_sig.ml index 053e3be066..eb6643d878 100644 --- a/compiler/lib-wasm/target_sig.ml +++ b/compiler/lib-wasm/target_sig.ml @@ -27,6 +27,8 @@ module type S = sig -> [ `Expr of Wasm_ast.expression | `Var of Wasm_ast.var ] list -> expression + val cast_closure : cps:bool -> arity:int -> expression -> expression + val load_function_pointer : cps:bool -> arity:int @@ -100,6 +102,8 @@ module type S = sig module Type : sig val value : Wasm_ast.value_type + val closure : Wasm_ast.value_type + val func_type : int -> Wasm_ast.func_type val primitive_type : int -> Wasm_ast.func_type @@ -162,6 +166,8 @@ module type S = sig val dummy_block : expression + val dummy_closure : expression + val as_block : expression -> expression end diff --git a/runtime/wasm/effect.wat b/runtime/wasm/effect.wat index 05bc0ad9c2..d0ae76330d 100644 --- a/runtime/wasm/effect.wat +++ b/runtime/wasm/effect.wat @@ -44,10 +44,10 @@ (type $block (array (mut (ref eq)))) (type $bytes (array (mut i8))) - (type $function_1 (func (param (ref eq) (ref eq)) (result (ref eq)))) + (type $function_1 (func (param (ref eq) (ref struct)) (result (ref eq)))) (type $closure (sub (struct (;(field i32);) (field (ref $function_1))))) (type $function_3 - (func (param (ref eq) (ref eq) (ref eq) (ref eq)) (result (ref eq)))) + (func (param (ref eq) (ref eq) (ref eq) (ref struct)) (result (ref eq)))) (type $closure_3 (sub $closure (struct (field (ref $function_1)) (field (ref $function_3))))) @@ -66,7 +66,7 @@ (@string $effect_unhandled "Effect.Unhandled") (func $raise_unhandled - (param $eff (ref eq)) (param (ref eq)) (result (ref eq)) + (param $eff (ref eq)) (param (ref struct)) (result (ref eq)) (block $null (call $caml_raise_with_arg (br_on_null $null @@ -132,6 +132,9 @@ (ref.i31 (global.get $cont_tag)))))) (i32.const 0)) + (type $dummy_env (struct)) + (global $dummy_env (ref struct) (struct.new $dummy_env)) + (@if (= effects "jspi") (@then ;; Apply a function f to a value v, both contained in a pair (f, v) @@ -139,10 +142,11 @@ (type $pair (struct (field (ref eq)) (field (ref eq)))) (func $apply_pair (param $p (ref $pair)) (result (ref eq)) - (local $f (ref eq)) + (local $f (ref $closure)) (return_call_ref $function_1 (struct.get $pair 1 (local.get $p)) - (local.tee $f (struct.get $pair 0 (local.get $p))) - (struct.get $closure 0 (ref.cast (ref $closure) (local.get $f))))) + (local.tee $f + (ref.cast (ref $closure) (struct.get $pair 0 (local.get $p)))) + (struct.get $closure 0 (local.get $f)))) ;; Low-level primitives @@ -298,7 +302,7 @@ (field $cont (ref eq))))) (func $call_effect_handler - (param $tail (ref eq)) (param $venv (ref eq)) (result (ref eq)) + (param $tail (ref eq)) (param $venv (ref struct)) (result (ref eq)) (local $env (ref $call_handler_env)) (local $handler (ref $closure_3)) (local.set $env (ref.cast (ref $call_handler_env) (local.get $venv))) @@ -340,7 +344,7 @@ (ref.is_null (struct.get $fiber $next (global.get $stack)))) (then (return_call $raise_unhandled - (local.get $eff) (ref.i31 (i32.const 0))))) + (local.get $eff) (global.get $dummy_env)))) (return_call $capture_continuation (ref.func $do_perform) (local.get $eff))) @@ -438,9 +442,9 @@ (@if (= effects "cps") (@then (type $function_2 - (func (param (ref eq) (ref eq) (ref eq)) (result (ref eq)))) + (func (param (ref eq) (ref eq) (ref struct)) (result (ref eq)))) (type $function_4 - (func (param (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) + (func (param (ref eq) (ref eq) (ref eq) (ref eq) (ref struct)) (result (ref eq)))) (type $cps_closure (sub (struct (field (ref $function_2))))) (type $cps_closure_0 (sub (struct (field (ref $function_1))))) @@ -485,7 +489,7 @@ (ref.i31 (i32.const 0))) (func $raise_exception - (param $exn (ref eq)) (param (ref eq)) (result (ref eq)) + (param $exn (ref eq)) (param (ref struct)) (result (ref eq)) (throw $ocaml_exception (local.get $exn))) (global $raise_exception (ref eq) @@ -506,13 +510,14 @@ (param $exn (ref eq)) (param (ref eq)) (result (ref eq)) (local.get $exn)) - (func $identity (param (ref eq)) (param (ref eq)) (result (ref eq)) + (func $identity (param (ref eq)) (param (ref struct)) (result (ref eq)) (local.get 0)) (global $identity (ref $closure) (struct.new $closure (ref.func $identity))) (func $trampoline_iterator - (param $f (ref eq)) (param $venv (ref eq)) (result (ref eq)) + (param $vf (ref eq)) (param $venv (ref struct)) (result (ref eq)) + (local $f (ref $cps_closure)) (local $env (ref $iterator)) (local $i i32) (local $args (ref $block)) (local.set $env (ref.cast (ref $iterator) (local.get $venv))) @@ -527,12 +532,12 @@ (array.len (local.get $args))) (then (global.get $identity)) (else (local.get $env))) - (local.get $f) - (struct.get $cps_closure 0 - (ref.cast (ref $cps_closure) (local.get $f))))) + (local.tee $f (ref.cast (ref $cps_closure) (local.get $vf))) + (struct.get $cps_closure 0 (local.get $f)))) (func $apply_iterator - (param $f (ref eq)) (param $venv (ref eq)) (result (ref eq)) + (param $vf (ref eq)) (param $venv (ref struct)) (result (ref eq)) + (local $f (ref $cps_closure)) (local $env (ref $iterator)) (local $i i32) (local $args (ref $block)) (local.set $env (ref.cast (ref $iterator) (local.get $venv))) @@ -550,9 +555,8 @@ (i32.add (local.get $i) (i32.const 1)))) (else (local.get $env))) - (local.get $f) - (struct.get $cps_closure 0 - (ref.cast (ref $cps_closure) (local.get $f))))) + (local.tee $f (ref.cast (ref $cps_closure) (local.get $vf))) + (struct.get $cps_closure 0 (local.get $f)))) (func (export "caml_apply_continuation") (param $args (ref eq)) (result (ref eq)) @@ -562,11 +566,13 @@ (ref.cast (ref $block) (local.get $args)))) (func $dummy_cps_fun - (param (ref eq)) (param (ref eq)) (param (ref eq)) (result (ref eq)) + (param (ref eq)) (param (ref eq)) (param (ref struct)) (result (ref eq)) (unreachable)) (func $caml_trampoline (export "caml_cps_trampoline") - (param $f (ref eq)) (param $vargs (ref eq)) (result (ref eq)) + (param $vf (ref eq)) (param $vargs (ref eq)) (result (ref eq)) + (local $f (ref $closure)) + (local $f_cps (ref $cps_closure)) (local $args (ref $block)) (local $i i32) (local $res (ref eq)) (local $exn (ref eq)) (local $top (ref $exn_stack)) @@ -589,9 +595,9 @@ (i32.eq (array.len (local.get $args)) (i32.const 1)) (then (call_ref $function_1 (global.get $identity) - (local.get $f) - (struct.get $cps_closure_0 0 - (ref.cast (ref $cps_closure_0) (local.get $f))))) + (local.tee $f + (ref.cast (ref $cps_closure_0) (local.get $vf))) + (struct.get $cps_closure_0 0 (local.get $f)))) (else (call_ref $function_2 (array.get $block (local.get $args) (i32.const 1)) @@ -604,9 +610,9 @@ (ref.func $trampoline_iterator) (i32.const 2) (local.get $args)))) - (local.get $f) - (struct.get $cps_closure 0 - (ref.cast (ref $cps_closure) (local.get $f))))))) + (local.tee $f_cps + (ref.cast (ref $cps_closure) (local.get $vf))) + (struct.get $cps_closure 0 (local.get $f_cps)))))) (global.set $cps_fiber_stack (local.get $saved_fiber_stack)) (return (local.get $res))) (catch $ocaml_exception @@ -622,15 +628,14 @@ (struct.set $cps_fiber $exn_stack (global.get $cps_fiber_stack) (struct.get $exn_stack $next (local.get $top))) - (local.set $f (struct.get $exn_stack $h (local.get $top))) + (local.set $vf (struct.get $exn_stack $h (local.get $top))) (try (do (local.set $res (call_ref $function_1 (local.get $exn) - (local.get $f) - (struct.get $closure 0 - (ref.cast (ref $closure) (local.get $f))))) + (local.tee $f (ref.cast (ref $closure) (local.get $vf))) + (struct.get $closure 0 (local.get $f)))) (global.set $cps_fiber_stack (local.get $saved_fiber_stack)) (return (local.get $res))) (catch $ocaml_exception @@ -686,16 +691,18 @@ (func (export "caml_perform_effect") (param $eff (ref eq)) (param $k0 (ref eq)) (result (ref eq)) - (local $handler (ref eq)) (local $k1 (ref eq)) + (local $handler (ref $cps_closure_3)) + (local $k1 (ref eq)) (local $cont (ref $block)) (local $last_fiber (ref $cps_fiber)) (if (ref.is_null (struct.get $cps_fiber $next (global.get $cps_fiber_stack))) (then (return_call $raise_unhandled - (local.get $eff) (ref.i31 (i32.const 0))))) + (local.get $eff) (global.get $dummy_env)))) (local.set $handler - (struct.get $cps_fiber $effect (global.get $cps_fiber_stack))) + (ref.cast (ref $cps_closure_3) + (struct.get $cps_fiber $effect (global.get $cps_fiber_stack)))) (local.set $last_fiber (global.get $cps_fiber_stack)) (struct.set $cps_fiber $cont (local.get $last_fiber) (local.get $k0)) (local.set $cont @@ -705,13 +712,12 @@ (return_call_ref $function_4 (local.get $eff) (local.get $cont) (local.get $last_fiber) (local.get $k1) (local.get $handler) - (struct.get $cps_closure_3 1 - (ref.cast (ref $cps_closure_3) (local.get $handler))))) + (struct.get $cps_closure_3 1 (local.get $handler)))) (func (export "caml_reperform_effect") (param $eff (ref eq)) (param $vcont (ref eq)) (param $vtail (ref eq)) (param $k0 (ref eq)) (result (ref eq)) - (local $handler (ref eq)) (local $k1 (ref eq)) + (local $handler (ref $cps_closure_3)) (local $k1 (ref eq)) (local $cont (ref $block)) (local $tail (ref $cps_fiber)) (local $last_fiber (ref $cps_fiber)) (if (ref.is_null @@ -723,11 +729,12 @@ (local.get $vtail) (local.get $k0))) (return_call $raise_unhandled - (local.get $eff) (ref.i31 (i32.const 0))))) + (local.get $eff) (global.get $dummy_env)))) (local.set $cont (ref.cast (ref $block) (local.get $vcont))) (local.set $tail (ref.cast (ref $cps_fiber) (local.get $vtail))) (local.set $handler - (struct.get $cps_fiber $effect (global.get $cps_fiber_stack))) + (ref.cast (ref $cps_closure_3) + (struct.get $cps_fiber $effect (global.get $cps_fiber_stack)))) (local.set $last_fiber (global.get $cps_fiber_stack)) (struct.set $cps_fiber $cont (local.get $last_fiber) (local.get $k0)) (struct.set $cps_fiber $next (local.get $tail) (local.get $last_fiber)) @@ -737,19 +744,20 @@ (local.get $eff) (local.get $cont) (local.get $last_fiber) (local.get $k1) (local.get $handler) - (struct.get $cps_closure_3 1 - (ref.cast (ref $cps_closure_3) (local.get $handler))))) + (struct.get $cps_closure_3 1 (local.get $handler)))) (func $cps_call_handler - (param $handler (ref eq)) (param $x (ref eq)) (result (ref eq)) + (param $vhandler (ref eq)) (param $x (ref eq)) (result (ref eq)) + (local $handler (ref $cps_closure)) (return_call_ref $function_2 (local.get $x) (call $caml_pop_fiber) - (local.get $handler) - (struct.get $cps_closure 0 - (ref.cast (ref $cps_closure) (local.get $handler))))) + (local.tee $handler + (ref.cast (ref $cps_closure) (local.get $vhandler))) + (struct.get $cps_closure 0 (local.get $handler)))) - (func $value_handler (param $x (ref eq)) (param (ref eq)) (result (ref eq)) + (func $value_handler + (param $x (ref eq)) (param (ref struct)) (result (ref eq)) (return_call $cps_call_handler (struct.get $cps_fiber $value (global.get $cps_fiber_stack)) (local.get $x))) @@ -757,7 +765,7 @@ (global $value_handler (ref $closure) (struct.new $closure (ref.func $value_handler))) - (func $exn_handler (param $x (ref eq)) (param (ref eq)) (result (ref eq)) + (func $exn_handler (param $x (ref eq)) (param (ref struct)) (result (ref eq)) (return_call $cps_call_handler (struct.get $cps_fiber $exn (global.get $cps_fiber_stack)) (local.get $x))) diff --git a/runtime/wasm/obj.wat b/runtime/wasm/obj.wat index 5e06a4a5ed..b396a6ef13 100644 --- a/runtime/wasm/obj.wat +++ b/runtime/wasm/obj.wat @@ -33,12 +33,12 @@ (type $bytes (array (mut i8))) (type $float (struct (field f64))) (type $float_array (array (mut f64))) - (type $function_1 (func (param (ref eq) (ref eq)) (result (ref eq)))) + (type $function_1 (func (param (ref eq) (ref struct)) (result (ref eq)))) (type $closure (sub (struct (;(field i32);) (field (ref $function_1))))) (type $closure_last_arg (sub $closure (struct (;(field i32);) (field (ref $function_1))))) (type $function_2 - (func (param (ref eq) (ref eq) (ref eq)) (result (ref eq)))) + (func (param (ref eq) (ref eq) (ref struct)) (result (ref eq)))) (type $cps_closure (sub (struct (field (ref $function_2))))) (type $cps_closure_last_arg (sub $cps_closure (struct (field (ref $function_2))))) @@ -59,7 +59,7 @@ (field (mut (ref null $closure_2)))))) (type $function_3 - (func (param (ref eq) (ref eq) (ref eq) (ref eq)) (result (ref eq)))) + (func (param (ref eq) (ref eq) (ref eq) (ref struct)) (result (ref eq)))) (type $closure_3 (sub $closure @@ -71,7 +71,7 @@ (field (mut (ref null $closure_3)))))) (type $function_4 - (func (param (ref eq) (ref eq) (ref eq) (ref eq) (ref eq)) + (func (param (ref eq) (ref eq) (ref eq) (ref eq) (ref struct)) (result (ref eq)))) (type $closure_4 @@ -493,22 +493,24 @@ ) (@else (func $caml_callback_1 (export "caml_callback_1") - (param $f (ref eq)) (param $x (ref eq)) (result (ref eq)) + (param $vf (ref eq)) (param $x (ref eq)) (result (ref eq)) + (local $f (ref $closure)) (return_call_ref $function_1 (local.get $x) - (local.get $f) - (struct.get $closure 0 (ref.cast (ref $closure) (local.get $f))))) + (local.tee $f (ref.cast (ref $closure) (local.get $vf))) + (struct.get $closure 0 (local.get $f)))) (func (export "caml_callback_2") - (param $f (ref eq)) (param $x (ref eq)) (param $y (ref eq)) + (param $vf (ref eq)) (param $x (ref eq)) (param $y (ref eq)) (result (ref eq)) + (local $f (ref $closure_2)) (drop (block $not_direct (result (ref eq)) (return_call_ref $function_2 (local.get $x) (local.get $y) - (local.get $f) - (struct.get $closure_2 1 + (local.tee $f (br_on_cast_fail $not_direct (ref eq) (ref $closure_2) - (local.get $f)))))) + (local.get $vf))) + (struct.get $closure_2 1 (local.get $f))))) (return_call $caml_callback_1 - (call $caml_callback_1 (local.get $f) (local.get $x)) + (call $caml_callback_1 (local.get $vf) (local.get $x)) (local.get $y))) )) )