1995-08-09 08:06:35 -07:00
|
|
|
(***********************************************************************)
|
|
|
|
(* *)
|
1996-04-30 07:53:58 -07:00
|
|
|
(* Objective Caml *)
|
1995-08-09 08:06:35 -07:00
|
|
|
(* *)
|
|
|
|
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
|
|
|
|
(* *)
|
1996-04-30 07:53:58 -07:00
|
|
|
(* Copyright 1996 Institut National de Recherche en Informatique et *)
|
1999-11-17 10:59:06 -08:00
|
|
|
(* en Automatique. All rights reserved. This file is distributed *)
|
|
|
|
(* under the terms of the Q Public License version 1.0. *)
|
1995-08-09 08:06:35 -07:00
|
|
|
(* *)
|
|
|
|
(***********************************************************************)
|
|
|
|
|
|
|
|
(* $Id$ *)
|
|
|
|
|
1995-06-15 01:17:29 -07:00
|
|
|
(* Liveness analysis.
|
|
|
|
Annotate mach code with the set of regs live at each point. *)
|
|
|
|
|
|
|
|
open Mach
|
|
|
|
|
|
|
|
let live_at_exit = ref Reg.Set.empty
|
1995-06-15 09:08:53 -07:00
|
|
|
let live_at_break = ref Reg.Set.empty
|
1995-06-15 01:17:29 -07:00
|
|
|
let live_at_raise = ref Reg.Set.empty
|
|
|
|
|
|
|
|
let rec live i finally =
|
|
|
|
(* finally is the set of registers live after execution of the
|
|
|
|
instruction sequence.
|
|
|
|
The result of the function is the set of registers live just
|
|
|
|
before the instruction sequence.
|
|
|
|
The instruction i is annotated by the set of registers live across
|
|
|
|
the instruction. *)
|
|
|
|
match i.desc with
|
|
|
|
Iend ->
|
|
|
|
i.live <- finally;
|
|
|
|
finally
|
|
|
|
| Ireturn | Iop(Itailcall_ind) | Iop(Itailcall_imm _) ->
|
|
|
|
(* i.live remains empty since no regs are live across *)
|
|
|
|
Reg.set_of_array i.arg
|
|
|
|
| Iifthenelse(test, ifso, ifnot) ->
|
|
|
|
let at_join = live i.next finally in
|
|
|
|
let at_fork = Reg.Set.union (live ifso at_join) (live ifnot at_join) in
|
|
|
|
i.live <- at_fork;
|
|
|
|
Reg.add_set_array at_fork i.arg
|
|
|
|
| Iswitch(index, cases) ->
|
|
|
|
let at_join = live i.next finally in
|
|
|
|
let at_fork = ref Reg.Set.empty in
|
|
|
|
for i = 0 to Array.length cases - 1 do
|
|
|
|
at_fork := Reg.Set.union !at_fork (live cases.(i) at_join)
|
|
|
|
done;
|
|
|
|
i.live <- !at_fork;
|
|
|
|
Reg.add_set_array !at_fork i.arg
|
|
|
|
| Iloop(body) ->
|
1995-06-15 09:08:53 -07:00
|
|
|
let at_top = ref Reg.Set.empty in
|
1995-06-15 01:17:29 -07:00
|
|
|
(* Yes, there are better algorithms, but we'll just iterate till
|
|
|
|
reaching a fixpoint. *)
|
|
|
|
begin try
|
|
|
|
while true do
|
1995-06-15 09:08:53 -07:00
|
|
|
let new_at_top = Reg.Set.union !at_top (live body !at_top) in
|
|
|
|
if Reg.Set.equal !at_top new_at_top then raise Exit;
|
|
|
|
at_top := new_at_top
|
1995-06-15 01:17:29 -07:00
|
|
|
done
|
|
|
|
with Exit -> ()
|
|
|
|
end;
|
1995-06-15 09:08:53 -07:00
|
|
|
i.live <- !at_top;
|
|
|
|
!at_top
|
1995-06-15 01:17:29 -07:00
|
|
|
| Icatch(body, handler) ->
|
|
|
|
let at_join = live i.next finally in
|
|
|
|
let before_handler = live handler at_join in
|
|
|
|
let saved_live_at_exit = !live_at_exit in
|
|
|
|
live_at_exit := before_handler;
|
|
|
|
let before_body = live body at_join in
|
|
|
|
live_at_exit := saved_live_at_exit;
|
|
|
|
i.live <- before_body;
|
|
|
|
before_body
|
|
|
|
| Iexit ->
|
1996-01-11 06:15:23 -08:00
|
|
|
i.live <- !live_at_exit; (* These regs are live across *)
|
1995-06-15 01:17:29 -07:00
|
|
|
!live_at_exit
|
|
|
|
| Itrywith(body, handler) ->
|
|
|
|
let at_join = live i.next finally in
|
|
|
|
let before_handler = live handler at_join in
|
|
|
|
let saved_live_at_raise = !live_at_raise in
|
1995-07-02 09:41:48 -07:00
|
|
|
live_at_raise := Reg.Set.remove Proc.loc_exn_bucket before_handler;
|
1995-06-15 01:17:29 -07:00
|
|
|
let before_body = live body at_join in
|
|
|
|
live_at_raise := saved_live_at_raise;
|
|
|
|
i.live <- before_body;
|
|
|
|
before_body
|
|
|
|
| Iraise ->
|
|
|
|
(* i.live remains empty since no regs are live across *)
|
|
|
|
Reg.add_set_array !live_at_raise i.arg
|
|
|
|
| _ ->
|
1995-07-07 05:07:07 -07:00
|
|
|
let across_after = Reg.diff_set_array (live i.next finally) i.res in
|
|
|
|
let across =
|
|
|
|
match i.desc with
|
1998-11-11 07:35:48 -08:00
|
|
|
Iop(Icall_ind) | Iop(Icall_imm _) | Iop(Iextcall(_, _))
|
|
|
|
| Iop(Iintop Icheckbound) | Iop(Iintop_imm(Icheckbound, _)) ->
|
1995-07-07 05:07:07 -07:00
|
|
|
(* The function call may raise an exception, branching to the
|
1998-11-11 07:35:48 -08:00
|
|
|
nearest enclosing try ... with. Similarly for bounds checks.
|
|
|
|
Hence, everything that must be live at the beginning of
|
|
|
|
the exception handler must also be live across this instr. *)
|
1995-07-07 05:07:07 -07:00
|
|
|
Reg.Set.union across_after !live_at_raise
|
|
|
|
| _ ->
|
|
|
|
across_after in
|
1995-06-15 01:17:29 -07:00
|
|
|
i.live <- across;
|
1995-07-07 05:07:07 -07:00
|
|
|
Reg.add_set_array across i.arg
|
1995-06-15 01:17:29 -07:00
|
|
|
|
2000-04-21 01:13:22 -07:00
|
|
|
let fundecl ppf f =
|
1996-07-25 06:15:16 -07:00
|
|
|
let initially_live = live f.fun_body Reg.Set.empty in
|
|
|
|
(* Sanity check: only function parameters can be live at entrypoint *)
|
|
|
|
let wrong_live = Reg.Set.diff initially_live (Reg.set_of_array f.fun_args) in
|
|
|
|
if not (Reg.Set.is_empty wrong_live) then begin
|
2000-04-21 01:13:22 -07:00
|
|
|
Format.fprintf ppf "%a@." Printmach.regset wrong_live;
|
1996-07-25 06:15:16 -07:00
|
|
|
Misc.fatal_error "Liveness.fundecl"
|
|
|
|
end
|