Skip to content

Commit ffe3020

Browse files
committed
Optimize calling a known function
1 parent e83dd90 commit ffe3020

File tree

16 files changed

+154
-89
lines changed

16 files changed

+154
-89
lines changed

compiler/lib-wasm/generate.ml

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

179179
let zero_divide_pc = -2
180180

181+
let exact_call kind =
182+
match kind with
183+
| Generic -> false
184+
| Exact | Known _ -> true
185+
181186
let rec translate_expr ctx context x e =
182187
match e with
183-
| Apply { f; args; exact }
184-
when exact || List.length args = if Var.Set.mem x ctx.in_cps then 2 else 1 ->
188+
| Apply { f; args; kind }
189+
when exact_call kind || List.length args = if Var.Set.mem x ctx.in_cps then 2 else 1
190+
->
185191
let rec loop acc l =
186192
match l with
187193
| [] -> (
@@ -198,13 +204,14 @@ module Generate (Target : Target_sig.S) = struct
198204
if b
199205
then return (W.Call (f, List.rev (closure :: acc)))
200206
else
201-
match funct with
202-
| W.RefFunc g ->
207+
match funct, kind with
208+
| W.RefFunc g, _ ->
203209
(* Functions with constant closures ignore their
204210
environment. In case of partial application, we
205211
still need the closure. *)
206-
let* cl = if exact then Value.unit else return closure in
212+
let* cl = if exact_call kind then Value.unit else return closure in
207213
return (W.Call (g, List.rev (cl :: acc)))
214+
| _, Known g -> return (W.Call (g, List.rev (closure :: acc)))
208215
| _ -> return (W.Call_ref (ty, funct, List.rev (closure :: acc))))
209216
| x :: r ->
210217
let* x = load x in

compiler/lib/code.ml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -408,11 +408,16 @@ type field_type =
408408
| Non_float
409409
| Float
410410

411+
type apply_kind =
412+
| Generic
413+
| Exact
414+
| Known of Var.t
415+
411416
type expr =
412417
| Apply of
413418
{ f : Var.t
414419
; args : Var.t list
415-
; exact : bool
420+
; kind : apply_kind
416421
}
417422
| Block of int * Var.t array * array_or_not * mutability
418423
| Field of Var.t * int * field_type
@@ -552,10 +557,12 @@ module Print = struct
552557

553558
let expr f e =
554559
match e with
555-
| Apply { f = g; args; exact } ->
556-
if exact
557-
then Format.fprintf f "%a!(%a)" Var.print g var_list args
558-
else Format.fprintf f "%a(%a)" Var.print g var_list args
560+
| Apply { f = g; args; kind } -> (
561+
match kind with
562+
| Generic -> Format.fprintf f "%a(%a)" Var.print g var_list args
563+
| Exact -> Format.fprintf f "%a!(%a)" Var.print g var_list args
564+
| Known h -> Format.fprintf f "%a{=%a}(%a)" Var.print g Var.print h var_list args
565+
)
559566
| Block (t, a, _, mut) ->
560567
Format.fprintf
561568
f

compiler/lib/code.mli

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

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

compiler/lib/duplicate.ml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ let subst_cont m s (pc, arg) = Addr.Map.find pc m, List.map arg ~f:(fun x -> s x
2424
let expr s e =
2525
match e with
2626
| Constant _ -> e
27-
| Apply { f; args; exact } ->
28-
Apply { f = s f; args = List.map args ~f:(fun x -> s x); exact }
27+
| Apply { f; args; kind = Known g } ->
28+
Apply { f = s f; args = List.map args ~f:(fun x -> s x); kind = Known (s g) }
29+
| Apply { f; args; kind = (Generic | Exact) as kind } ->
30+
Apply { f = s f; args = List.map args ~f:(fun x -> s x); kind }
2931
| Block (n, a, k, mut) -> Block (n, Array.map a ~f:(fun x -> s x), k, mut)
3032
| Field (x, n, field_type) -> Field (s x, n, field_type)
3133
| Closure _ -> failwith "Inlining/Duplicating closure is currenly not supported"

compiler/lib/effects.ml

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -304,12 +304,15 @@ let allocate_closure ~st ~params ~body ~branch =
304304
let name = Var.fresh () in
305305
[ Let (name, Closure (params, (pc, []))) ], name
306306

307-
let tail_call ~st ?(instrs = []) ~exact ~in_cps ~check ~f args =
308-
assert (exact || check);
307+
let tail_call ~st ?(instrs = []) ~kind ~in_cps ~check ~f args =
308+
assert (
309+
match kind with
310+
| Generic -> check
311+
| Exact | Known _ -> true);
309312
let ret = Var.fresh () in
310313
if check then st.trampolined_calls := Var.Set.add ret !(st.trampolined_calls);
311314
if in_cps then st.in_cps := Var.Set.add ret !(st.in_cps);
312-
instrs @ [ Let (ret, Apply { f; args; exact }) ], Return ret
315+
instrs @ [ Let (ret, Apply { f; args; kind }) ], Return ret
313316

314317
let cps_branch ~st ~src (pc, args) =
315318
match Addr.Set.mem pc st.blocks_to_transform with
@@ -327,14 +330,8 @@ let cps_branch ~st ~src (pc, args) =
327330
(* We check the stack depth only for backward edges (so, at
328331
least once per loop iteration) *)
329332
let check = Hashtbl.find st.block_order src >= Hashtbl.find st.block_order pc in
330-
tail_call
331-
~st
332-
~instrs
333-
~exact:true
334-
~in_cps:false
335-
~check
336-
~f:(closure_of_pc ~st pc)
337-
args
333+
let f = closure_of_pc ~st pc in
334+
tail_call ~st ~instrs ~kind:(Known f) ~in_cps:false ~check ~f args
338335

339336
let cps_jump_cont ~st ~src ((pc, _) as cont) =
340337
match Addr.Set.mem pc st.blocks_to_transform with
@@ -396,7 +393,7 @@ let cps_last ~st ~alloc_jump_closures pc (last : last) ~k : instr list * last =
396393
(* Is the number of successive 'returns' is unbounded is CPS, it
397394
means that we have an unbounded of calls in direct style
398395
(even with tail call optimization) *)
399-
tail_call ~st ~exact:true ~in_cps:false ~check:false ~f:k [ x ]
396+
tail_call ~st ~kind:Exact ~in_cps:false ~check:false ~f:k [ x ]
400397
| Raise (x, rmode) -> (
401398
assert (List.is_empty alloc_jump_closures);
402399
match Hashtbl.find_opt st.matching_exn_handler pc with
@@ -431,7 +428,7 @@ let cps_last ~st ~alloc_jump_closures pc (last : last) ~k : instr list * last =
431428
tail_call
432429
~st
433430
~instrs:(Let (exn_handler, Prim (Extern "caml_pop_trap", [])) :: instrs)
434-
~exact:true
431+
~kind:Exact
435432
~in_cps:false
436433
~check:false
437434
~f:exn_handler
@@ -481,6 +478,14 @@ let cps_last ~st ~alloc_jump_closures pc (last : last) ~k : instr list * last =
481478
@ (Let (exn_handler, Prim (Extern "caml_pop_trap", [])) :: body)
482479
, branch ))
483480

481+
let refine_kind k k' =
482+
match k, k' with
483+
| Known _, _ -> k
484+
| _, Known _ -> k'
485+
| Exact, _ -> k
486+
| _, Exact -> k'
487+
| Generic, Generic -> k
488+
484489
let cps_instr ~st (instr : instr) : instr =
485490
match instr with
486491
| Let (x, Closure (params, (pc, _))) when Var.Set.mem x st.cps_needed ->
@@ -498,11 +503,15 @@ let cps_instr ~st (instr : instr) : instr =
498503
(Extern "caml_alloc_dummy_function", [ size; Pc (Int (Targetint.succ a)) ])
499504
)
500505
| _ -> assert false)
501-
| Let (x, Apply { f; args; _ }) when not (Var.Set.mem x st.cps_needed) ->
506+
| Let (x, Apply { f; args; kind }) when not (Var.Set.mem x st.cps_needed) ->
502507
(* At the moment, we turn into CPS any function not called with
503508
the right number of parameter *)
504-
assert (Global_flow.exact_call st.flow_info f (List.length args));
505-
Let (x, Apply { f; args; exact = true })
509+
let kind' = Global_flow.apply_kind st.flow_info f (List.length args) in
510+
assert (
511+
match kind' with
512+
| Generic -> false
513+
| Exact | Known _ -> true);
514+
Let (x, Apply { f; args; kind = refine_kind kind kind' })
506515
| Let (_, (Apply _ | Prim (Extern ("%resume" | "%perform" | "%reperform"), _))) ->
507516
assert false
508517
| _ -> instr
@@ -552,21 +561,21 @@ let cps_block ~st ~k pc block =
552561
[ Let (x, e) ], Return x)
553562
in
554563
match e with
555-
| Apply { f; args; exact } when Var.Set.mem x st.cps_needed ->
564+
| Apply { f; args; kind } when Var.Set.mem x st.cps_needed ->
556565
Some
557566
(fun ~k ->
558-
let exact =
559-
exact || Global_flow.exact_call st.flow_info f (List.length args)
567+
let kind =
568+
refine_kind kind (Global_flow.apply_kind st.flow_info f (List.length args))
560569
in
561-
tail_call ~st ~exact ~in_cps:true ~check:true ~f (args @ [ k ]))
570+
tail_call ~st ~kind ~in_cps:true ~check:true ~f (args @ [ k ]))
562571
| Prim (Extern "%resume", [ Pv stack; Pv f; Pv arg ]) ->
563572
Some
564573
(fun ~k ->
565574
let k' = Var.fresh_n "cont" in
566575
tail_call
567576
~st
568577
~instrs:[ Let (k', Prim (Extern "caml_resume_stack", [ Pv stack; Pv k ])) ]
569-
~exact:(Global_flow.exact_call st.flow_info f 1)
578+
~kind:(Global_flow.apply_kind st.flow_info f 1)
570579
~in_cps:true
571580
~check:true
572581
~f

compiler/lib/generate.ml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,13 @@ module Share = struct
149149
List.fold_left block.body ~init:share ~f:(fun share i ->
150150
match i with
151151
| Let (_, Constant c) -> get_constant c share
152-
| Let (x, Apply { args; exact; _ }) ->
152+
| Let (x, Apply { args; kind; _ }) ->
153153
let trampolined = Var.Set.mem x trampolined_calls in
154+
let exact =
155+
match kind with
156+
| Generic -> false
157+
| Exact | Known _ -> true
158+
in
154159
if (not exact) || trampolined
155160
then add_apply { arity = List.length args; exact; trampolined } share
156161
else share
@@ -1181,7 +1186,12 @@ let remove_unused_tail_args ctx exact trampolined args =
11811186
let rec translate_expr ctx loc x e level : (_ * J.statement_list) Expr_builder.t =
11821187
let open Expr_builder in
11831188
match e with
1184-
| Apply { f; args; exact } ->
1189+
| Apply { f; args; kind } ->
1190+
let exact =
1191+
match kind with
1192+
| Generic -> false
1193+
| Exact | Known _ -> true
1194+
in
11851195
let trampolined = Var.Set.mem x ctx.Ctx.trampolined_calls in
11861196
let args = remove_unused_tail_args ctx exact trampolined args in
11871197
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
@@ -682,17 +682,29 @@ let f ~fast p =
682682
; info_return_vals = rets
683683
}
684684

685-
let exact_call info f n =
685+
let apply_kind info f n =
686686
match Var.Tbl.get info.info_approximation f with
687-
| Top | Values { others = true; _ } -> false
688-
| Values { known; others = false } ->
689-
Var.Set.for_all
690-
(fun g ->
691-
match info.info_defs.(Var.idx g) with
692-
| Expr (Closure (params, _)) -> List.length params = n
693-
| Expr (Block _) -> true
694-
| Expr _ | Phi _ -> assert false)
695-
known
687+
| Top | Values { others = true; _ } -> Generic
688+
| Values { known; others = false } -> (
689+
match
690+
Var.Set.fold
691+
(fun g acc ->
692+
match info.info_defs.(Var.idx g) with
693+
| Expr (Closure (params, _)) ->
694+
if List.length params = n
695+
then
696+
match acc with
697+
| None -> Some (Known g)
698+
| Some (Known _) -> Some Exact
699+
| Some (Exact | Generic) -> acc
700+
else Some Generic
701+
| Expr (Block _) -> acc
702+
| Expr _ | Phi _ -> assert false)
703+
known
704+
None
705+
with
706+
| None -> Exact
707+
| Some kind -> kind)
696708

697709
let function_arity info f =
698710
match Var.Tbl.get info.info_approximation f with
@@ -705,9 +717,10 @@ let function_arity info f =
705717
| Expr (Closure (params, _)) -> (
706718
let n = List.length params in
707719
match acc with
708-
| None -> Some (Some n)
709-
| Some (Some n') when n <> n' -> Some None
710-
| Some _ -> acc)
720+
| None -> Some (Some (n, Known g))
721+
| Some (Some (n', _)) when n <> n' -> Some None
722+
| Some (Some (_, Known _)) -> Some (Some (n, Exact))
723+
| Some (None | Some (_, (Exact | Generic))) -> acc)
711724
| Expr (Block _) -> acc
712725
| Expr _ | Phi _ -> assert false)
713726
known

compiler/lib/global_flow.mli

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,6 @@ type info =
4646

4747
val f : fast:bool -> Code.program -> info
4848

49-
val exact_call : info -> Var.t -> int -> bool
49+
val apply_kind : info -> Var.t -> int -> Code.apply_kind
5050

51-
val function_arity : info -> Var.t -> int option
51+
val function_arity : info -> Var.t -> (int * Code.apply_kind) option

compiler/lib/inline.ml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ let inline ~first_class_primitives live_vars closures name pc (outer, p) =
180180
~init:([], (outer, block.branch, p))
181181
~f:(fun i (rem, state) ->
182182
match i with
183-
| Let (x, Apply { f; args; exact = true; _ }) when Var.Map.mem f closures -> (
183+
| Let (x, Apply { f; args; kind = Exact | Known _; _ })
184+
when Var.Map.mem f closures -> (
184185
let outer, branch, p = state in
185186
let { cl_params = params
186187
; cl_cont = clos_cont
@@ -268,7 +269,7 @@ let inline ~first_class_primitives live_vars closures name pc (outer, p) =
268269
if recursive
269270
then
270271
( Let (f, Closure (params, clos_cont))
271-
:: Let (x, Apply { f; args; exact = true })
272+
:: Let (x, Apply { f; args; kind = Known f })
272273
:: rem
273274
, (outer, branch, p) )
274275
else

0 commit comments

Comments
 (0)