143 lines
5.6 KiB
OCaml
143 lines
5.6 KiB
OCaml
(**************************************************************************)
|
|
(* *)
|
|
(* OCaml *)
|
|
(* *)
|
|
(* Xavier Leroy, projet Gallium, INRIA Rocquencourt *)
|
|
(* *)
|
|
(* Copyright 2014 Institut National de Recherche en Informatique et *)
|
|
(* en Automatique. *)
|
|
(* *)
|
|
(* All rights reserved. This file is distributed under the terms of *)
|
|
(* the GNU Lesser General Public License version 2.1, with the *)
|
|
(* special exception on linking described in the file LICENSE. *)
|
|
(* *)
|
|
(**************************************************************************)
|
|
|
|
(* Dead code elimination: remove pure instructions whose results are
|
|
not used. *)
|
|
|
|
open Mach
|
|
|
|
module Int = Numbers.Int
|
|
|
|
type d = {
|
|
i : instruction; (* optimized instruction *)
|
|
regs : Reg.Set.t; (* a set of registers live "before" instruction [i] *)
|
|
exits : Int.Set.t; (* indexes of Iexit instructions "live before" [i] *)
|
|
}
|
|
|
|
let append a b =
|
|
let rec append a b =
|
|
match a.desc with
|
|
| Iend -> b
|
|
| _ -> { a with next = append a.next b }
|
|
in
|
|
match b.desc with
|
|
| Iend -> a
|
|
| _ -> append a b
|
|
|
|
let rec deadcode i =
|
|
match i.desc with
|
|
| Iend | Ireturn | Iop(Itailcall_ind) | Iop(Itailcall_imm _) | Iraise _ ->
|
|
let regs = Reg.add_set_array i.live i.arg in
|
|
{ i; regs; exits = Int.Set.empty; }
|
|
| Iop op ->
|
|
let s = deadcode i.next in
|
|
if Proc.op_is_pure op (* no side effects *)
|
|
&& Reg.disjoint_set_array s.regs i.res (* results are not used after *)
|
|
&& not (Proc.regs_are_volatile i.arg) (* no stack-like hard reg *)
|
|
&& not (Proc.regs_are_volatile i.res) (* is involved *)
|
|
then begin
|
|
assert (Array.length i.res > 0); (* sanity check *)
|
|
s
|
|
end else begin
|
|
{ i = {i with next = s.i};
|
|
regs = Reg.add_set_array i.live i.arg;
|
|
exits = s.exits;
|
|
}
|
|
end
|
|
| Iifthenelse(test, ifso, ifnot) ->
|
|
let ifso' = deadcode ifso in
|
|
let ifnot' = deadcode ifnot in
|
|
let s = deadcode i.next in
|
|
{ i = {i with desc = Iifthenelse(test, ifso'.i, ifnot'.i); next = s.i};
|
|
regs = Reg.add_set_array i.live i.arg;
|
|
exits = Int.Set.union s.exits
|
|
(Int.Set.union ifso'.exits ifnot'.exits);
|
|
}
|
|
| Iswitch(index, cases) ->
|
|
let dc = Array.map deadcode cases in
|
|
let cases' = Array.map (fun c -> c.i) dc in
|
|
let s = deadcode i.next in
|
|
{ i = {i with desc = Iswitch(index, cases'); next = s.i};
|
|
regs = Reg.add_set_array i.live i.arg;
|
|
exits = Array.fold_left
|
|
(fun acc c -> Int.Set.union acc c.exits) s.exits dc;
|
|
}
|
|
| Icatch(rec_flag, handlers, body) ->
|
|
let body' = deadcode body in
|
|
let s = deadcode i.next in
|
|
let handlers' = Int.Map.map deadcode (Int.Map.of_list handlers) in
|
|
(* Previous passes guarantee that indexes of handlers are unique
|
|
across the entire function and Iexit instructions refer
|
|
to the correctly scoped handlers.
|
|
We do not rely on it here, for safety. *)
|
|
let rec add_live nfail (live_exits, used_handlers) =
|
|
if Int.Set.mem nfail live_exits then
|
|
(live_exits, used_handlers)
|
|
else
|
|
let live_exits = Int.Set.add nfail live_exits in
|
|
match Int.Map.find_opt nfail handlers' with
|
|
| None -> (live_exits, used_handlers)
|
|
| Some handler ->
|
|
let used_handlers = (nfail, handler) :: used_handlers in
|
|
match rec_flag with
|
|
| Cmm.Nonrecursive -> (live_exits, used_handlers)
|
|
| Cmm.Recursive ->
|
|
Int.Set.fold add_live handler.exits (live_exits, used_handlers)
|
|
in
|
|
let live_exits, used_handlers =
|
|
Int.Set.fold add_live body'.exits (Int.Set.empty, [])
|
|
in
|
|
(* Remove exits that are going out of scope. *)
|
|
let used_handler_indexes = Int.Set.of_list (List.map fst used_handlers) in
|
|
let live_exits = Int.Set.diff live_exits used_handler_indexes in
|
|
(* For non-recursive catch, live exits referenced in handlers are free. *)
|
|
let live_exits =
|
|
match rec_flag with
|
|
| Cmm.Recursive -> live_exits
|
|
| Cmm.Nonrecursive ->
|
|
List.fold_left (fun exits (_,h) -> Int.Set.union h.exits exits)
|
|
live_exits
|
|
used_handlers
|
|
in
|
|
let exits = Int.Set.union s.exits live_exits in
|
|
begin match used_handlers with
|
|
| [] -> (* Simplify catch without handlers *)
|
|
{ i = append body'.i s.i;
|
|
regs = body'.regs;
|
|
exits;
|
|
}
|
|
| _ ->
|
|
let handlers = List.map (fun (n,h) -> (n,h.i)) used_handlers in
|
|
{ i = { i with desc = Icatch(rec_flag, handlers, body'.i); next = s.i };
|
|
regs = i.live;
|
|
exits;
|
|
}
|
|
end
|
|
| Iexit nfail ->
|
|
{ i; regs = i.live; exits = Int.Set.singleton nfail; }
|
|
| Itrywith(body, handler) ->
|
|
let body' = deadcode body in
|
|
let handler' = deadcode handler in
|
|
let s = deadcode i.next in
|
|
{ i = {i with desc = Itrywith(body'.i, handler'.i); next = s.i};
|
|
regs = i.live;
|
|
exits = Int.Set.union s.exits
|
|
(Int.Set.union body'.exits handler'.exits);
|
|
}
|
|
|
|
let fundecl f =
|
|
let new_body = deadcode f.fun_body in
|
|
{f with fun_body = new_body.i}
|