Skip to content

Commit 25fbbf7

Browse files
committed
Compiler: lower level switch
1 parent 559f0d7 commit 25fbbf7

File tree

16 files changed

+524
-526
lines changed

16 files changed

+524
-526
lines changed

compiler/lib/code.ml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ type last =
354354
| Stop
355355
| Branch of cont
356356
| Cond of Var.t * cont * cont
357-
| Switch of Var.t * cont array * cont array
357+
| Switch of Var.t * cont array
358358
| Pushtrap of cont * Var.t * cont * Addr.Set.t
359359
| Poptrap of cont
360360

@@ -499,10 +499,9 @@ module Print = struct
499499
| Branch c -> Format.fprintf f "branch %a" cont c
500500
| Cond (x, cont1, cont2) ->
501501
Format.fprintf f "if %a then %a else %a" Var.print x cont cont1 cont cont2
502-
| Switch (x, a1, a2) ->
502+
| Switch (x, a1) ->
503503
Format.fprintf f "switch %a {" Var.print x;
504504
Array.iteri a1 ~f:(fun i c -> Format.fprintf f "int %d -> %a; " i cont c);
505-
Array.iteri a2 ~f:(fun i c -> Format.fprintf f "tag %d -> %a; " i cont c);
506505
Format.fprintf f "}"
507506
| Pushtrap (cont1, x, cont2, pcs) ->
508507
Format.fprintf
@@ -598,9 +597,8 @@ let fold_children blocks pc f accu =
598597
let accu = f pc1 accu in
599598
let accu = f pc2 accu in
600599
accu
601-
| Switch (_, a1, a2) ->
600+
| Switch (_, a1) ->
602601
let accu = Array.fold_right ~init:accu ~f:(fun (pc, _) accu -> f pc accu) a1 in
603-
let accu = Array.fold_right ~init:accu ~f:(fun (pc, _) accu -> f pc accu) a2 in
604602
accu
605603

606604
type 'c fold_blocs = block Addr.Map.t -> Addr.t -> (Addr.t -> 'c -> 'c) -> 'c -> 'c
@@ -726,9 +724,7 @@ let invariant { blocks; start; _ } =
726724
| Cond (_x, cont1, cont2) ->
727725
check_cont cont1;
728726
check_cont cont2
729-
| Switch (_x, a1, a2) ->
730-
Array.iteri a1 ~f:(fun _ cont -> check_cont cont);
731-
Array.iteri a2 ~f:(fun _ cont -> check_cont cont)
727+
| Switch (_x, a1) -> Array.iteri a1 ~f:(fun _ cont -> check_cont cont)
732728
| Pushtrap (cont1, _x, cont2, _pcs) ->
733729
check_cont cont1;
734730
check_cont cont2

compiler/lib/code.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ type last =
201201
| Stop
202202
| Branch of cont
203203
| Cond of Var.t * cont * cont
204-
| Switch of Var.t * cont array * cont array
204+
| Switch of Var.t * cont array
205205
| Pushtrap of cont * Var.t * cont * Addr.Set.t
206206
| Poptrap of cont
207207

compiler/lib/deadcode.ml

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,9 @@ and mark_reachable st pc =
9797
mark_var st x;
9898
mark_cont_reachable st cont1;
9999
mark_cont_reachable st cont2
100-
| Switch (x, a1, a2) ->
100+
| Switch (x, a1) ->
101101
mark_var st x;
102-
Array.iter a1 ~f:(fun cont -> mark_cont_reachable st cont);
103-
Array.iter a2 ~f:(fun cont -> mark_cont_reachable st cont)
102+
Array.iter a1 ~f:(fun cont -> mark_cont_reachable st cont)
104103
| Pushtrap (cont1, _, cont2, _) ->
105104
mark_cont_reachable st cont1;
106105
mark_cont_reachable st cont2)
@@ -136,11 +135,8 @@ let filter_live_last blocks st (l, loc) =
136135
| Branch cont -> Branch (filter_cont blocks st cont)
137136
| Cond (x, cont1, cont2) ->
138137
Cond (x, filter_cont blocks st cont1, filter_cont blocks st cont2)
139-
| Switch (x, a1, a2) ->
140-
Switch
141-
( x
142-
, Array.map a1 ~f:(fun cont -> filter_cont blocks st cont)
143-
, Array.map a2 ~f:(fun cont -> filter_cont blocks st cont) )
138+
| Switch (x, a1) ->
139+
Switch (x, Array.map a1 ~f:(fun cont -> filter_cont blocks st cont))
144140
| Pushtrap (cont1, x, cont2, pcs) ->
145141
Pushtrap
146142
( filter_cont blocks st cont1
@@ -204,9 +200,7 @@ let f ({ blocks; _ } as p : Code.program) =
204200
| Cond (_, cont1, cont2) ->
205201
add_cont_dep blocks defs cont1;
206202
add_cont_dep blocks defs cont2
207-
| Switch (_, a1, a2) ->
208-
Array.iter a1 ~f:(fun cont -> add_cont_dep blocks defs cont);
209-
Array.iter a2 ~f:(fun cont -> add_cont_dep blocks defs cont)
203+
| Switch (_, a1) -> Array.iter a1 ~f:(fun cont -> add_cont_dep blocks defs cont)
210204
| Pushtrap (cont, _, cont_h, _) ->
211205
add_cont_dep blocks defs cont_h;
212206
add_cont_dep blocks defs cont

compiler/lib/effects.ml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -418,13 +418,11 @@ let cps_last ~st ~alloc_jump_closures pc ((last, last_loc) : last * loc) ~k :
418418
, cps_jump_cont ~st ~src:pc cont1 last_loc
419419
, cps_jump_cont ~st ~src:pc cont2 last_loc )
420420
, last_loc ) )
421-
| Switch (x, c1, c2) ->
421+
| Switch (x, c1) ->
422422
(* To avoid code duplication during JavaScript generation, we need
423423
to create a single block per continuation *)
424424
let cps_jump_cont = Fun.memoize (fun x -> cps_jump_cont ~st ~src:pc x last_loc) in
425-
( alloc_jump_closures
426-
, ( Switch (x, Array.map c1 ~f:cps_jump_cont, Array.map c2 ~f:cps_jump_cont)
427-
, last_loc ) )
425+
alloc_jump_closures, (Switch (x, Array.map c1 ~f:cps_jump_cont), last_loc)
428426
| Pushtrap (body_cont, exn, ((handler_pc, _) as handler_cont), _) -> (
429427
assert (Hashtbl.mem st.is_continuation handler_pc);
430428
match Addr.Set.mem handler_pc st.blocks_to_transform with
@@ -911,8 +909,7 @@ let remove_empty_blocks ~live_vars (p : Code.program) : Code.program =
911909
match branch with
912910
| Branch cont -> Branch (resolve cont)
913911
| Cond (x, cont1, cont2) -> Cond (x, resolve cont1, resolve cont2)
914-
| Switch (x, a1, a2) ->
915-
Switch (x, Array.map ~f:resolve a1, Array.map ~f:resolve a2)
912+
| Switch (x, a1) -> Switch (x, Array.map ~f:resolve a1)
916913
| Pushtrap (cont1, x, cont2, s) ->
917914
Pushtrap (resolve cont1, x, resolve cont2, s)
918915
| Poptrap cont -> Poptrap (resolve cont)

compiler/lib/eval.ml

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,46 @@ let is_int info x =
189189
| Pc (Int _) -> Y
190190
| Pc _ -> N
191191

192+
let the_tag_of info x get =
193+
match x with
194+
| Pv x ->
195+
get_approx
196+
info
197+
(fun x ->
198+
match info.info_defs.(Var.idx x) with
199+
| Expr (Block (j, _, _)) ->
200+
if Var.ISet.mem info.info_possibly_mutable x then None else get j
201+
| Expr (Constant (Tuple (j, _, _))) -> get j
202+
| _ -> None)
203+
None
204+
(fun u v ->
205+
match u, v with
206+
| Some i, Some j when Poly.(i = j) -> u
207+
| _ -> None)
208+
x
209+
| Pc (Tuple (j, _, _)) -> get j
210+
| _ -> None
211+
212+
let the_cont_of info x (a : cont array) =
213+
(* The value of [x] might be meaningless when we're inside a dead code.
214+
The proper fix would be to remove the deadcode entirely.
215+
Meanwhile, add guards to prevent Invalid_argument("index out of bounds")
216+
see https://github.com/ocsigen/js_of_ocaml/issues/485 *)
217+
let get i = if i >= 0 && i < Array.length a then Some a.(i) else None in
218+
get_approx
219+
info
220+
(fun x ->
221+
match info.info_defs.(Var.idx x) with
222+
| Expr (Prim (Extern "%direct_obj_tag", [ b ])) -> the_tag_of info b get
223+
| Expr (Constant (Int j)) -> get (Int32.to_int j)
224+
| _ -> None)
225+
None
226+
(fun u v ->
227+
match u, v with
228+
| Some i, Some j when Poly.(i = j) -> u
229+
| _ -> None)
230+
x
231+
192232
let eval_instr info ((x, loc) as i) =
193233
match x with
194234
| Let (x, Prim (Extern ("caml_js_equals" | "caml_equal"), [ y; z ])) -> (
@@ -228,6 +268,13 @@ let eval_instr info ((x, loc) as i) =
228268
let c = Constant (Int b) in
229269
Flow.update_def info x c;
230270
[ Let (x, c), loc ])
271+
| Let (x, Prim (Extern "%direct_obj_tag", [ y ])) -> (
272+
match the_tag_of info y (fun x -> Some x) with
273+
| Some tag ->
274+
let c = Constant (Int (Int32.of_int tag)) in
275+
Flow.update_def info x c;
276+
[ Let (x, c), loc ]
277+
| None -> [ i ])
231278
| Let (x, Prim (Extern "caml_sys_const_backend_type", [ _ ])) ->
232279
let jsoo = Code.Var.fresh () in
233280
[ Let (jsoo, Constant (String "js_of_ocaml")), noloc
@@ -271,34 +318,6 @@ let eval_instr info ((x, loc) as i) =
271318
])
272319
| _ -> [ i ]
273320

274-
type case_of =
275-
| CConst of int
276-
| CTag of int
277-
| Unknown
278-
279-
let the_case_of info x =
280-
match x with
281-
| Pv x ->
282-
get_approx
283-
info
284-
(fun x ->
285-
match info.info_defs.(Var.idx x) with
286-
| Expr (Constant (Int i)) -> CConst (Int32.to_int i)
287-
| Expr (Block (j, _, _)) ->
288-
if Var.ISet.mem info.info_possibly_mutable x then Unknown else CTag j
289-
| Expr (Constant (Tuple (j, _, _))) -> CTag j
290-
| _ -> Unknown)
291-
Unknown
292-
(fun u v ->
293-
match u, v with
294-
| CTag i, CTag j when i = j -> u
295-
| CConst i, CConst j when i = j -> u
296-
| _ -> Unknown)
297-
x
298-
| Pc (Int i) -> CConst (Int32.to_int i)
299-
| Pc (Tuple (j, _, _)) -> CTag j
300-
| _ -> Unknown
301-
302321
type cond_of =
303322
| Zero
304323
| Non_zero
@@ -341,15 +360,10 @@ let eval_branch info (l, loc) =
341360
| Zero -> Branch ffalse
342361
| Non_zero -> Branch ftrue
343362
| Unknown -> b)
344-
| Switch (x, const, tags) as b -> (
345-
(* [the_case_of info (Pv x)] might be meaningless when we're inside a dead code.
346-
The proper fix would be to remove the deadcode entirely.
347-
Meanwhile, add guards to prevent Invalid_argument("index out of bounds")
348-
see https://github.com/ocsigen/js_of_ocaml/issues/485 *)
349-
match the_case_of info (Pv x) with
350-
| CConst j when j >= 0 && j < Array.length const -> Branch const.(j)
351-
| CTag j when j >= 0 && j < Array.length tags -> Branch tags.(j)
352-
| CConst _ | CTag _ | Unknown -> b)
363+
| Switch (x, a) as b -> (
364+
match the_cont_of info x a with
365+
| Some cont -> Branch cont
366+
| None -> b)
353367
| _ as b -> b
354368
in
355369
l, loc
@@ -380,15 +394,11 @@ let rec do_not_raise pc visited blocks =
380394
let visited = do_not_raise pc1 visited blocks in
381395
let visited = do_not_raise pc2 visited blocks in
382396
visited
383-
| Switch (_, a1, a2) ->
397+
| Switch (_, a1) ->
384398
let visited =
385399
Array.fold_left a1 ~init:visited ~f:(fun visited (pc, _) ->
386400
do_not_raise pc visited blocks)
387401
in
388-
let visited =
389-
Array.fold_left a2 ~init:visited ~f:(fun visited (pc, _) ->
390-
do_not_raise pc visited blocks)
391-
in
392402
visited
393403
| Pushtrap _ -> raise May_raise
394404

compiler/lib/flow.ml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,8 @@ let program_deps { blocks; _ } =
119119
| Cond (_, cont1, cont2) ->
120120
cont_deps blocks vars deps defs cont1;
121121
cont_deps blocks vars deps defs cont2
122-
| Switch (_, a1, a2) ->
123-
Array.iter a1 ~f:(fun cont -> cont_deps blocks vars deps defs cont);
124-
Array.iter a2 ~f:(fun cont -> cont_deps blocks vars deps defs cont)
122+
| Switch (_, a1) ->
123+
Array.iter a1 ~f:(fun cont -> cont_deps blocks vars deps defs cont)
125124
| Pushtrap (cont, x, cont_h, _) ->
126125
add_param_def vars defs x;
127126
cont_deps blocks vars deps defs cont_h;

compiler/lib/freevars.ml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,9 @@ let iter_last_free_var f l =
6464
f x;
6565
iter_cont_free_vars f cont1;
6666
iter_cont_free_vars f cont2
67-
| Switch (x, a1, a2) ->
67+
| Switch (x, a1) ->
6868
f x;
69-
Array.iter a1 ~f:(fun c -> iter_cont_free_vars f c);
70-
Array.iter a2 ~f:(fun c -> iter_cont_free_vars f c)
69+
Array.iter a1 ~f:(fun c -> iter_cont_free_vars f c)
7170
| Pushtrap (cont1, _, cont2, _) ->
7271
iter_cont_free_vars f cont1;
7372
iter_cont_free_vars f cont2

compiler/lib/generate.ml

Lines changed: 5 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,7 @@ let _ =
911911
register_un_prim_ctx "%caml_format_int_special" `Pure (fun ctx cx loc ->
912912
let s = J.EBin (J.Plus, str_js_utf8 "", cx) in
913913
ocaml_string ~ctx ~loc s);
914+
register_un_prim "%direct_obj_tag" `Mutator (fun cx _loc -> Mlvalue.Block.tag cx);
914915
register_bin_prim "caml_array_unsafe_get" `Mutable (fun cx cy _ ->
915916
Mlvalue.Array.field cx cy);
916917
register_bin_prim "%int_add" `Pure (fun cx cy _ -> to_int (plus_int cx cy));
@@ -1440,15 +1441,10 @@ and compile_block_no_loop st queue (pc : Addr.t) ~fall_through scope_stack =
14401441
let seq, queue = translate_instrs st.ctx queue block.body block.branch in
14411442
let nbbranch =
14421443
match fst block.branch with
1443-
| Switch (_, a, b) ->
1444+
| Switch (_, a) ->
14441445
(* Build an artifical dtree with the correct layout so that
14451446
[Dtree.nbbranch dtree pc] is correct *)
1446-
let dtree =
1447-
match a, b with
1448-
| [||], _ -> DTree.build_switch b
1449-
| _, [||] -> DTree.build_switch a
1450-
| _ -> DTree.If (IsTrue, DTree.build_switch a, DTree.build_switch b)
1451-
in
1447+
let dtree = DTree.build_switch a in
14521448
fun pc -> DTree.nbbranch dtree pc
14531449
| Cond (_, a, b) ->
14541450
let dtree = DTree.build_if a b in
@@ -1583,7 +1579,7 @@ and compile_conditional st queue ~fall_through last scope_stack : _ * _ =
15831579
| Raise _ -> Format.eprintf "raise;@;"
15841580
| Stop -> Format.eprintf "stop;@;"
15851581
| Cond (x, _, _) -> Format.eprintf "@[<hv 2>cond(%a){@;" Code.Var.print x
1586-
| Switch (x, _, _) -> Format.eprintf "@[<hv 2>switch(%a){@;" Code.Var.print x);
1582+
| Switch (x, _) -> Format.eprintf "@[<hv 2>switch(%a){@;" Code.Var.print x);
15871583
let loc = source_location st.ctx pc in
15881584
let res =
15891585
match last with
@@ -1642,20 +1638,7 @@ and compile_conditional st queue ~fall_through last scope_stack : _ * _ =
16421638
(DTree.build_if c1 c2)
16431639
in
16441640
never, flush_all queue b
1645-
| Switch (x, [||], a2) ->
1646-
let (_px, cx), queue = access_queue queue x in
1647-
let never, code =
1648-
compile_decision_tree
1649-
"Tag"
1650-
st
1651-
scope_stack
1652-
~fall_through
1653-
loc
1654-
(Mlvalue.Block.tag cx)
1655-
(DTree.build_switch a2)
1656-
in
1657-
never, flush_all queue code
1658-
| Switch (x, a1, [||]) ->
1641+
| Switch (x, a1) ->
16591642
let (_px, cx), queue = access_queue queue x in
16601643
let never, code =
16611644
compile_decision_tree
@@ -1668,39 +1651,6 @@ and compile_conditional st queue ~fall_through last scope_stack : _ * _ =
16681651
(DTree.build_switch a1)
16691652
in
16701653
never, flush_all queue code
1671-
| Switch (x, a1, a2) ->
1672-
(* The variable x is accessed several times, so we can directly
1673-
refer to it *)
1674-
let never1, b1 =
1675-
compile_decision_tree
1676-
"Int"
1677-
st
1678-
scope_stack
1679-
~fall_through
1680-
loc
1681-
(var x)
1682-
(DTree.build_switch a1)
1683-
in
1684-
let never2, b2 =
1685-
compile_decision_tree
1686-
"Tag"
1687-
st
1688-
scope_stack
1689-
~fall_through
1690-
loc
1691-
(Mlvalue.Block.tag (var x))
1692-
(DTree.build_switch a2)
1693-
in
1694-
let code =
1695-
Js_simpl.if_statement
1696-
(Mlvalue.is_immediate (var x))
1697-
loc
1698-
(Js_simpl.block b1)
1699-
never1
1700-
(Js_simpl.block b2)
1701-
never2
1702-
in
1703-
never1 && never2, flush_all queue code
17041654
in
17051655
(if debug ()
17061656
then

0 commit comments

Comments
 (0)