20
20
21
21
(* XXX
22
22
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)
26
23
=> should have special code for switches that include the preceding
27
24
if statement when possible
28
25
=> 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 =
741
738
| Old -> dominance_frontier_ref_implem st pc
742
739
| New -> dominance_frontier_opt st pc
743
740
in
741
+ if debug () then Format. eprintf " Dominance(%d): %s@." pc (string_of_set frontier);
744
742
(if debug_dominance_frontier ()
745
743
then
746
744
let o, n =
@@ -1357,6 +1355,49 @@ and translate_instrs ctx expr_queue loc instr =
1357
1355
let instrs, expr_queue = translate_instrs ctx expr_queue loc rem in
1358
1356
st @ instrs, expr_queue
1359
1357
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
+
1360
1401
and compile_block st queue (pc : Addr.t ) frontier interm =
1361
1402
if (not (List. is_empty queue))
1362
1403
&& (Addr.Set. mem pc st.loops || not (Config.Flag. inline () ))
@@ -1374,7 +1415,65 @@ and compile_block st queue (pc : Addr.t) frontier interm =
1374
1415
| [] -> J.Label. zero
1375
1416
in
1376
1417
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
1378
1477
let body =
1379
1478
let rec remove_tailing_continue acc = function
1380
1479
| [] -> body
@@ -1383,6 +1482,7 @@ and compile_block st queue (pc : Addr.t) frontier interm =
1383
1482
in
1384
1483
remove_tailing_continue [] body
1385
1484
in
1485
+ Addr.Set. iter (unprotect_preds st) exit_frontier;
1386
1486
let for_loop =
1387
1487
( J. For_statement
1388
1488
( J. Left None
@@ -1405,9 +1505,20 @@ and compile_block st queue (pc : Addr.t) frontier interm =
1405
1505
if ! used then Some l else None
1406
1506
| [] -> assert false
1407
1507
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)
1411
1522
1412
1523
and compile_block_no_loop st queue (pc : Addr.t ) frontier interm =
1413
1524
if pc > = 0
@@ -1448,23 +1559,23 @@ and compile_block_no_loop st queue (pc : Addr.t) frontier interm =
1448
1559
let pc3s = Addr.Set. filter (fun pc -> Hashtbl. mem st.succs pc) pc3s in
1449
1560
(* no need to limit body for simple flow with no
1450
1561
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
1453
1564
then false
1454
1565
else
1455
1566
match Addr.Map. find pc st.blocks with
1456
1567
| { body = [] ; branch = Return _ ; _ } -> false
1457
- | { body = [] ; branch = Branch (pc' , _ ); _ } -> limit pc'
1568
+ | { body = [] ; branch = Branch (pc' , _ ); _ } -> keep_frontier st frontier pc'
1458
1569
| _ -> true
1459
1570
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
1461
1572
(* TODO: Check that we are inside the [frontier/new_frontier] *)
1462
1573
let handler_frontier =
1463
1574
resolve_nodes interm (Addr.Set. union exn_frontier handler_frontier)
1464
1575
in
1465
1576
Addr.Set. iter (incr_preds st) handler_frontier;
1466
1577
let prefix, handler_frontier_cont, handler_interm =
1467
- colapse_frontier st handler_frontier interm
1578
+ colapse_frontier " try-catch " st handler_frontier interm
1468
1579
in
1469
1580
assert (Addr.Set. cardinal handler_frontier_cont < = 1 );
1470
1581
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 =
1550
1661
, source_location st.ctx pc )
1551
1662
:: after) )
1552
1663
| _ ->
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
1554
1667
assert (Addr.Set. cardinal frontier_cont < = 1 );
1555
1668
List. iter succs ~f: (fun (pc , _ ) ->
1556
1669
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 =
1568
1681
in
1569
1682
never_cond || never_after, seq @ prefix @ cond @ after
1570
1683
1571
- and colapse_frontier st new_frontier interm =
1684
+ and colapse_frontier name st new_frontier interm =
1572
1685
if Addr.Set. cardinal new_frontier > 1
1573
1686
then (
1574
1687
if debug ()
1575
1688
then
1576
1689
Format. eprintf
1577
- " colapse frontier into %d: %s@."
1690
+ " colapse frontier (%s) into %d: %s@."
1691
+ name
1578
1692
st.interm_idx
1579
1693
(string_of_set new_frontier);
1580
1694
let x = Code.Var. fresh_n " switch" in
@@ -1605,11 +1719,14 @@ and colapse_frontier st new_frontier interm =
1605
1719
Addr.Set. iter (fun pc -> protect_preds st pc) new_frontier;
1606
1720
Hashtbl. add st.succs idx (Addr.Set. elements new_frontier);
1607
1721
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
1608
1726
(* The [dominance_frontier_cache] is invalidated by [incr_preds] and [protect_preds] *)
1609
1727
( [ J. Variable_statement [ J. V x, Some (int default, J. N ) ], J. N ]
1610
1728
, 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 ))
1613
1730
else [] , new_frontier, interm
1614
1731
1615
1732
and compile_decision_tree st backs frontier interm loc cx dtree =
0 commit comments