Skip to content

Commit 24dcd04

Browse files
vouillonhhugo
authored andcommitted
Optimize calls to a statically known function
When the function called is statically known, we can directly call it without using the closure's code pointer.
1 parent ae75485 commit 24dcd04

File tree

4 files changed

+87
-37
lines changed

4 files changed

+87
-37
lines changed

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
* Compiler/wasm: optimize integer operations (#2032)
4848
* Compiler/wasm: use type analysis to remove some unnecessary uses of JavasScript strict equality (#2040)
4949
* Compiler/wasm: use more precise environment types (#2041)
50+
* Compiler/wasm: optimize calls to statically known function (#2044)
5051

5152
## Bug fixes
5253
* Compiler: fix stack overflow issues with double translation (#1869)

compiler/lib-wasm/generate.ml

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module Generate (Target : Target_sig.S) = struct
3636
{ live : int array
3737
; in_cps : Effects.in_cps
3838
; deadcode_sentinal : Var.t
39+
; global_flow_info : Global_flow.info
3940
; types : Typing.typ Var.Tbl.t
4041
; blocks : block Addr.Map.t
4142
; closures : Closure_conversion.closure Var.Map.t
@@ -784,44 +785,53 @@ module Generate (Target : Target_sig.S) = struct
784785

785786
let rec translate_expr ctx context x e =
786787
match e with
787-
| Apply { f; args; exact }
788-
when exact || List.length args = if Var.Set.mem x ctx.in_cps then 2 else 1 ->
789-
let rec loop acc l =
790-
match l with
791-
| [] -> (
792-
let arity = List.length args in
793-
let funct = Var.fresh () in
794-
let* closure = tee funct (load f) in
795-
let* ty, funct =
796-
Memory.load_function_pointer
797-
~cps:(Var.Set.mem x ctx.in_cps)
798-
~arity
799-
(load funct)
800-
in
801-
let* b = is_closure f in
802-
if b
803-
then return (W.Call (f, List.rev (closure :: acc)))
804-
else
805-
match funct with
806-
| W.RefFunc g ->
807-
(* Functions with constant closures ignore their
788+
| Apply { f; args; exact; _ } ->
789+
if exact || List.length args = if Var.Set.mem x ctx.in_cps then 2 else 1
790+
then
791+
let rec loop acc l =
792+
match l with
793+
| [] -> (
794+
let arity = List.length args in
795+
let funct = Var.fresh () in
796+
let* closure = tee funct (load f) in
797+
let* ty, funct =
798+
Memory.load_function_pointer
799+
~cps:(Var.Set.mem x ctx.in_cps)
800+
~arity
801+
(load funct)
802+
in
803+
let* b = is_closure f in
804+
if b
805+
then return (W.Call (f, List.rev (closure :: acc)))
806+
else
807+
match funct with
808+
| W.RefFunc g ->
809+
(* Functions with constant closures ignore their
808810
environment. In case of partial application, we
809811
still need the closure. *)
810-
let* cl = if exact then Value.unit else return closure in
811-
return (W.Call (g, List.rev (cl :: acc)))
812-
| _ -> return (W.Call_ref (ty, funct, List.rev (closure :: acc))))
813-
| x :: r ->
814-
let* x = load_and_box ctx x in
815-
loop (x :: acc) r
816-
in
817-
loop [] args
818-
| Apply { f; args; _ } ->
819-
let* apply =
820-
need_apply_fun ~cps:(Var.Set.mem x ctx.in_cps) ~arity:(List.length args)
821-
in
822-
let* args = expression_list (fun x -> load_and_box ctx x) args in
823-
let* closure = load f in
824-
return (W.Call (apply, args @ [ closure ]))
812+
let* cl = if exact then Value.unit else return closure in
813+
return (W.Call (g, List.rev (cl :: acc)))
814+
| _ -> (
815+
match
816+
if exact
817+
then Global_flow.get_unique_closure ctx.global_flow_info f
818+
else None
819+
with
820+
| Some g -> return (W.Call (g, List.rev (closure :: acc)))
821+
| None -> return (W.Call_ref (ty, funct, List.rev (closure :: acc)))
822+
))
823+
| x :: r ->
824+
let* x = load_and_box ctx x in
825+
loop (x :: acc) r
826+
in
827+
loop [] args
828+
else
829+
let* apply =
830+
need_apply_fun ~cps:(Var.Set.mem x ctx.in_cps) ~arity:(List.length args)
831+
in
832+
let* args = expression_list (fun x -> load_and_box ctx x) args in
833+
let* closure = load f in
834+
return (W.Call (apply, args @ [ closure ]))
825835
| Block (tag, a, _, _) ->
826836
Memory.allocate
827837
~deadcode_sentinal:ctx.deadcode_sentinal
@@ -1390,6 +1400,7 @@ module Generate (Target : Target_sig.S) = struct
13901400
~warn_on_unhandled_effect
13911401
*)
13921402
~deadcode_sentinal
1403+
~global_flow_info
13931404
~types =
13941405
global_context.unit_name <- unit_name;
13951406
let p, closures = Closure_conversion.f p in
@@ -1400,6 +1411,7 @@ module Generate (Target : Target_sig.S) = struct
14001411
{ live = live_vars
14011412
; in_cps
14021413
; deadcode_sentinal
1414+
; global_flow_info
14031415
; types
14041416
; blocks = p.blocks
14051417
; closures
@@ -1512,7 +1524,17 @@ let f ~context ~unit_name p ~live_vars ~in_cps ~deadcode_sentinal ~global_flow_d
15121524
let types = Typing.f ~state ~info ~deadcode_sentinal p in
15131525
let t = Timer.make () in
15141526
let p = fix_switch_branches p in
1515-
let res = G.f ~context ~unit_name ~live_vars ~in_cps ~deadcode_sentinal ~types p in
1527+
let res =
1528+
G.f
1529+
~context
1530+
~unit_name
1531+
~live_vars
1532+
~in_cps
1533+
~deadcode_sentinal
1534+
~global_flow_info:info
1535+
~types
1536+
p
1537+
in
15161538
if times () then Format.eprintf " code gen.: %a@." Timer.print t;
15171539
res
15181540

compiler/lib/global_flow.ml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,31 @@ let exact_call info f n =
805805
| Expr _ | Phi _ -> assert false)
806806
known
807807

808+
let get_unique_closure info f =
809+
(* The specialize pass can create knew functions *)
810+
if Var.idx f >= Var.Tbl.length info.info_approximation
811+
then None
812+
else
813+
match Var.Tbl.get info.info_approximation f with
814+
| Top | Values { others = true; _ } -> None
815+
| Values { known; others = false } -> (
816+
match
817+
Var.Set.fold
818+
(fun g acc ->
819+
match info.info_defs.(Var.idx g) with
820+
| Expr (Closure _) -> (
821+
match acc with
822+
| None -> Some (Some g)
823+
| Some (Some _) -> Some None
824+
| Some None -> acc)
825+
| Expr (Block _) -> acc
826+
| Expr _ | Phi _ -> assert false)
827+
known
828+
None
829+
with
830+
| None -> None
831+
| Some kind -> kind)
832+
808833
let function_arity info f =
809834
match Var.Tbl.get info.info_approximation f with
810835
| Top | Values { others = true; _ } -> None

compiler/lib/global_flow.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ val f : fast:bool -> Code.program -> state * info
8282

8383
val exact_call : info -> Var.t -> int -> bool
8484

85+
val get_unique_closure : info -> Var.t -> Var.t option
86+
8587
val function_arity : info -> Var.t -> int option

0 commit comments

Comments
 (0)