[closures preanalysis][2/n] Add second preanalysis that fills the body of the specialized methods with blocks

Reviewed By: ngorogiannis

Differential Revision: D23216021

fbshipit-source-id: b8e924127
master
Dulma Churchill 4 years ago committed by Facebook GitHub Bot
parent 892b16b8c0
commit 2e66bf6b8f

@ -641,6 +641,15 @@ let create_node pdesc loc kind instrs =
create_node_from_not_reversed pdesc loc kind (Instrs.of_list instrs)
let shallow_copy_code_from_pdesc ~orig_pdesc ~dest_pdesc =
dest_pdesc.nodes <- orig_pdesc.nodes ;
dest_pdesc.nodes_num <- orig_pdesc.nodes_num ;
dest_pdesc.start_node <- orig_pdesc.start_node ;
dest_pdesc.exit_node <- orig_pdesc.exit_node ;
dest_pdesc.loop_heads <- orig_pdesc.loop_heads ;
dest_pdesc.wto <- orig_pdesc.wto
(** Set the successor and exception nodes. If this is a join node right before the exit node, add an
extra node in the middle, otherwise nullify and abstract instructions cannot be added after a
conditional. *)

@ -322,6 +322,8 @@ val is_captured_var : t -> Var.t -> bool
val has_modify_in_block_attr : t -> Pvar.t -> bool
val shallow_copy_code_from_pdesc : orig_pdesc:t -> dest_pdesc:t -> unit
(** per-procedure CFGs are stored in the SQLite "procedures" table as NULL if the procedure has no
CFG *)
module SQLite : SqliteUtils.Data with type t = t option

@ -308,6 +308,10 @@ let get_initializer_pname {pv_name; pv_kind} =
None
let swap_proc_in_local_pvar pvar proc_name =
match pvar.pv_kind with Local_var _ -> {pvar with pv_kind= Local_var proc_name} | _ -> pvar
let rename ~f {pv_name; pv_kind} =
let pv_name = Mangled.rename ~f pv_name in
let pv_hash = name_hash pv_name in

@ -167,6 +167,8 @@ val build_formal_from_pvar : t -> Mangled.t
val materialized_cpp_temporary : string
val swap_proc_in_local_pvar : t -> Procname.t -> t
val rename : f:(string -> string) -> t -> t
(** Sets of pvars. *)

@ -0,0 +1,114 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open! IStd
module CFG = ProcCfg.Normal
module PPPVar = struct
type t = Pvar.t [@@deriving compare, equal]
let pp = Pvar.pp Pp.text
end
module VDom = AbstractDomain.Flat (PPPVar)
module Domain = AbstractDomain.Map (Ident) (VDom)
module TransferFunctions = struct
module CFG = CFG
module Domain = Domain
type analysis_data = unit
let exec_instr astate _analysis_data _node instr =
let open Sil in
match instr with
| Load {id; e= Exp.Lvar pvar} ->
Domain.add id (VDom.v pvar) astate
| Load {id} ->
Domain.add id VDom.bottom astate
| Call ((id, _), _, _, _, _) ->
Domain.add id VDom.bottom astate
| _ ->
astate
let pp_session_name node fmt =
Format.fprintf fmt "Closure Subst Specialized Method %a" CFG.Node.pp_id (CFG.Node.id node)
end
module Analyzer = AbstractInterpreter.MakeRPO (TransferFunctions)
let exec_instr domain proc_name formals_to_blocks_map _node instr =
let open Sil in
let res =
let exec_exp exp =
let exec_pvar pvar = Pvar.swap_proc_in_local_pvar pvar proc_name in
match exp with Exp.Lvar origin_pvar -> Exp.Lvar (exec_pvar origin_pvar) | exp -> exp
in
match instr with
| Load {id; e; root_typ; loc} ->
[Load {id; e= exec_exp e; root_typ; typ= root_typ; loc}]
| Store {e1; root_typ; typ; e2; loc} ->
[Store {e1= exec_exp e1; root_typ; typ; e2= exec_exp e2; loc}]
| Call (ret_id_typ, Var id, origin_args, loc, call_flags) -> (
let converted_args = List.map ~f:(fun (exp, typ) -> (exec_exp exp, typ)) origin_args in
match Option.bind ~f:VDom.get (Domain.find_opt id domain) with
| None ->
[instr]
| Some pvar -> (
match Mangled.Map.find_opt (Pvar.get_name pvar) formals_to_blocks_map with
| Some (procname, extra_formals) ->
let extra_args, load_instrs =
List.map
~f:(fun (name, typ) ->
let e = Exp.Lvar (Pvar.mk name proc_name) in
let id = Ident.create_fresh Ident.knormal in
let load_instr = Load {id; e; root_typ= typ; typ; loc} in
((Exp.Var id, typ), load_instr) )
extra_formals
|> List.unzip
in
load_instrs
@ [ Call
(ret_id_typ, Const (Cfun procname), extra_args @ converted_args, loc, call_flags)
]
| None ->
[instr] ) )
| Call (return_ids, origin_call_exp, origin_args, loc, call_flags) ->
let converted_args = List.map ~f:(fun (exp, typ) -> (exec_exp exp, typ)) origin_args in
[Call (return_ids, exec_exp origin_call_exp, converted_args, loc, call_flags)]
| Prune (origin_exp, loc, is_true_branch, if_kind) ->
[Prune (exec_exp origin_exp, loc, is_true_branch, if_kind)]
| _ ->
[instr]
in
Array.of_list res
let process summary =
let pdesc = Summary.get_proc_desc summary in
let proc_name = Procdesc.get_proc_name pdesc in
let proc_attributes = Procdesc.get_attributes pdesc in
match proc_attributes.ProcAttributes.specialized_with_blocks_info with
| Some spec_with_blocks_info -> (
match AnalysisCallbacks.get_proc_desc spec_with_blocks_info.orig_proc with
| Some orig_proc_desc -> (
let formals_to_blocks_map = spec_with_blocks_info.formals_to_procs_and_new_formals in
Procdesc.shallow_copy_code_from_pdesc ~orig_pdesc:orig_proc_desc ~dest_pdesc:pdesc ;
let analysis_data = () in
match Analyzer.compute_post ~initial:Domain.empty analysis_data pdesc with
| Some domain ->
let used_ids = Domain.bindings domain |> List.map ~f:fst in
Ident.update_name_generator used_ids ;
Procdesc.replace_instrs_by pdesc ~f:(exec_instr domain proc_name formals_to_blocks_map)
|> ignore ;
()
| None ->
() )
| _ ->
() )
| _ ->
()

@ -378,7 +378,9 @@ let do_preanalysis exe_env pdesc =
if Config.function_pointer_specialization && not (Procname.is_java proc_name) then
FunctionPointerSubstitution.process pdesc ;
(* NOTE: It is important that this preanalysis stays before Liveness *)
if not (Procname.is_java proc_name) then ClosuresSubstitution.process summary ;
if not (Procname.is_java proc_name) then (
ClosuresSubstitution.process summary ;
ClosureSubstSpecializedMethod.process summary ) ;
Liveness.process summary tenv ;
AddAbstractionInstructions.process pdesc ;
if Procname.is_java proc_name then Devirtualizer.process summary tenv ;

@ -95,6 +95,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Some typ, (Var.ProgramVar pv, _) ->
(not (Pvar.is_frontend_tmp pv))
&& (not (Procdesc.is_captured_pvar pdesc pv))
&& (not (Procdesc.has_modify_in_block_attr pdesc pv))
&& MaybeUninitVars.mem access_expr maybe_uninit_vars
&& should_report_on_type typ
| _, _ ->

@ -13,6 +13,10 @@
@end
typedef void (^MyBlock)();
void dispatch(MyBlock block) { block(); }
@implementation Singleton
// Common FP in Pulse NPEs, this requires block specialization
@ -25,6 +29,16 @@
return a->_x;
}
- (int)dispatch_no_npe_good {
static Singleton* a = nil;
static dispatch_once_t onceToken;
dispatch(^{
a = [[Singleton alloc] init];
a->_x = 5;
});
return a->_x;
}
@end
int captured_npe_bad() {

@ -74,12 +74,12 @@
if (result != nil) {
[resultsList addObject:result];
}
[ArrayUtils enumerate:^(id obj, NSUInteger idx, BOOL* stop) {
__strong __typeof(weakSelf) strongSelf = weakSelf; // no bug here
if (strongSelf) {
int x = strongSelf->x;
}
}];
}];
[ArrayUtils enumerate:^(id obj, NSUInteger idx, BOOL* stop) {
__strong __typeof(weakSelf) strongSelf = weakSelf; // no bug here
if (strongSelf) {
int x = strongSelf->x;
}
}];
return resultsList;
}

Loading…
Cancel
Save