Skip to content

Commit 9f34b01

Browse files
committed
Optimize calling a known function
1 parent 68dd986 commit 9f34b01

16 files changed

+172
-108
lines changed

compiler/lib-wasm/generate.ml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,16 @@ module Generate (Target : Target_sig.S) = struct
184184

185185
let zero_divide_pc = -2
186186

187+
let exact_call kind =
188+
match kind with
189+
| Generic -> false
190+
| Exact | Known _ -> true
191+
187192
let rec translate_expr ctx context x e =
188193
match e with
189-
| Apply { f; args; exact }
190-
when exact || List.length args = if Var.Set.mem x ctx.in_cps then 2 else 1 ->
194+
| Apply { f; args; kind }
195+
when exact_call kind || List.length args = if Var.Set.mem x ctx.in_cps then 2 else 1
196+
->
191197
let rec loop acc l =
192198
match l with
193199
| [] -> (
@@ -204,13 +210,14 @@ module Generate (Target : Target_sig.S) = struct
204210
if b
205211
then return (W.Call (f, List.rev (closure :: acc)))
206212
else
207-
match funct with
208-
| W.RefFunc g ->
213+
match funct, kind with
214+
| W.RefFunc g, _ ->
209215
(* Functions with constant closures ignore their
210216
environment. In case of partial application, we
211217
still need the closure. *)
212-
let* cl = if exact then Value.unit else return closure in
218+
let* cl = if exact_call kind then Value.unit else return closure in
213219
return (W.Call (g, List.rev (cl :: acc)))
220+
| _, Known g -> return (W.Call (g, List.rev (closure :: acc)))
214221
| _ -> return (W.Call_ref (ty, funct, List.rev (closure :: acc))))
215222
| x :: r ->
216223
let* x = load x in

compiler/lib/code.ml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,16 @@ type field_type =
412412
| Non_float
413413
| Float
414414

415+
type apply_kind =
416+
| Generic
417+
| Exact
418+
| Known of Var.t
419+
415420
type expr =
416421
| Apply of
417422
{ f : Var.t
418423
; args : Var.t list
419-
; exact : bool
424+
; kind : apply_kind
420425
}
421426
| Block of int * Var.t array * array_or_not * mutability
422427
| Field of Var.t * int * field_type
@@ -556,10 +561,12 @@ module Print = struct
556561

557562
let expr f e =
558563
match e with
559-
| Apply { f = g; args; exact } ->
560-
if exact
561-
then Format.fprintf f "%a!(%a)" Var.print g var_list args
562-
else Format.fprintf f "%a(%a)" Var.print g var_list args
564+
| Apply { f = g; args; kind } -> (
565+
match kind with
566+
| Generic -> Format.fprintf f "%a(%a)" Var.print g var_list args
567+
| Exact -> Format.fprintf f "%a!(%a)" Var.print g var_list args
568+
| Known h -> Format.fprintf f "%a{=%a}(%a)" Var.print g Var.print h var_list args
569+
)
563570
| Block (t, a, _, mut) ->
564571
Format.fprintf
565572
f

compiler/lib/code.mli

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,16 @@ type field_type =
208208
| Non_float
209209
| Float
210210

211+
type apply_kind =
212+
| Generic
213+
| Exact (* # of arguments = # of parameters *)
214+
| Known of Var.t (* Exact and we know which function is called *)
215+
211216
type expr =
212217
| Apply of
213218
{ f : Var.t
214219
; args : Var.t list
215-
; exact : bool (* if true, then # of arguments = # of parameters *)
220+
; kind : apply_kind
216221
}
217222
| Block of int * Var.t array * array_or_not * mutability
218223
| Field of Var.t * int * field_type

compiler/lib/effects.ml

Lines changed: 47 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -336,12 +336,15 @@ let allocate_closure ~st ~params ~body ~branch =
336336
let name = Var.fresh () in
337337
[ Let (name, Closure (params, (pc, []))) ], name
338338

339-
let tail_call ~st ?(instrs = []) ~exact ~in_cps ~check ~f args =
340-
assert (exact || check);
339+
let tail_call ~st ?(instrs = []) ~kind ~in_cps ~check ~f args =
340+
assert (
341+
match kind with
342+
| Generic -> check
343+
| Exact | Known _ -> true);
341344
let ret = Var.fresh () in
342345
if check then st.trampolined_calls := Var.Set.add ret !(st.trampolined_calls);
343346
if in_cps then st.in_cps := Var.Set.add ret !(st.in_cps);
344-
instrs @ [ Let (ret, Apply { f; args; exact }) ], Return ret
347+
instrs @ [ Let (ret, Apply { f; args; kind }) ], Return ret
345348

346349
let cps_branch ~st ~src (pc, args) =
347350
match Addr.Set.mem pc st.blocks_to_transform with
@@ -359,14 +362,8 @@ let cps_branch ~st ~src (pc, args) =
359362
(* We check the stack depth only for backward edges (so, at
360363
least once per loop iteration) *)
361364
let check = Hashtbl.find st.block_order src >= Hashtbl.find st.block_order pc in
362-
tail_call
363-
~st
364-
~instrs
365-
~exact:true
366-
~in_cps:false
367-
~check
368-
~f:(closure_of_pc ~st pc)
369-
args
365+
let f = closure_of_pc ~st pc in
366+
tail_call ~st ~instrs ~kind:(Known f) ~in_cps:false ~check ~f args
370367

371368
let cps_jump_cont ~st ~src ((pc, _) as cont) =
372369
match Addr.Set.mem pc st.blocks_to_transform with
@@ -433,7 +430,7 @@ let cps_last ~st ~alloc_jump_closures pc (last : last) ~k : instr list * last =
433430
(* If the number of successive 'returns' is unbounded in CPS, it
434431
means that we have an unbounded of calls in direct style
435432
(even with tail call optimization) *)
436-
tail_call ~st ~exact:true ~in_cps:false ~check:false ~f:k [ x ]
433+
tail_call ~st ~kind:Exact ~in_cps:false ~check:false ~f:k [ x ]
437434
| Raise (x, rmode) -> (
438435
assert (List.is_empty alloc_jump_closures);
439436
match Hashtbl.find_opt st.matching_exn_handler pc with
@@ -468,7 +465,7 @@ let cps_last ~st ~alloc_jump_closures pc (last : last) ~k : instr list * last =
468465
tail_call
469466
~st
470467
~instrs:(Let (exn_handler, Prim (Extern "caml_pop_trap", [])) :: instrs)
471-
~exact:true
468+
~kind:Exact
472469
~in_cps:false
473470
~check:false
474471
~f:exn_handler
@@ -522,6 +519,14 @@ let cps_last ~st ~alloc_jump_closures pc (last : last) ~k : instr list * last =
522519
@ (Let (exn_handler, Prim (Extern "caml_pop_trap", [])) :: body)
523520
, branch ))
524521

522+
let refine_kind k k' =
523+
match k, k' with
524+
| Known _, _ -> k
525+
| _, Known _ -> k'
526+
| Exact, _ -> k
527+
| _, Exact -> k'
528+
| Generic, Generic -> k
529+
525530
let rewrite_instr ~st (instr : instr) : instr =
526531
match instr with
527532
| Let (x, Closure (_, (pc, _))) when Var.Set.mem x st.cps_needed ->
@@ -542,34 +547,37 @@ let rewrite_instr ~st (instr : instr) : instr =
542547
(Extern "caml_alloc_dummy_function", [ size; Pc (Int (Targetint.succ a)) ])
543548
)
544549
| _ -> assert false)
545-
| Let (x, Apply { f; args; exact }) when not (Var.Set.mem x st.cps_needed) ->
546-
if double_translate ()
547-
then
548-
let exact =
549-
(* If this function is unknown to the global flow analysis, then it was
550-
introduced by the lambda lifting and we don't have exactness info any more. *)
551-
exact
552-
|| Var.idx f < Var.Tbl.length st.flow_info.info_approximation
553-
&& Global_flow.exact_call st.flow_info f (List.length args)
554-
in
555-
Let (x, Apply { f; args; exact })
556-
else (
557-
(* At the moment, we turn into CPS any function not called with
558-
the right number of parameter *)
559-
assert (Global_flow.exact_call st.flow_info f (List.length args));
560-
Let (x, Apply { f; args; exact = true }))
550+
| Let (x, Apply { f; args; kind }) when not (Var.Set.mem x st.cps_needed) ->
551+
let kind' =
552+
(* If this function is unknown to the global flow analysis,
553+
then it was introduced by the lambda lifting before double
554+
translation and we don't have exactness info any more. *)
555+
if Var.idx f >= Var.Tbl.length st.flow_info.info_approximation
556+
then Generic
557+
else Global_flow.apply_kind st.flow_info f (List.length args)
558+
in
559+
assert (
560+
double_translate ()
561+
||
562+
(* We turn into CPS any function not called with the right
563+
number of arguments *)
564+
match kind' with
565+
| Generic -> false
566+
| Exact | Known _ -> true);
567+
Let (x, Apply { f; args; kind = refine_kind kind kind' })
561568
| Let (_, e) when effect_primitive_or_application e ->
562569
(* For the CPS target, applications of CPS functions and effect primitives require
563570
more work (allocating a continuation and/or modifying end-of-block branches) and
564571
are handled in a specialized function. *)
565572
assert false
566573
| _ -> instr
567574

568-
let call_exact flow_info (f : Var.t) nargs : bool =
575+
let call_kind flow_info (f : Var.t) nargs =
569576
(* If [f] is unknown to the global flow analysis, then it was introduced by
570577
the lambda lifting and we don't have exactness about it. *)
571-
Var.idx f < Var.Tbl.length flow_info.Global_flow.info_approximation
572-
&& Global_flow.exact_call flow_info f nargs
578+
if Var.idx f >= Var.Tbl.length flow_info.Global_flow.info_approximation
579+
then Generic
580+
else Global_flow.apply_kind flow_info f nargs
573581

574582
let cps_instr ~st (instr : instr) : instr list =
575583
match instr with
@@ -578,7 +586,7 @@ let cps_instr ~st (instr : instr) : instr list =
578586
Otherwise, the runtime primitive is used. *)
579587
let unit = Var.fresh_n "unit" in
580588
[ Let (unit, Constant (Int Targetint.zero))
581-
; Let (x, Apply { exact = call_exact st.flow_info f 1; f; args = [ unit ] })
589+
; Let (x, Apply { kind = call_kind st.flow_info f 1; f; args = [ unit ] })
582590
]
583591
| _ -> [ rewrite_instr ~st instr ]
584592

@@ -653,11 +661,11 @@ let cps_block ~st ~k ~orig_pc block =
653661
[ Let (x, e) ], Return x)
654662
in
655663
match e with
656-
| Apply { f; args; exact } when Var.Set.mem x st.cps_needed ->
664+
| Apply { f; args; kind } when Var.Set.mem x st.cps_needed ->
657665
Some
658666
(fun ~k ->
659-
let exact = exact || call_exact st.flow_info f (List.length args) in
660-
tail_call ~st ~exact ~in_cps:true ~check:true ~f (args @ [ k ]))
667+
let kind = refine_kind kind (call_kind st.flow_info f (List.length args)) in
668+
tail_call ~st ~kind ~in_cps:true ~check:true ~f (args @ [ k ]))
661669
| Prim (Extern "%resume", [ Pv stack; Pv f; Pv arg; tail ]) ->
662670
Some
663671
(fun ~k ->
@@ -666,7 +674,7 @@ let cps_block ~st ~k ~orig_pc block =
666674
~st
667675
~instrs:
668676
[ Let (k', Prim (Extern "caml_resume_stack", [ Pv stack; tail; Pv k ])) ]
669-
~exact:(call_exact st.flow_info f 1)
677+
~kind:(call_kind st.flow_info f 1)
670678
~in_cps:true
671679
~check:true
672680
~f
@@ -754,8 +762,8 @@ let rewrite_direct_block ~st ~cps_needed ~closure_info ~pc block =
754762
(* We just need to call [f] in direct style. *)
755763
let unit = Var.fresh_n "unit" in
756764
let unit_val = Int Targetint.zero in
757-
let exact = call_exact st.flow_info f 1 in
758-
[ Let (unit, Constant unit_val); Let (x, Apply { exact; f; args = [ unit ] }) ]
765+
let kind = call_kind st.flow_info f 1 in
766+
[ Let (unit, Constant unit_val); Let (x, Apply { kind; f; args = [ unit ] }) ]
759767
| (Let _ | Assign _ | Set_field _ | Offset_ref _ | Array_set _ | Event _) as instr
760768
-> [ instr ]
761769
in

compiler/lib/generate.ml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,14 @@ module Share = struct
157157
List.fold_left block.body ~init:share ~f:(fun share i ->
158158
match i with
159159
| Let (_, Constant c) -> get_constant c share
160-
| Let (x, Apply { args; exact; _ }) ->
160+
| Let (x, Apply { args; kind; _ }) ->
161161
let trampolined = Var.Set.mem x trampolined_calls in
162162
let in_cps = Var.Set.mem x in_cps in
163+
let exact =
164+
match kind with
165+
| Generic -> false
166+
| Exact | Known _ -> true
167+
in
163168
if (not exact) || trampolined
164169
then
165170
add_apply
@@ -1241,7 +1246,12 @@ let remove_unused_tail_args ctx exact trampolined args =
12411246
let rec translate_expr ctx loc x e level : (_ * J.statement_list) Expr_builder.t =
12421247
let open Expr_builder in
12431248
match e with
1244-
| Apply { f; args; exact } ->
1249+
| Apply { f; args; kind } ->
1250+
let exact =
1251+
match kind with
1252+
| Generic -> false
1253+
| Exact | Known _ -> true
1254+
in
12451255
let trampolined = Var.Set.mem x ctx.Ctx.trampolined_calls in
12461256
let args = remove_unused_tail_args ctx exact trampolined args in
12471257
let* () = info ~need_loc:true mutator_p in

compiler/lib/generate_closure.ml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ let rec collect_apply pc blocks visited tc =
4646
match block.branch with
4747
| Return x -> (
4848
match List.last block.body with
49-
| Some (Let (y, Apply { f; exact = true; _ })) when Code.Var.compare x y = 0 ->
50-
Some (add_multi f pc tc)
49+
| Some (Let (y, Apply { f; kind = Exact | Known _; _ }))
50+
when Code.Var.compare x y = 0 -> Some (add_multi f pc tc)
5151
| None -> None
5252
| Some _ -> None)
5353
| _ -> None
@@ -100,7 +100,7 @@ module Trampoline = struct
100100
match counter with
101101
| None ->
102102
{ params = []
103-
; body = [ Let (return, Apply { f; args; exact = true }) ]
103+
; body = [ Let (return, Apply { f; args; kind = Known f }) ]
104104
; branch = Return return
105105
}
106106
| Some counter ->
@@ -110,7 +110,7 @@ module Trampoline = struct
110110
[ Let
111111
( counter_plus_1
112112
, Prim (Extern "%int_add", [ Pv counter; Pc (Int Targetint.one) ]) )
113-
; Let (return, Apply { f; args = counter_plus_1 :: args; exact = true })
113+
; Let (return, Apply { f; args = counter_plus_1 :: args; kind = Known f })
114114
]
115115
; branch = Return return
116116
}
@@ -139,14 +139,14 @@ module Trampoline = struct
139139
(match counter with
140140
| None ->
141141
[ Event loc
142-
; Let (result1, Apply { f; args; exact = true })
142+
; Let (result1, Apply { f; args; kind = Known f })
143143
; Event Parse_info.zero
144144
; Let (result2, Prim (Extern "caml_trampoline", [ Pv result1 ]))
145145
]
146146
| Some counter ->
147147
[ Event loc
148148
; Let (counter, Constant (Int Targetint.zero))
149-
; Let (result1, Apply { f; args = counter :: args; exact = true })
149+
; Let (result1, Apply { f; args = counter :: args; kind = Known f })
150150
; Event Parse_info.zero
151151
; Let (result2, Prim (Extern "caml_trampoline", [ Pv result1 ]))
152152
])
@@ -222,7 +222,7 @@ module Trampoline = struct
222222
let bounce_call_pc = free_pc + 1 in
223223
let free_pc = free_pc + 2 in
224224
match List.rev block.body with
225-
| Let (x, Apply { f; args; exact = true }) :: rem_rev ->
225+
| Let (x, Apply { f; args; kind = Exact | Known _ }) :: rem_rev ->
226226
assert (Var.equal f ci.f_name);
227227
let blocks =
228228
Addr.Map.add

compiler/lib/global_flow.ml

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -704,17 +704,29 @@ let f ~fast p =
704704
; info_return_vals = rets
705705
}
706706

707-
let exact_call info f n =
707+
let apply_kind info f n =
708708
match Var.Tbl.get info.info_approximation f with
709-
| Top | Values { others = true; _ } -> false
710-
| Values { known; others = false } ->
711-
Var.Set.for_all
712-
(fun g ->
713-
match info.info_defs.(Var.idx g) with
714-
| Expr (Closure (params, _)) -> List.length params = n
715-
| Expr (Block _) -> true
716-
| Expr _ | Phi _ -> assert false)
717-
known
709+
| Top | Values { others = true; _ } -> Generic
710+
| Values { known; others = false } -> (
711+
match
712+
Var.Set.fold
713+
(fun g acc ->
714+
match info.info_defs.(Var.idx g) with
715+
| Expr (Closure (params, _)) ->
716+
if List.length params = n
717+
then
718+
match acc with
719+
| None -> Some (Known g)
720+
| Some (Known _) -> Some Exact
721+
| Some (Exact | Generic) -> acc
722+
else Some Generic
723+
| Expr (Block _) -> acc
724+
| Expr _ | Phi _ -> assert false)
725+
known
726+
None
727+
with
728+
| None -> Exact
729+
| Some kind -> kind)
718730

719731
let function_arity info f =
720732
match Var.Tbl.get info.info_approximation f with
@@ -727,9 +739,10 @@ let function_arity info f =
727739
| Expr (Closure (params, _)) -> (
728740
let n = List.length params in
729741
match acc with
730-
| None -> Some (Some n)
731-
| Some (Some n') when n <> n' -> Some None
732-
| Some _ -> acc)
742+
| None -> Some (Some (n, Known g))
743+
| Some (Some (n', _)) when n <> n' -> Some None
744+
| Some (Some (_, Known _)) -> Some (Some (n, Exact))
745+
| Some (None | Some (_, (Exact | Generic))) -> acc)
733746
| Expr (Block _) -> acc
734747
| Expr _ | Phi _ -> assert false)
735748
known

0 commit comments

Comments
 (0)