Skip to content

Commit b2168d6

Browse files
vouillonhhugo
authored andcommitted
Reference unboxing
1 parent 3d05b15 commit b2168d6

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

compiler/lib/driver.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ let round ~first : 'a -> 'a =
167167
+> flow
168168
+> specialize
169169
+> eval
170+
+> Ref_unboxing.f
170171
+> inline
171172
+> deadcode
172173

compiler/lib/phisimpl.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ let program_deps { blocks; _ } =
6969
(fun _pc block ->
7070
List.iter block.body ~f:(fun i ->
7171
match i with
72+
| Let (x, Prim (Extern "%identity", [ Pv y ])) ->
73+
add_var vars x;
74+
add_dep deps x y;
75+
add_def vars defs x y
7276
| Let (x, e) ->
7377
add_var vars x;
7478
expr_deps blocks vars deps defs x e

compiler/lib/ref_unboxing.ml

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
open! Stdlib
2+
open Code
3+
4+
(*
5+
ocamlc does not perform reference unboxing when emitting debugging
6+
information. Inlining can enable additional reference unboxing.
7+
8+
TODO: handle assignment in handler
9+
*)
10+
11+
let debug = Debug.find "unbox-refs"
12+
13+
let times = Debug.find "times"
14+
15+
let stats = Debug.find "stats"
16+
17+
let rewrite refs block m =
18+
let m, l =
19+
List.fold_left
20+
~f:(fun (m, rem) i ->
21+
match i with
22+
| Let (x, Block (0, [| y |], (NotArray | Unknown), Maybe_mutable))
23+
when Var.Set.mem x refs -> Var.Map.add x y m, rem
24+
| Let (y, Field (x, 0, Non_float)) when Var.Map.mem x m ->
25+
m, Let (y, Prim (Extern "%identity", [ Pv (Var.Map.find x m) ])) :: rem
26+
| Offset_ref (x, n) when Var.Map.mem x m ->
27+
let y = Var.fresh () in
28+
( Var.Map.add x y m
29+
, Let
30+
( y
31+
, Prim
32+
( Extern "%int_add"
33+
, [ Pv (Var.Map.find x m); Pc (Int (Targetint.of_int_exn n)) ] ) )
34+
:: rem )
35+
| Set_field (x, _, Non_float, y) when Var.Map.mem x m -> Var.Map.add x y m, rem
36+
| _ -> m, i :: rem)
37+
block.body
38+
~init:(m, [])
39+
in
40+
m, List.rev l
41+
42+
let rewrite_cont relevant_vars vars (pc', args) =
43+
let refs, _ = Hashtbl.find relevant_vars pc' in
44+
let vars = Var.Map.filter (fun x _ -> Var.Set.mem x refs) vars in
45+
pc', List.map ~f:snd (Var.Map.bindings vars) @ args
46+
47+
let rewrite_function p variables pc =
48+
let relevant_vars = Hashtbl.create 16 in
49+
let g = Structure.(dominator_tree (build_graph p.blocks pc)) in
50+
let rec traverse_tree g pc vars =
51+
let block = Addr.Map.find pc p.blocks in
52+
let vars' =
53+
List.fold_left
54+
~f:(fun s i ->
55+
match i with
56+
| Let (x, Block (0, [| _ |], (NotArray | Unknown), Maybe_mutable))
57+
when Var.Hashtbl.mem variables x -> Var.Set.add x s
58+
| _ -> s)
59+
~init:vars
60+
block.body
61+
in
62+
Hashtbl.add relevant_vars pc (vars, vars');
63+
Addr.Set.iter (fun pc' -> traverse_tree g pc' vars') (Structure.get_edges g pc)
64+
in
65+
traverse_tree g pc Var.Set.empty;
66+
let rec traverse_tree' g pc blocks =
67+
let block = Addr.Map.find pc p.blocks in
68+
let vars, refs = Hashtbl.find relevant_vars pc in
69+
let vars =
70+
Var.Set.fold (fun x m -> Var.Map.add x (Var.fork x) m) vars Var.Map.empty
71+
in
72+
let params = List.map ~f:snd (Var.Map.bindings vars) @ block.params in
73+
let vars, body = rewrite refs block vars in
74+
let branch =
75+
match block.branch with
76+
| Return _ | Raise _ | Stop -> block.branch
77+
| Branch cont -> Branch (rewrite_cont relevant_vars vars cont)
78+
| Cond (x, cont, cont') ->
79+
Cond
80+
( x
81+
, rewrite_cont relevant_vars vars cont
82+
, rewrite_cont relevant_vars vars cont' )
83+
| Switch (x, a) ->
84+
Switch (x, Array.map ~f:(fun cont -> rewrite_cont relevant_vars vars cont) a)
85+
| Pushtrap (cont, x, cont') ->
86+
Pushtrap
87+
( rewrite_cont relevant_vars vars cont
88+
, x
89+
, rewrite_cont relevant_vars vars cont' )
90+
| Poptrap cont -> Poptrap (rewrite_cont relevant_vars vars cont)
91+
in
92+
let blocks = Addr.Map.add pc { params; body; branch } blocks in
93+
Addr.Set.fold
94+
(fun pc' blocks -> traverse_tree' g pc' blocks)
95+
(Structure.get_edges g pc)
96+
blocks
97+
in
98+
let blocks = traverse_tree' g pc p.blocks in
99+
{ p with blocks }
100+
101+
let f p =
102+
let t = Timer.make () in
103+
let candidates = Var.Hashtbl.create 128 in
104+
let updated = Var.Hashtbl.create 128 in
105+
let visited = BitSet.create' p.free_pc in
106+
let discard x = Var.Hashtbl.remove candidates x in
107+
let check_field_access depth x =
108+
match Var.Hashtbl.find candidates x with
109+
| exception Not_found -> false
110+
| depth' ->
111+
if depth' = depth
112+
then true
113+
else (
114+
Var.Hashtbl.remove candidates x;
115+
false)
116+
in
117+
let rec traverse depth start_pc pc =
118+
if not (BitSet.mem visited pc)
119+
then (
120+
BitSet.set visited pc;
121+
let block = Addr.Map.find pc p.blocks in
122+
List.iter
123+
~f:(fun i ->
124+
match i with
125+
| Let (x, Block (0, [| _ |], (NotArray | Unknown), Maybe_mutable)) ->
126+
Freevars.iter_instr_free_vars discard i;
127+
Var.Hashtbl.replace candidates x depth
128+
| Let (_, Closure (_, (pc', _), _)) -> traverse (depth + 1) pc' pc'
129+
| Let (_, Field (x, 0, Non_float)) -> ignore (check_field_access depth x)
130+
| Offset_ref (x, _) ->
131+
if check_field_access depth x then Var.Hashtbl.replace updated x start_pc
132+
| Set_field (x, _, Non_float, y) ->
133+
discard y;
134+
if check_field_access depth x then Var.Hashtbl.replace updated x start_pc
135+
| _ -> Freevars.iter_instr_free_vars discard i)
136+
block.body;
137+
Freevars.iter_last_free_var discard block.branch;
138+
match block.branch with
139+
| Pushtrap ((pc', _), _, (pc'', _)) ->
140+
traverse (depth + 1) start_pc pc';
141+
traverse depth start_pc pc''
142+
| Poptrap (pc', _) -> traverse (depth - 1) start_pc pc'
143+
| _ -> Code.fold_children p.blocks pc (fun pc' () -> traverse depth start_pc pc') ())
144+
in
145+
traverse 0 p.start p.start;
146+
if debug ()
147+
then
148+
Print.program
149+
Format.err_formatter
150+
(fun _ i ->
151+
match i with
152+
| Instr (Let (x, _))
153+
when Var.Hashtbl.mem candidates x && Var.Hashtbl.mem updated x -> "REF"
154+
| _ -> "")
155+
p;
156+
Var.Hashtbl.filter_map_inplace
157+
(fun x _depth -> try Some (Var.Hashtbl.find updated x) with Not_found -> None)
158+
candidates;
159+
let functions =
160+
Var.Hashtbl.fold (fun _ pc s -> Addr.Set.add pc s) candidates Addr.Set.empty
161+
in
162+
let p = Addr.Set.fold (fun pc p -> rewrite_function p candidates pc) functions p in
163+
if times () then Format.eprintf " reference unboxing: %a@." Timer.print t;
164+
if stats ()
165+
then Format.eprintf "Stats - reference unboxing: %d@." (Var.Hashtbl.length candidates);
166+
p

0 commit comments

Comments
 (0)