Skip to content

Commit 1a86c20

Browse files
committed
Compiler: exit loop early
1 parent 53bc41c commit 1a86c20

File tree

6 files changed

+435
-315
lines changed

6 files changed

+435
-315
lines changed

compiler/lib/generate.ml

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,54 @@ let build_graph ctx pc =
729729
in
730730
loop pc Addr.Set.empty [];
731731
Hashtbl.add preds pc 1;
732+
let () =
733+
(* Create an artificial frontier when for-loops are not longer necessary *)
734+
let loopback = Hashtbl.create 17 in
735+
let rec loop pc loop_headers =
736+
match Hashtbl.find loopback pc with
737+
| x -> x
738+
| exception Not_found ->
739+
let loop_headers =
740+
if Addr.Set.mem pc !loops then Addr.Set.add pc loop_headers else loop_headers
741+
in
742+
let backs = Hashtbl.find backs pc in
743+
let all_backs =
744+
List.fold_left (Hashtbl.find succs pc) ~init:backs ~f:(fun acc pc ->
745+
Addr.Set.union acc (loop pc loop_headers))
746+
|> Addr.Set.inter loop_headers
747+
in
748+
Hashtbl.replace loopback pc all_backs;
749+
all_backs
750+
in
751+
ignore (loop pc Addr.Set.empty);
752+
let compute_exit_loop pc_loop =
753+
let visited = Hashtbl.create 18 in
754+
let rec find pc_loops pc acc =
755+
if Hashtbl.mem visited pc
756+
then acc
757+
else if Addr.Set.cardinal (Addr.Set.inter pc_loops (Hashtbl.find loopback pc)) = 0
758+
then Addr.Set.add pc acc
759+
else
760+
let pc_loops =
761+
if Addr.Set.mem pc !loops then Addr.Set.add pc pc_loops else pc_loops
762+
in
763+
let block = Addr.Map.find pc blocks in
764+
let succs =
765+
match block.branch with
766+
| Pushtrap ((pc1, _), _, (pc_exn, _), _) ->
767+
Addr.Set.add pc_exn (Hashtbl.find poptrap pc1) |> Addr.Set.elements
768+
| _ -> Hashtbl.find succs pc
769+
in
770+
List.fold_left succs ~init:acc ~f:(fun acc pc' -> find pc_loops pc' acc)
771+
in
772+
find (Addr.Set.singleton pc_loop) pc_loop Addr.Set.empty
773+
in
774+
Addr.Set.iter
775+
(fun pc_loop ->
776+
let set = compute_exit_loop pc_loop in
777+
add_cf_frontier pc_loop set)
778+
!loops
779+
in
732780
let () =
733781
(* Create an artificial frontier when we pop an exception handler *)
734782
let rec keep_front pc =
@@ -1417,6 +1465,10 @@ and compile_block st queue (pc : Addr.t) loop_stack frontier interm =
14171465
| false -> compile_block_no_loop st queue pc loop_stack frontier interm
14181466
| true ->
14191467
if debug () then Format.eprintf "@[<hv 2>for(;;) {@,";
1468+
let grey = dominance_frontier st pc in
1469+
let exit_prefix, exit_cont, exit_interm, merge_node =
1470+
colapse_frontier "for-loop" st grey interm
1471+
in
14201472
let never_body, body =
14211473
let lab =
14221474
match loop_stack with
@@ -1426,7 +1478,13 @@ and compile_block st queue (pc : Addr.t) loop_stack frontier interm =
14261478
let lab_used = ref false in
14271479
let loop_stack = (pc, (lab, lab_used)) :: loop_stack in
14281480
let never_body, body =
1429-
compile_block_no_loop st queue pc loop_stack frontier interm
1481+
compile_block_no_loop
1482+
st
1483+
queue
1484+
pc
1485+
loop_stack
1486+
(Addr.Set.union frontier exit_cont)
1487+
exit_interm
14301488
in
14311489
let body =
14321490
let rec remove_tailing_continue acc = function
@@ -1459,7 +1517,10 @@ and compile_block st queue (pc : Addr.t) loop_stack frontier interm =
14591517
in
14601518
never_body, [ for_loop ]
14611519
in
1462-
never_body, body
1520+
let never_after, after =
1521+
compile_merge_node st exit_cont loop_stack frontier interm merge_node
1522+
in
1523+
never_body || never_after, exit_prefix @ body @ after
14631524

14641525
(* Compile block. Loops have already been handled. *)
14651526
and compile_block_no_loop st queue (pc : Addr.t) loop_stack frontier interm =

0 commit comments

Comments
 (0)