[preanal] inlining synthetic methods as a pre-analysis

Summary:
This is so that all pre-analyses are together instead of spread across
several modules.

PS: this function is the worst.

Reviewed By: ngorogiannis

Differential Revision: D19973285

fbshipit-source-id: b326e99cd
master
Jules Villard 5 years ago committed by Facebook Github Bot
parent e27e07603a
commit 2047f4c535

@ -56,101 +56,6 @@ let store source_file cfg =
Procname.Hash.iter save_proc cfg Procname.Hash.iter save_proc cfg
(** Inline a synthetic (access or bridge) method. *)
let inline_synthetic_method ((ret_id, _) as ret) etl pdesc loc_call : Sil.instr option =
let found instr instr' =
L.(debug Analysis Verbose)
"XX inline_synthetic_method found instr: %a@."
(Sil.pp_instr ~print_types:true Pp.text)
instr ;
L.(debug Analysis Verbose)
"XX inline_synthetic_method instr': %a@."
(Sil.pp_instr ~print_types:true Pp.text)
instr' ;
Some instr'
in
let do_instr instr =
match (instr, etl) with
| Sil.Load {e= Exp.Lfield (Exp.Var _, fn, ft); root_typ; typ}, [(* getter for fields *) (e1, _)]
->
let instr' =
Sil.Load {id= ret_id; e= Exp.Lfield (e1, fn, ft); root_typ; typ; loc= loc_call}
in
found instr instr'
| Sil.Load {e= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ}, [] when Pvar.is_global pvar
->
(* getter for static fields *)
let instr' =
Sil.Load {id= ret_id; e= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ; loc= loc_call}
in
found instr instr'
| ( Sil.Store {e1= Exp.Lfield (_, fn, ft); root_typ; typ}
, [(* setter for fields *) (e1, _); (e2, _)] ) ->
let instr' = Sil.Store {e1= Exp.Lfield (e1, fn, ft); root_typ; typ; e2; loc= loc_call} in
found instr instr'
| Sil.Store {e1= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ}, [(e1, _)]
when Pvar.is_global pvar ->
(* setter for static fields *)
let instr' =
Sil.Store {e1= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ; e2= e1; loc= loc_call}
in
found instr instr'
| Sil.Call (_, Exp.Const (Const.Cfun pn), etl', _, cf), _
when Int.equal (List.length etl') (List.length etl) ->
let instr' = Sil.Call (ret, Exp.Const (Const.Cfun pn), etl, loc_call, cf) in
found instr instr'
| Sil.Call (_, Exp.Const (Const.Cfun pn), etl', _, cf), _
when Int.equal (List.length etl' + 1) (List.length etl) ->
let etl1 =
match List.rev etl with
(* remove last element *)
| _ :: l ->
List.rev l
| [] ->
assert false
in
let instr' = Sil.Call (ret, Exp.Const (Const.Cfun pn), etl1, loc_call, cf) in
found instr instr'
| _ ->
None
in
Procdesc.find_map_instrs ~f:do_instr pdesc
(** Find synthetic (access or bridge) Java methods in the procedure and inline them in the cfg. *)
let proc_inline_synthetic_methods cfg pdesc : unit =
let instr_inline_synthetic_method _node instr =
match instr with
| Sil.Call (ret_id_typ, Exp.Const (Const.Cfun (Procname.Java java_pn as pn)), etl, loc, _) -> (
match Procname.Hash.find cfg pn with
| pd ->
let is_access = Procname.Java.is_access_method java_pn in
let attributes = Procdesc.get_attributes pd in
let is_synthetic = attributes.is_synthetic_method in
let is_bridge = attributes.is_bridge_method in
let is_generated_for_lambda =
String.is_substring ~substring:"$Lambda$" (Procname.get_method pn)
in
(* this is a temporary hack in order to stop synthetic inlining on
methods that are generated for lambda rewritting *)
if is_generated_for_lambda then instr
else if is_access || is_bridge || is_synthetic then
inline_synthetic_method ret_id_typ etl pd loc |> Option.value ~default:instr
else instr
| exception (Caml.Not_found | Not_found_s _) ->
instr )
| _ ->
instr
in
let (_updated : bool) = Procdesc.replace_instrs pdesc ~f:instr_inline_synthetic_method in
()
let inline_java_synthetic_methods cfg =
let f pname pdesc = if Procname.is_java pname then proc_inline_synthetic_methods cfg pdesc in
Procname.Hash.iter f cfg
let pp_proc_signatures fmt cfg = let pp_proc_signatures fmt cfg =
F.fprintf fmt "@[<v>METHOD SIGNATURES@;" ; F.fprintf fmt "@[<v>METHOD SIGNATURES@;" ;
iter_over_sorted_procs ~f:(Procdesc.pp_signature fmt) cfg ; iter_over_sorted_procs ~f:(Procdesc.pp_signature fmt) cfg ;

@ -31,7 +31,4 @@ val create_proc_desc : t -> ProcAttributes.t -> Procdesc.t
val iter_sorted : t -> f:(Procdesc.t -> unit) -> unit val iter_sorted : t -> f:(Procdesc.t -> unit) -> unit
(** Iterate over all the proc descs in the cfg in ascending order *) (** Iterate over all the proc descs in the cfg in ascending order *)
val inline_java_synthetic_methods : t -> unit
(** Inline the java synthetic methods in the cfg (in-place) *)
val pp_proc_signatures : Format.formatter -> t -> unit val pp_proc_signatures : Format.formatter -> t -> unit

@ -28,7 +28,6 @@ let get_existing_data source_file =
let add source_file cfg tenv integer_type_widths = let add source_file cfg tenv integer_type_widths =
Cfg.inline_java_synthetic_methods cfg ;
let tenv, proc_names = let tenv, proc_names =
(* The same source file may get captured several times in a single capture event, for instance (* The same source file may get captured several times in a single capture event, for instance
because it is compiled with different options, or from different symbolic links. This may because it is compiled with different options, or from different symbolic links. This may

@ -35,6 +35,107 @@ module AddAbstractionInstructions = struct
Procdesc.iter_nodes do_node pdesc Procdesc.iter_nodes do_node pdesc
end end
(** Find synthetic (including access and bridge) Java methods in the procedure and inline them in
the cfg.
This is a horrible hack that inlines only *one* instruction ouf of the callee. This works only
on some synthetic methods that have a particular shape. *)
module InlineJavaSyntheticMethods = struct
(** Inline a synthetic (access or bridge) method. *)
let inline_synthetic_method ((ret_id, _) as ret) etl pdesc loc_call : Sil.instr option =
let found instr instr' =
L.debug Analysis Verbose
"inline_synthetic_method translated the call %a as %a (original instr %a)@\n" Procname.pp
(Procdesc.get_proc_name pdesc)
(Sil.pp_instr ~print_types:true Pp.text)
instr'
(Sil.pp_instr ~print_types:true Pp.text)
instr ;
Some instr'
in
let do_instr instr =
match (instr, etl) with
| ( Sil.Load {e= Exp.Lfield (Exp.Var _, fn, ft); root_typ; typ}
, [(* getter for fields *) (e1, _)] ) ->
let instr' =
Sil.Load {id= ret_id; e= Exp.Lfield (e1, fn, ft); root_typ; typ; loc= loc_call}
in
found instr instr'
| Sil.Load {e= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ}, [] when Pvar.is_global pvar
->
(* getter for static fields *)
let instr' =
Sil.Load
{id= ret_id; e= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ; loc= loc_call}
in
found instr instr'
| ( Sil.Store {e1= Exp.Lfield (_, fn, ft); root_typ; typ}
, [(* setter for fields *) (e1, _); (e2, _)] ) ->
let instr' = Sil.Store {e1= Exp.Lfield (e1, fn, ft); root_typ; typ; e2; loc= loc_call} in
found instr instr'
| Sil.Store {e1= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ}, [(e1, _)]
when Pvar.is_global pvar ->
(* setter for static fields *)
let instr' =
Sil.Store {e1= Exp.Lfield (Exp.Lvar pvar, fn, ft); root_typ; typ; e2= e1; loc= loc_call}
in
found instr instr'
| Sil.Call (_, Exp.Const (Const.Cfun pn), etl', _, cf), _
when Int.equal (List.length etl') (List.length etl) ->
let instr' = Sil.Call (ret, Exp.Const (Const.Cfun pn), etl, loc_call, cf) in
found instr instr'
| Sil.Call (_, Exp.Const (Const.Cfun pn), etl', _, cf), _
when Int.equal (List.length etl' + 1) (List.length etl) ->
let etl1 =
match List.rev etl with
(* remove last element *)
| _ :: l ->
List.rev l
| [] ->
assert false
in
let instr' = Sil.Call (ret, Exp.Const (Const.Cfun pn), etl1, loc_call, cf) in
found instr instr'
| _ ->
None
in
Procdesc.find_map_instrs ~f:do_instr pdesc
let process pdesc =
let is_generated_for_lambda proc_name =
String.is_substring ~substring:"$Lambda$" (Procname.get_method proc_name)
in
let should_inline proc_name =
(not (is_generated_for_lambda proc_name))
&&
match Attributes.load proc_name with
| None ->
false
| Some attributes ->
let is_access =
match proc_name with
| Procname.Java java_proc_name ->
Procname.Java.is_access_method java_proc_name
| _ ->
false
in
let is_synthetic = attributes.is_synthetic_method in
let is_bridge = attributes.is_bridge_method in
is_access || is_bridge || is_synthetic
in
let instr_inline_synthetic_method _node (instr : Sil.instr) =
match instr with
| Call (ret_id_typ, Const (Cfun pn), etl, loc, _) when should_inline pn ->
Option.bind (Procdesc.load pn) ~f:(fun proc_desc_callee ->
inline_synthetic_method ret_id_typ etl proc_desc_callee loc )
|> Option.value ~default:instr
| _ ->
instr
in
Procdesc.replace_instrs pdesc ~f:instr_inline_synthetic_method |> ignore
end
(** perform liveness analysis and insert Nullify/Remove_temps instructions into the IR to make it (** perform liveness analysis and insert Nullify/Remove_temps instructions into the IR to make it
easy for analyses to do abstract garbage collection *) easy for analyses to do abstract garbage collection *)
module Liveness = struct module Liveness = struct
@ -236,8 +337,10 @@ end
let do_preanalysis exe_env pdesc = let do_preanalysis exe_env pdesc =
let summary = Summary.OnDisk.reset pdesc in let summary = Summary.OnDisk.reset pdesc in
let tenv = Exe_env.get_tenv exe_env (Procdesc.get_proc_name pdesc) in let tenv = Exe_env.get_tenv exe_env (Procdesc.get_proc_name pdesc) in
if Config.function_pointer_specialization && not (Procname.is_java (Procdesc.get_proc_name pdesc)) let proc_name = Procdesc.get_proc_name pdesc in
then FunctionPointerSubstitution.process summary tenv ; if Procname.is_java proc_name then InlineJavaSyntheticMethods.process pdesc ;
if Config.function_pointer_specialization && not (Procname.is_java proc_name) then
FunctionPointerSubstitution.process summary tenv ;
Liveness.process summary tenv ; Liveness.process summary tenv ;
AddAbstractionInstructions.process pdesc ; AddAbstractionInstructions.process pdesc ;
NoReturn.process pdesc ; NoReturn.process pdesc ;

Loading…
Cancel
Save