2020
2121(* XXX
2222 Patterns:
23- => loops should avoid absorbing the whole continuation...
24- (detect when the continuation does not loop anymore and close
25- the loop at this point)
2623 => should have special code for switches that include the preceding
2724 if statement when possible
2825 => if e1 then {if e2 then P else Q} else {if e3 then P else Q}
@@ -741,6 +738,7 @@ let dominance_frontier st pc =
741738 | Old -> dominance_frontier_ref_implem st pc
742739 | New -> dominance_frontier_opt st pc
743740 in
741+ if debug () then Format. eprintf " Dominance(%d): %s@." pc (string_of_set frontier);
744742 (if debug_dominance_frontier ()
745743 then
746744 let o, n =
@@ -1357,6 +1355,49 @@ and translate_instrs ctx expr_queue loc instr =
13571355 let instrs, expr_queue = translate_instrs ctx expr_queue loc rem in
13581356 st @ instrs, expr_queue
13591357
1358+ and compute_exit_loop st pc_loop =
1359+ let cache = Hashtbl. create 17 in
1360+ let rec loop pc =
1361+ match Hashtbl. find cache pc with
1362+ | x -> x
1363+ | exception Not_found ->
1364+ let succs = Hashtbl. find st.succs pc in
1365+ let backs = Hashtbl. find st.backs pc in
1366+ let all_backs =
1367+ List. fold_left succs ~init: backs ~f: (fun acc pc -> Addr.Set. union acc (loop pc))
1368+ in
1369+ Hashtbl. replace cache pc all_backs;
1370+ all_backs
1371+ in
1372+ ignore (loop pc_loop);
1373+ let visited = Hashtbl. create 18 in
1374+ let rec find pc_loops exn_handler pc acc =
1375+ if Hashtbl. mem visited pc
1376+ then acc
1377+ else
1378+ let () = Hashtbl. add visited pc () in
1379+ let succs = Hashtbl. find st.succs pc in
1380+ let backs = Hashtbl. find cache pc in
1381+ if Addr.Set. cardinal (Addr.Set. inter pc_loops backs) > 0
1382+ || not (List. is_empty exn_handler)
1383+ then
1384+ let pc_loops =
1385+ if Addr.Set. mem pc st.loops then Addr.Set. add pc pc_loops else pc_loops
1386+ in
1387+ let block = Addr.Map. find pc st.blocks in
1388+ List. fold_left succs ~init: acc ~f: (fun acc pc' ->
1389+ let exn_handler =
1390+ match block.branch with
1391+ | Pushtrap (_ , _ , (pc_exn , _ ), _ ) when pc' <> pc_exn ->
1392+ block.branch :: exn_handler
1393+ | Poptrap _ -> List. tl exn_handler
1394+ | _ -> exn_handler
1395+ in
1396+ find pc_loops exn_handler pc' acc)
1397+ else Addr.Set. add pc acc
1398+ in
1399+ find (Addr.Set. singleton pc_loop) [] pc_loop Addr.Set. empty
1400+
13601401and compile_block st queue (pc : Addr.t ) frontier interm =
13611402 if (not (List. is_empty queue))
13621403 && (Addr.Set. mem pc st.loops || not (Config.Flag. inline () ))
@@ -1374,7 +1415,65 @@ and compile_block st queue (pc : Addr.t) frontier interm =
13741415 | [] -> J.Label. zero
13751416 in
13761417 st.loop_stack < - (pc, (lab, ref false )) :: st.loop_stack;
1377- let never_body, body = compile_block_no_loop st queue pc frontier interm in
1418+ let exit_grey = compute_exit_loop st pc in
1419+ let size_of the_pc =
1420+ let fold _blocs pc f acc =
1421+ let succs = Hashtbl. find st.succs pc in
1422+ List. fold_left succs ~init: acc ~f: (fun acc pc -> f pc acc)
1423+ in
1424+ Code. traverse
1425+ { fold }
1426+ (fun pc i ->
1427+ let b = Addr.Map. find pc st.blocks in
1428+ (* prefer to not nest for loops *)
1429+ let i = if Addr.Set. mem pc st.loops then i + 10000 else i in
1430+ let i =
1431+ if Addr.Set. is_empty (Hashtbl. find st.backs pc) then i else i + 10000
1432+ in
1433+ let i =
1434+ match b.branch with
1435+ | Cond _ | Switch _ -> i + 10
1436+ | Branch _ -> i
1437+ | Stop | Raise _ | Return _ | Poptrap _ -> i
1438+ | Pushtrap _ -> i + 10
1439+ in
1440+ i + List. length b.body)
1441+ the_pc
1442+ st.blocks
1443+ 0
1444+ in
1445+ let list_max f = function
1446+ | [] -> None
1447+ | x :: xs ->
1448+ Some
1449+ (List. fold_left
1450+ ~init: (f x, x)
1451+ xs
1452+ ~f: (fun (v , data ) x ->
1453+ let y = f x in
1454+ if y > x then y, x else v, data))
1455+ in
1456+ let exit_grey =
1457+ Addr.Set. elements exit_grey
1458+ |> List. partition ~f: (fun pc -> Addr.Set. is_empty (dominance_frontier st pc))
1459+ |> function
1460+ | _ , [ other ] -> Addr.Set. singleton other
1461+ | l , _ -> (
1462+ match list_max (fun x -> size_of x) l with
1463+ | None -> Addr.Set. empty
1464+ | Some (size , pc ) ->
1465+ if size < 50 then Addr.Set. empty else Addr.Set. singleton pc)
1466+ in
1467+ let exit_frontier = resolve_nodes interm exit_grey in
1468+ let exit_prefix, exit_cont, exit_interm =
1469+ colapse_frontier " for-loop" st exit_frontier interm
1470+ in
1471+ assert (Addr.Set. cardinal exit_cont < = 1 );
1472+ let frontier_inner = Addr.Set. union frontier exit_cont in
1473+ Addr.Set. iter (protect_preds st) exit_frontier;
1474+ let never_body, body =
1475+ compile_block_no_loop st queue pc frontier_inner exit_interm
1476+ in
13781477 let body =
13791478 let rec remove_tailing_continue acc = function
13801479 | [] -> body
@@ -1383,6 +1482,7 @@ and compile_block st queue (pc : Addr.t) frontier interm =
13831482 in
13841483 remove_tailing_continue [] body
13851484 in
1485+ Addr.Set. iter (unprotect_preds st) exit_frontier;
13861486 let for_loop =
13871487 ( J. For_statement
13881488 ( J. Left None
@@ -1405,9 +1505,20 @@ and compile_block st queue (pc : Addr.t) frontier interm =
14051505 if ! used then Some l else None
14061506 | [] -> assert false
14071507 in
1408- match label with
1409- | None -> never_body, [ for_loop ]
1410- | Some label -> never_body, [ J. Labelled_statement (label, for_loop), J. N ])
1508+ let for_loop =
1509+ match label with
1510+ | None -> for_loop
1511+ | Some label -> J. Labelled_statement (label, for_loop), J. N
1512+ in
1513+ let for_loop = exit_prefix @ [ for_loop ] in
1514+ match Addr.Set. choose exit_cont with
1515+ | exception Not_found -> never_body, for_loop
1516+ | pc ->
1517+ if Addr.Set. mem pc frontier
1518+ then never_body, for_loop
1519+ else
1520+ let never_after, after = compile_block st [] pc frontier interm in
1521+ never_body || never_after, for_loop @ after)
14111522
14121523and compile_block_no_loop st queue (pc : Addr.t ) frontier interm =
14131524 if pc > = 0
@@ -1448,23 +1559,23 @@ and compile_block_no_loop st queue (pc : Addr.t) frontier interm =
14481559 let pc3s = Addr.Set. filter (fun pc -> Hashtbl. mem st.succs pc) pc3s in
14491560 (* no need to limit body for simple flow with no
14501561 instruction. eg return and branch *)
1451- let rec limit pc =
1452- if Addr.Set. mem pc exn_frontier
1562+ let rec keep_frontier st frontier pc =
1563+ if Addr.Set. mem pc frontier
14531564 then false
14541565 else
14551566 match Addr.Map. find pc st.blocks with
14561567 | { body = [] ; branch = Return _ ; _ } -> false
1457- | { body = [] ; branch = Branch (pc' , _ ); _ } -> limit pc'
1568+ | { body = [] ; branch = Branch (pc' , _ ); _ } -> keep_frontier st frontier pc'
14581569 | _ -> true
14591570 in
1460- let handler_frontier = Addr.Set. filter limit pc3s in
1571+ let handler_frontier = Addr.Set. filter (keep_frontier st exn_frontier) pc3s in
14611572 (* TODO: Check that we are inside the [frontier/new_frontier] *)
14621573 let handler_frontier =
14631574 resolve_nodes interm (Addr.Set. union exn_frontier handler_frontier)
14641575 in
14651576 Addr.Set. iter (incr_preds st) handler_frontier;
14661577 let prefix, handler_frontier_cont, handler_interm =
1467- colapse_frontier st handler_frontier interm
1578+ colapse_frontier " try-catch " st handler_frontier interm
14681579 in
14691580 assert (Addr.Set. cardinal handler_frontier_cont < = 1 );
14701581 let try_catch_frontier = Addr.Set. union new_frontier handler_frontier_cont in
@@ -1550,7 +1661,9 @@ and compile_block_no_loop st queue (pc : Addr.t) frontier interm =
15501661 , source_location st.ctx pc )
15511662 :: after) )
15521663 | _ ->
1553- let prefix, frontier_cont, new_interm = colapse_frontier st new_frontier interm in
1664+ let prefix, frontier_cont, new_interm =
1665+ colapse_frontier " default" st new_frontier interm
1666+ in
15541667 assert (Addr.Set. cardinal frontier_cont < = 1 );
15551668 List. iter succs ~f: (fun (pc , _ ) ->
15561669 if Addr.Map. mem pc new_interm then decr_preds st pc);
@@ -1568,13 +1681,14 @@ and compile_block_no_loop st queue (pc : Addr.t) frontier interm =
15681681 in
15691682 never_cond || never_after, seq @ prefix @ cond @ after
15701683
1571- and colapse_frontier st new_frontier interm =
1684+ and colapse_frontier name st new_frontier interm =
15721685 if Addr.Set. cardinal new_frontier > 1
15731686 then (
15741687 if debug ()
15751688 then
15761689 Format. eprintf
1577- " colapse frontier into %d: %s@."
1690+ " colapse frontier (%s) into %d: %s@."
1691+ name
15781692 st.interm_idx
15791693 (string_of_set new_frontier);
15801694 let x = Code.Var. fresh_n " switch" in
@@ -1605,11 +1719,14 @@ and colapse_frontier st new_frontier interm =
16051719 Addr.Set. iter (fun pc -> protect_preds st pc) new_frontier;
16061720 Hashtbl. add st.succs idx (Addr.Set. elements new_frontier);
16071721 Hashtbl. add st.backs idx Addr.Set. empty;
1722+ let interm =
1723+ List. fold_right pc_i ~init: interm ~f: (fun (pc , i ) interm ->
1724+ Addr.Map. add pc (idx, (x, i, default = i)) interm)
1725+ in
16081726 (* The [dominance_frontier_cache] is invalidated by [incr_preds] and [protect_preds] *)
16091727 ( [ J. Variable_statement [ J. V x, Some (int default, J. N ) ], J. N ]
16101728 , Addr.Set. singleton idx
1611- , List. fold_right pc_i ~init: interm ~f: (fun (pc , i ) interm ->
1612- Addr.Map. add pc (idx, (x, i, default = i)) interm) ))
1729+ , interm ))
16131730 else [] , new_frontier, interm
16141731
16151732and compile_decision_tree st backs frontier interm loc cx dtree =
0 commit comments