Skip to content

Commit 7bfbab1

Browse files
committed
Copy files from ocaml/ by executing scripts/copy-ocaml-subset.sh
1 parent 7059376 commit 7bfbab1

File tree

358 files changed

+72778
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

358 files changed

+72778
-0
lines changed

backend/CSEgen.ml

+369
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
(**************************************************************************)
2+
(* *)
3+
(* OCaml *)
4+
(* *)
5+
(* Xavier Leroy, projet Gallium, INRIA Rocquencourt *)
6+
(* *)
7+
(* Copyright 2014 Institut National de Recherche en Informatique et *)
8+
(* en Automatique. *)
9+
(* *)
10+
(* All rights reserved. This file is distributed under the terms of *)
11+
(* the GNU Lesser General Public License version 2.1, with the *)
12+
(* special exception on linking described in the file LICENSE. *)
13+
(* *)
14+
(**************************************************************************)
15+
16+
(* Common subexpression elimination by value numbering over extended
17+
basic blocks. *)
18+
19+
open Mach
20+
21+
type valnum = int
22+
23+
(* Classification of operations *)
24+
25+
type op_class =
26+
| Op_pure (* pure arithmetic, produce one or several result *)
27+
| Op_checkbound (* checkbound-style: no result, can raise an exn *)
28+
| Op_load (* memory load *)
29+
| Op_store of bool (* memory store, false = init, true = assign *)
30+
| Op_other (* anything else that does not allocate nor store in memory *)
31+
32+
(* We maintain sets of equations of the form
33+
valnums = operation(valnums)
34+
plus a mapping from registers to valnums (value numbers). *)
35+
36+
type rhs = operation * valnum array
37+
38+
module Equations = struct
39+
module Rhs_map =
40+
Map.Make(struct type t = rhs let compare = Stdlib.compare end)
41+
42+
type 'a t =
43+
{ load_equations : 'a Rhs_map.t;
44+
other_equations : 'a Rhs_map.t }
45+
46+
let empty =
47+
{ load_equations = Rhs_map.empty;
48+
other_equations = Rhs_map.empty }
49+
50+
let add op_class op v m =
51+
match op_class with
52+
| Op_load ->
53+
{ m with load_equations = Rhs_map.add op v m.load_equations }
54+
| _ ->
55+
{ m with other_equations = Rhs_map.add op v m.other_equations }
56+
57+
let find op_class op m =
58+
match op_class with
59+
| Op_load ->
60+
Rhs_map.find op m.load_equations
61+
| _ ->
62+
Rhs_map.find op m.other_equations
63+
64+
let remove_loads m =
65+
{ load_equations = Rhs_map.empty;
66+
other_equations = m.other_equations }
67+
end
68+
69+
type numbering =
70+
{ num_next: int; (* next fresh value number *)
71+
num_eqs: valnum array Equations.t; (* mapping rhs -> valnums *)
72+
num_reg: valnum Reg.Map.t } (* mapping register -> valnum *)
73+
74+
let empty_numbering =
75+
{ num_next = 0; num_eqs = Equations.empty; num_reg = Reg.Map.empty }
76+
77+
(** Generate a fresh value number [v] and associate it to register [r].
78+
Returns a pair [(n',v)] with the updated value numbering [n']. *)
79+
80+
let fresh_valnum_reg n r =
81+
let v = n.num_next in
82+
({n with num_next = v + 1; num_reg = Reg.Map.add r v n.num_reg}, v)
83+
84+
(* Same, for a set of registers [rs]. *)
85+
86+
let array_fold_transf (f: numbering -> 'a -> numbering * 'b) n (a: 'a array)
87+
: numbering * 'b array =
88+
match Array.length a with
89+
| 0 -> (n, [||])
90+
| 1 -> let (n', b) = f n a.(0) in (n', [|b|])
91+
| l -> let b = Array.make l 0 and n = ref n in
92+
for i = 0 to l - 1 do
93+
let (n', x) = f !n a.(i) in
94+
b.(i) <- x; n := n'
95+
done;
96+
(!n, b)
97+
98+
let fresh_valnum_regs n rs =
99+
array_fold_transf fresh_valnum_reg n rs
100+
101+
(** [valnum_reg n r] returns the value number for the contents of
102+
register [r]. If none exists, a fresh value number is returned
103+
and associated with register [r]. The possibly updated numbering
104+
is also returned. [valnum_regs] is similar, but for an array of
105+
registers. *)
106+
107+
let valnum_reg n r =
108+
try
109+
(n, Reg.Map.find r n.num_reg)
110+
with Not_found ->
111+
fresh_valnum_reg n r
112+
113+
let valnum_regs n rs =
114+
array_fold_transf valnum_reg n rs
115+
116+
(* Look up the set of equations for an equation with the given rhs.
117+
Return [Some res] if there is one, where [res] is the lhs. *)
118+
119+
let find_equation op_class n rhs =
120+
try
121+
Some(Equations.find op_class rhs n.num_eqs)
122+
with Not_found ->
123+
None
124+
125+
(* Find a register containing the given value number. *)
126+
127+
let find_reg_containing n v =
128+
Reg.Map.fold (fun r v' res -> if v' = v then Some r else res)
129+
n.num_reg None
130+
131+
(* Find a set of registers containing the given value numbers. *)
132+
133+
let find_regs_containing n vs =
134+
match Array.length vs with
135+
| 0 -> Some [||]
136+
| 1 -> begin match find_reg_containing n vs.(0) with
137+
| None -> None
138+
| Some r -> Some [|r|]
139+
end
140+
| l -> let rs = Array.make l Reg.dummy in
141+
begin try
142+
for i = 0 to l - 1 do
143+
match find_reg_containing n vs.(i) with
144+
| None -> raise Exit
145+
| Some r -> rs.(i) <- r
146+
done;
147+
Some rs
148+
with Exit ->
149+
None
150+
end
151+
152+
(* Associate the given value number to the given result register,
153+
without adding new equations. *)
154+
155+
let set_known_reg n r v =
156+
{ n with num_reg = Reg.Map.add r v n.num_reg }
157+
158+
(* Associate the given value numbers to the given result registers,
159+
without adding new equations. *)
160+
161+
let array_fold2 f n a1 a2 =
162+
let l = Array.length a1 in
163+
assert (l = Array.length a2);
164+
let n = ref n in
165+
for i = 0 to l - 1 do n := f !n a1.(i) a2.(i) done;
166+
!n
167+
168+
let set_known_regs n rs vs =
169+
array_fold2 set_known_reg n rs vs
170+
171+
(* Record the effect of a move: no new equations, but the result reg
172+
maps to the same value number as the argument reg. *)
173+
174+
let set_move n src dst =
175+
let (n1, v) = valnum_reg n src in
176+
{ n1 with num_reg = Reg.Map.add dst v n1.num_reg }
177+
178+
(* Record the equation [fresh valnums = rhs] and associate the given
179+
result registers [rs] to [fresh valnums]. *)
180+
181+
let set_fresh_regs n rs rhs op_class =
182+
let (n1, vs) = fresh_valnum_regs n rs in
183+
{ n1 with num_eqs = Equations.add op_class rhs vs n.num_eqs }
184+
185+
(* Forget everything we know about the given result registers,
186+
which are receiving unpredictable values at run-time. *)
187+
188+
let set_unknown_regs n rs =
189+
{ n with num_reg = Array.fold_right Reg.Map.remove rs n.num_reg }
190+
191+
(* Keep only the equations satisfying the given predicate. *)
192+
193+
let remove_load_numbering n =
194+
{ n with num_eqs = Equations.remove_loads n.num_eqs }
195+
196+
(* Forget everything we know about registers of type [Addr]. *)
197+
198+
let kill_addr_regs n =
199+
{ n with num_reg =
200+
Reg.Map.filter (fun r _n -> r.Reg.typ <> Cmm.Addr) n.num_reg }
201+
202+
(* Prepend a set of moves before [i] to assign [srcs] to [dsts]. *)
203+
204+
let insert_single_move i src dst = instr_cons (Iop Imove) [|src|] [|dst|] i
205+
206+
let insert_move srcs dsts i =
207+
match Array.length srcs with
208+
| 0 -> i
209+
| 1 -> instr_cons (Iop Imove) srcs dsts i
210+
| _ -> (* Parallel move: first copy srcs into tmps one by one,
211+
then copy tmps into dsts one by one *)
212+
let tmps = Reg.createv_like srcs in
213+
let i1 = array_fold2 insert_single_move i tmps dsts in
214+
array_fold2 insert_single_move i1 srcs tmps
215+
216+
class cse_generic = object (self)
217+
218+
(* Default classification of operations. Can be overridden in
219+
processor-specific files to classify specific operations better. *)
220+
221+
method class_of_operation op =
222+
match op with
223+
| Imove | Ispill | Ireload -> assert false (* treated specially *)
224+
| Iconst_int _ | Iconst_float _ | Iconst_symbol _ -> Op_pure
225+
| Icall_ind | Icall_imm _ | Itailcall_ind | Itailcall_imm _
226+
| Iextcall _ | Iprobe _ -> assert false (* treated specially *)
227+
| Istackoffset _ -> Op_other
228+
| Iload(_,_) -> Op_load
229+
| Istore(_,_,asg) -> Op_store asg
230+
| Ialloc _ -> assert false (* treated specially *)
231+
| Iintop(Icheckbound) -> Op_checkbound
232+
| Iintop _ -> Op_pure
233+
| Iintop_imm(Icheckbound, _) -> Op_checkbound
234+
| Iintop_imm(_, _) -> Op_pure
235+
| Inegf | Iabsf | Iaddf | Isubf | Imulf | Idivf
236+
| Ifloatofint | Iintoffloat -> Op_pure
237+
| Ispecific _ -> Op_other
238+
| Iname_for_debugger _ -> Op_pure
239+
| Iprobe_is_enabled _ -> Op_other
240+
241+
(* Operations that are so cheap that it isn't worth factoring them. *)
242+
243+
method is_cheap_operation op =
244+
match op with
245+
| Iconst_int _ -> true
246+
| _ -> false
247+
248+
(* Forget all equations involving memory loads. Performed after a
249+
non-initializing store *)
250+
251+
method private kill_loads n =
252+
remove_load_numbering n
253+
254+
(* Perform CSE on the given instruction [i] and its successors.
255+
[n] is the value numbering current at the beginning of [i]. *)
256+
257+
method private cse n i =
258+
match i.desc with
259+
| Iend | Ireturn | Iop(Itailcall_ind) | Iop(Itailcall_imm _)
260+
| Iexit _ | Iraise _ ->
261+
i
262+
| Iop (Imove | Ispill | Ireload) ->
263+
(* For moves, we associate the same value number to the result reg
264+
as to the argument reg. *)
265+
let n1 = set_move n i.arg.(0) i.res.(0) in
266+
{i with next = self#cse n1 i.next}
267+
| Iop (Icall_ind | Icall_imm _ | Iextcall _ | Iprobe _) ->
268+
(* For function calls, we should at least forget:
269+
- equations involving memory loads, since the callee can
270+
perform arbitrary memory stores;
271+
- equations involving arithmetic operations that can
272+
produce [Addr]-typed derived pointers into the heap
273+
(see below for Ialloc);
274+
- mappings from hardware registers to value numbers,
275+
since the callee does not preserve these registers.
276+
That doesn't leave much usable information: checkbounds
277+
could be kept, but won't be usable for CSE as one of their
278+
arguments is always a memory load. For simplicity, we
279+
just forget everything. *)
280+
{i with next = self#cse empty_numbering i.next}
281+
| Iop (Ialloc _) ->
282+
(* For allocations, we must avoid extending the live range of a
283+
pseudoregister across the allocation if this pseudoreg
284+
is a derived heap pointer (a pointer into the heap that does
285+
not point to the beginning of a Caml block). PR#6484 is an
286+
example of this situation. Such pseudoregs have type [Addr].
287+
Pseudoregs with types other than [Addr] can be kept.
288+
Moreover, allocation can trigger the asynchronous execution
289+
of arbitrary Caml code (finalizer, signal handler, context
290+
switch), which can contain non-initializing stores.
291+
Hence, all equations over loads must be removed. *)
292+
let n1 = kill_addr_regs (self#kill_loads n) in
293+
let n2 = set_unknown_regs n1 i.res in
294+
{i with next = self#cse n2 i.next}
295+
| Iop op ->
296+
begin match self#class_of_operation op with
297+
| (Op_pure | Op_checkbound | Op_load) as op_class ->
298+
let (n1, varg) = valnum_regs n i.arg in
299+
let n2 = set_unknown_regs n1 (Proc.destroyed_at_oper i.desc) in
300+
begin match find_equation op_class n1 (op, varg) with
301+
| Some vres ->
302+
(* This operation was computed earlier. *)
303+
(* Are there registers that hold the results computed earlier? *)
304+
begin match find_regs_containing n1 vres with
305+
| Some res when (not (self#is_cheap_operation op))
306+
&& (not (Proc.regs_are_volatile res)) ->
307+
(* We can replace res <- op args with r <- move res,
308+
provided res are stable (non-volatile) registers.
309+
If the operation is very cheap to compute, e.g.
310+
an integer constant, don't bother. *)
311+
let n3 = set_known_regs n1 i.res vres in
312+
(* This is n1 above and not n2 because the move
313+
does not destroy any regs *)
314+
insert_move res i.res (self#cse n3 i.next)
315+
| _ ->
316+
(* We already computed the operation but lost its
317+
results. Associate the result registers to
318+
the result valnums of the previous operation. *)
319+
let n3 = set_known_regs n2 i.res vres in
320+
{i with next = self#cse n3 i.next}
321+
end
322+
| None ->
323+
(* This operation produces a result we haven't seen earlier. *)
324+
let n3 = set_fresh_regs n2 i.res (op, varg) op_class in
325+
{i with next = self#cse n3 i.next}
326+
end
327+
| Op_store false | Op_other ->
328+
(* An initializing store or an "other" operation do not invalidate
329+
any equations, but we do not know anything about the results. *)
330+
let n1 = set_unknown_regs n (Proc.destroyed_at_oper i.desc) in
331+
let n2 = set_unknown_regs n1 i.res in
332+
{i with next = self#cse n2 i.next}
333+
| Op_store true ->
334+
(* A non-initializing store can invalidate
335+
anything we know about prior loads. *)
336+
let n1 = set_unknown_regs n (Proc.destroyed_at_oper i.desc) in
337+
let n2 = set_unknown_regs n1 i.res in
338+
let n3 = self#kill_loads n2 in
339+
{i with next = self#cse n3 i.next}
340+
end
341+
(* For control structures, we set the numbering to empty at every
342+
join point, but propagate the current numbering across fork points. *)
343+
| Iifthenelse(test, ifso, ifnot) ->
344+
let n1 = set_unknown_regs n (Proc.destroyed_at_oper i.desc) in
345+
{i with desc = Iifthenelse(test, self#cse n1 ifso, self#cse n1 ifnot);
346+
next = self#cse empty_numbering i.next}
347+
| Iswitch(index, cases) ->
348+
let n1 = set_unknown_regs n (Proc.destroyed_at_oper i.desc) in
349+
{i with desc = Iswitch(index, Array.map (self#cse n1) cases);
350+
next = self#cse empty_numbering i.next}
351+
| Icatch(rec_flag, handlers, body) ->
352+
let aux (nfail, handler) =
353+
nfail, self#cse empty_numbering handler
354+
in
355+
{i with desc = Icatch(rec_flag, List.map aux handlers, self#cse n body);
356+
next = self#cse empty_numbering i.next}
357+
| Itrywith(body, handler) ->
358+
{i with desc = Itrywith(self#cse n body,
359+
self#cse empty_numbering handler);
360+
next = self#cse empty_numbering i.next}
361+
362+
method fundecl f =
363+
(* CSE can trigger bad register allocation behaviors, see MPR#7630 *)
364+
if List.mem Cmm.No_CSE f.fun_codegen_options then
365+
f
366+
else
367+
{f with fun_body = self#cse empty_numbering f.fun_body }
368+
369+
end

0 commit comments

Comments
 (0)