252 lines
8.5 KiB
OCaml
252 lines
8.5 KiB
OCaml
(**************************************************************************)
|
|
(* *)
|
|
(* OCaml *)
|
|
(* *)
|
|
(* Pierre Chambart, OCamlPro *)
|
|
(* Mark Shinwell and Leo White, Jane Street Europe *)
|
|
(* *)
|
|
(* Copyright 2013--2016 OCamlPro SAS *)
|
|
(* Copyright 2014--2016 Jane Street Group LLC *)
|
|
(* *)
|
|
(* 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. *)
|
|
(* *)
|
|
(**************************************************************************)
|
|
|
|
[@@@ocaml.warning "+a-4-9-30-40-41-42"]
|
|
|
|
module Closure_stack = struct
|
|
type t = node list
|
|
|
|
and node =
|
|
| Closure of Closure_id.t * Debuginfo.t
|
|
| Call of Closure_id.t * Debuginfo.t
|
|
| Inlined
|
|
| Specialised of Closure_id.Set.t
|
|
|
|
let create () = []
|
|
|
|
let note_entering_closure t ~closure_id ~dbg =
|
|
if not !Clflags.inlining_report then t
|
|
else
|
|
match t with
|
|
| [] | (Closure _ | Inlined | Specialised _) :: _->
|
|
(Closure (closure_id, dbg)) :: t
|
|
| (Call _) :: _ ->
|
|
Misc.fatal_errorf "note_entering_closure: unexpected Call node"
|
|
|
|
(* CR-someday lwhite: since calls do not have a unique id it is possible
|
|
some calls will end up sharing nodes. *)
|
|
let note_entering_call t ~closure_id ~dbg =
|
|
if not !Clflags.inlining_report then t
|
|
else
|
|
match t with
|
|
| [] | (Closure _ | Inlined | Specialised _) :: _ ->
|
|
(Call (closure_id, dbg)) :: t
|
|
| (Call _) :: _ ->
|
|
Misc.fatal_errorf "note_entering_call: unexpected Call node"
|
|
|
|
let note_entering_inlined t =
|
|
if not !Clflags.inlining_report then t
|
|
else
|
|
match t with
|
|
| [] | (Closure _ | Inlined | Specialised _) :: _->
|
|
Misc.fatal_errorf "note_entering_inlined: missing Call node"
|
|
| (Call _) :: _ -> Inlined :: t
|
|
|
|
let note_entering_specialised t ~closure_ids =
|
|
if not !Clflags.inlining_report then t
|
|
else
|
|
match t with
|
|
| [] | (Closure _ | Inlined | Specialised _) :: _ ->
|
|
Misc.fatal_errorf "note_entering_specialised: missing Call node"
|
|
| (Call _) :: _ -> Specialised closure_ids :: t
|
|
|
|
end
|
|
|
|
let log
|
|
: (Closure_stack.t * Inlining_stats_types.Decision.t) list ref
|
|
= ref []
|
|
|
|
let record_decision decision ~closure_stack =
|
|
if !Clflags.inlining_report then begin
|
|
match closure_stack with
|
|
| []
|
|
| Closure_stack.Closure _ :: _
|
|
| Closure_stack.Inlined :: _
|
|
| Closure_stack.Specialised _ :: _ ->
|
|
Misc.fatal_errorf "record_decision: missing Call node"
|
|
| Closure_stack.Call _ :: _ ->
|
|
log := (closure_stack, decision) :: !log
|
|
end
|
|
|
|
module Inlining_report = struct
|
|
|
|
module Place = struct
|
|
type kind =
|
|
| Closure
|
|
| Call
|
|
|
|
type t = Debuginfo.t * Closure_id.t * kind
|
|
|
|
let compare ((d1, cl1, k1) : t) ((d2, cl2, k2) : t) =
|
|
let c = Debuginfo.compare d1 d2 in
|
|
if c <> 0 then c else
|
|
let c = Closure_id.compare cl1 cl2 in
|
|
if c <> 0 then c else
|
|
match k1, k2 with
|
|
| Closure, Closure -> 0
|
|
| Call, Call -> 0
|
|
| Closure, Call -> 1
|
|
| Call, Closure -> -1
|
|
end
|
|
|
|
module Place_map = Map.Make(Place)
|
|
|
|
type t = node Place_map.t
|
|
|
|
and node =
|
|
| Closure of t
|
|
| Call of call
|
|
|
|
and call =
|
|
{ decision: Inlining_stats_types.Decision.t option;
|
|
inlined: t option;
|
|
specialised: t option; }
|
|
|
|
let empty_call =
|
|
{ decision = None;
|
|
inlined = None;
|
|
specialised = None; }
|
|
|
|
(* Prevented or unchanged decisions may be overridden by a later look at the
|
|
same call. Other decisions may also be "overridden" because calls are not
|
|
uniquely identified. *)
|
|
let add_call_decision call (decision : Inlining_stats_types.Decision.t) =
|
|
match call.decision, decision with
|
|
| None, _ -> { call with decision = Some decision }
|
|
| Some _, Prevented _ -> call
|
|
| Some (Prevented _), _ -> { call with decision = Some decision }
|
|
| Some (Specialised _), _ -> call
|
|
| Some _, Specialised _ -> { call with decision = Some decision }
|
|
| Some (Inlined _), _ -> call
|
|
| Some _, Inlined _ -> { call with decision = Some decision }
|
|
| Some Unchanged _, Unchanged _ -> call
|
|
|
|
let add_decision t (stack, decision) =
|
|
let rec loop t : Closure_stack.t -> _ = function
|
|
| Closure(cl, dbg) :: rest ->
|
|
let key : Place.t = (dbg, cl, Closure) in
|
|
let v =
|
|
try
|
|
match Place_map.find key t with
|
|
| Closure v -> v
|
|
| Call _ -> assert false
|
|
with Not_found -> Place_map.empty
|
|
in
|
|
let v = loop v rest in
|
|
Place_map.add key (Closure v) t
|
|
| Call(cl, dbg) :: rest ->
|
|
let key : Place.t = (dbg, cl, Call) in
|
|
let v =
|
|
try
|
|
match Place_map.find key t with
|
|
| Call v -> v
|
|
| Closure _ -> assert false
|
|
with Not_found -> empty_call
|
|
in
|
|
let v =
|
|
match rest with
|
|
| [] -> add_call_decision v decision
|
|
| Inlined :: rest ->
|
|
let inlined =
|
|
match v.inlined with
|
|
| None -> Place_map.empty
|
|
| Some inlined -> inlined
|
|
in
|
|
let inlined = loop inlined rest in
|
|
{ v with inlined = Some inlined }
|
|
| Specialised _ :: rest ->
|
|
let specialised =
|
|
match v.specialised with
|
|
| None -> Place_map.empty
|
|
| Some specialised -> specialised
|
|
in
|
|
let specialised = loop specialised rest in
|
|
{ v with specialised = Some specialised }
|
|
| Call _ :: _ -> assert false
|
|
| Closure _ :: _ -> assert false
|
|
in
|
|
Place_map.add key (Call v) t
|
|
| [] -> assert false
|
|
| Inlined :: _ -> assert false
|
|
| Specialised _ :: _ -> assert false
|
|
in
|
|
loop t (List.rev stack)
|
|
|
|
let build log =
|
|
List.fold_left add_decision Place_map.empty log
|
|
|
|
let print_stars ppf n =
|
|
let s = String.make n '*' in
|
|
Format.fprintf ppf "%s" s
|
|
|
|
let rec print ~depth ppf t =
|
|
Place_map.iter (fun (dbg, cl, _) v ->
|
|
match v with
|
|
| Closure t ->
|
|
Format.fprintf ppf "@[<h>%a Definition of %a%s@]@."
|
|
print_stars (depth + 1)
|
|
Closure_id.print cl
|
|
(Debuginfo.to_string dbg);
|
|
print ppf ~depth:(depth + 1) t;
|
|
if depth = 0 then Format.pp_print_newline ppf ()
|
|
| Call c ->
|
|
match c.decision with
|
|
| None ->
|
|
Misc.fatal_error "Inlining_report.print: missing call decision"
|
|
| Some decision ->
|
|
Format.pp_open_vbox ppf (depth + 2);
|
|
Format.fprintf ppf "@[<h>%a Application of %a%s@]@;@;@[%a@]"
|
|
print_stars (depth + 1)
|
|
Closure_id.print cl
|
|
(Debuginfo.to_string dbg)
|
|
Inlining_stats_types.Decision.summary decision;
|
|
Format.pp_close_box ppf ();
|
|
Format.pp_print_newline ppf ();
|
|
Format.pp_print_newline ppf ();
|
|
Inlining_stats_types.Decision.calculation ~depth:(depth + 1)
|
|
ppf decision;
|
|
begin
|
|
match c.specialised with
|
|
| None -> ()
|
|
| Some specialised ->
|
|
print ppf ~depth:(depth + 1) specialised
|
|
end;
|
|
begin
|
|
match c.inlined with
|
|
| None -> ()
|
|
| Some inlined ->
|
|
print ppf ~depth:(depth + 1) inlined
|
|
end;
|
|
if depth = 0 then Format.pp_print_newline ppf ())
|
|
t
|
|
|
|
let print ppf t = print ~depth:0 ppf t
|
|
|
|
end
|
|
|
|
let really_save_then_forget_decisions ~output_prefix =
|
|
let report = Inlining_report.build !log in
|
|
let out_channel = open_out (output_prefix ^ ".inlining.org") in
|
|
let ppf = Format.formatter_of_out_channel out_channel in
|
|
Inlining_report.print ppf report;
|
|
close_out out_channel;
|
|
log := []
|
|
|
|
let save_then_forget_decisions ~output_prefix =
|
|
if !Clflags.inlining_report then begin
|
|
really_save_then_forget_decisions ~output_prefix
|
|
end
|