[control][cost] Refactor

Summary:
- We hoist calculation of `loop_head_to_loop_nodes` to simplify `get_loop_control_map` and also to allow it to be used by inefficient keyset iterator without needing to compute exit maps unnecessarily.
- nit on comments
- `open Control` in `loop_control.ml`
-  hoist bound map calculation in `cost.ml`

Reviewed By: ngorogiannis

Differential Revision: D25952592

fbshipit-source-id: ef6103497
master
Ezgi Çiçek 4 years ago committed by Facebook GitHub Bot
parent d1935941a6
commit 7487676f9f

@ -49,15 +49,14 @@ module LoopHeadToGuardNodes = Procdesc.NodeMap
type loop_control_maps = type loop_control_maps =
{ exit_map: LoopHeads.t ExitNodeToLoopHeads.t { exit_map: LoopHeads.t ExitNodeToLoopHeads.t
; loop_head_to_guard_nodes: GuardNodes.t LoopHeadToGuardNodes.t ; loop_head_to_guard_nodes: GuardNodes.t LoopHeadToGuardNodes.t }
; nodes: Procdesc.Node.t list Lazy.t }
(* forward transfer function for control dependencies *) (* forward transfer function for control dependencies *)
module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct
module CFG = CFG module CFG = CFG
module Domain = ControlDepSet module Domain = ControlDepSet
type analysis_data = loop_control_maps type analysis_data = Procdesc.Node.t list * loop_control_maps
let collect_vars_in_exp exp loop_head = let collect_vars_in_exp exp loop_head =
Var.get_all_vars_in_exp exp Var.get_all_vars_in_exp exp
@ -90,27 +89,26 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct
| Some deps -> | Some deps ->
ControlDepSet.union deps acc ControlDepSet.union deps acc
| None -> ( | None -> (
(* the variables in the prone node do not appear in (* the variables in the prune node do not appear in
predecessor nodes. This could happen if the variable predecessor nodes. This could happen if the variable
is defined in a dangling node due to a frontend is defined in a dangling node due to a frontend
issue (when reading from a global variable). In that issue (when reading from a global variable). In that
case, we find that node among all the nodes of the case, we find that node among all the nodes of the
cfg and pick up the control variables. *) cfg and pick up the control variables. *)
let all_nodes = force nodes in match
match List.find_map nodes ~f:(fun node ->
List.find_map all_nodes ~f:(fun node -> if Procdesc.Node.is_dangling node then
if Procdesc.Node.is_dangling node then let instrs = Procdesc.Node.get_instrs node in
let instrs = Procdesc.Node.get_instrs node in Instrs.find_map instrs ~f:(find_vars_in_decl id loop_head prune_node)
Instrs.find_map instrs ~f:(find_vars_in_decl id loop_head prune_node) else None )
else None ) with
with | Some deps ->
| Some deps -> ControlDepSet.union deps acc
ControlDepSet.union deps acc | None ->
| None -> L.internal_error
L.internal_error "Failed to get the definition of the control variable %a in exp %a \n" Ident.pp
"Failed to get the definition of the control variable %a in exp %a \n" Ident.pp id Exp.pp exp ;
id Exp.pp exp ; acc ) )
acc ) )
(* extract vars from the prune instructions in the node *) (* extract vars from the prune instructions in the node *)
@ -135,7 +133,7 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct
along with the loop header that CV is originating from along with the loop header that CV is originating from
- a loop exit node, remove control variables of its guard nodes - a loop exit node, remove control variables of its guard nodes
This is correct because the CVs are only going to be temporaries. *) This is correct because the CVs are only going to be temporaries. *)
let exec_instr astate {exit_map; nodes; loop_head_to_guard_nodes} (node : CFG.Node.t) _ = let exec_instr astate (nodes, {exit_map; loop_head_to_guard_nodes}) (node : CFG.Node.t) _ =
let node = CFG.Node.underlying_node node in let node = CFG.Node.underlying_node node in
let astate' = let astate' =
match LoopHeadToGuardNodes.find_opt node loop_head_to_guard_nodes with match LoopHeadToGuardNodes.find_opt node loop_head_to_guard_nodes with
@ -179,7 +177,9 @@ type invariant_map = ControlDepAnalyzer.invariant_map
let compute_invariant_map proc_desc control_maps : invariant_map = let compute_invariant_map proc_desc control_maps : invariant_map =
let node_cfg = CFG.from_pdesc proc_desc in let node_cfg = CFG.from_pdesc proc_desc in
ControlDepAnalyzer.exec_cfg node_cfg control_maps ~initial:ControlDepSet.empty ControlDepAnalyzer.exec_cfg node_cfg
(Procdesc.get_nodes proc_desc, control_maps)
~initial:ControlDepSet.empty
(* Filter CVs which are invariant in the loop where the CV originated from *) (* Filter CVs which are invariant in the loop where the CV originated from *)

@ -24,8 +24,7 @@ type invariant_map
type loop_control_maps = type loop_control_maps =
{ exit_map: LoopHeads.t ExitNodeToLoopHeads.t { exit_map: LoopHeads.t ExitNodeToLoopHeads.t
; loop_head_to_guard_nodes: GuardNodes.t LoopHeadToGuardNodes.t ; loop_head_to_guard_nodes: GuardNodes.t LoopHeadToGuardNodes.t }
; nodes: Procdesc.Node.t list Lazy.t }
val compute_invariant_map : Procdesc.t -> loop_control_maps -> invariant_map val compute_invariant_map : Procdesc.t -> loop_control_maps -> invariant_map

@ -104,7 +104,9 @@ let when_dominating_preds_satisfy idom my_node ~fun_name ~class_name_f ~f =
let checker {IntraproceduralAnalysis.proc_desc; tenv; err_log} = let checker {IntraproceduralAnalysis.proc_desc; tenv; err_log} =
let cfg = CFG.from_pdesc proc_desc in let cfg = CFG.from_pdesc proc_desc in
let _, loop_head_to_loop_nodes = Loop_control.get_loop_control_maps cfg in let loop_head_to_loop_nodes =
Loop_control.(get_loop_head_to_source_nodes cfg |> get_loop_head_to_loop_nodes)
in
let idom = Dominators.get_idoms proc_desc in let idom = Dominators.get_idoms proc_desc in
Procdesc.NodeMap.iter Procdesc.NodeMap.iter
(fun loop_head loop_nodes -> (fun loop_head loop_nodes ->

@ -7,8 +7,7 @@
open! IStd open! IStd
module L = Logging module L = Logging
open Control
let nid_int n = (Procdesc.Node.get_id n :> int)
type edge_type = {source: Procdesc.Node.t; target: Procdesc.Node.t} [@@deriving compare] type edge_type = {source: Procdesc.Node.t; target: Procdesc.Node.t} [@@deriving compare]
@ -48,7 +47,7 @@ let get_back_edges pdesc =
Visually: Visually:
target target (loop head)
/| /|
/ . / .
/ | / |
@ -64,12 +63,13 @@ let get_back_edges pdesc =
Often, exit_node is a prune node. *) Often, exit_node is a prune node. *)
let get_exit_nodes_in_loop loop_nodes = let get_exit_nodes_in_loop loop_nodes =
let succs_of_loop_nodes = let succs_of_loop_nodes =
Control.GuardNodes.fold GuardNodes.fold
(fun n acc -> (fun n acc ->
Procdesc.Node.get_succs n |> Control.GuardNodes.of_list |> Control.GuardNodes.union acc ) Procdesc.Node.get_succs n
loop_nodes Control.GuardNodes.empty |> List.fold ~init:acc ~f:(fun acc succ -> GuardNodes.add succ acc) )
loop_nodes GuardNodes.empty
in in
Control.GuardNodes.diff succs_of_loop_nodes loop_nodes |> Control.GuardNodes.elements GuardNodes.diff succs_of_loop_nodes loop_nodes
(** Starting from the start_nodes, find all the nodes upwards until the target is reached, i.e (** Starting from the start_nodes, find all the nodes upwards until the target is reached, i.e
@ -79,12 +79,12 @@ let get_all_nodes_upwards_until target start_nodes =
| [] -> | [] ->
found_nodes found_nodes
| node :: wl' -> | node :: wl' ->
if Control.GuardNodes.mem node found_nodes then aux found_nodes wl' if GuardNodes.mem node found_nodes then aux found_nodes wl'
else else
let preds = Procdesc.Node.get_preds node in let preds = Procdesc.Node.get_preds node in
aux (Control.GuardNodes.add node found_nodes) (List.append preds wl') aux (GuardNodes.add node found_nodes) (List.append preds wl')
in in
aux (Control.GuardNodes.singleton target) start_nodes aux (GuardNodes.singleton target) start_nodes
let is_prune node = let is_prune node =
@ -94,16 +94,14 @@ let is_prune node =
(** Remove pairs of prune nodes that are for the same condition, i.e. sibling of the same parent. (** Remove pairs of prune nodes that are for the same condition, i.e. sibling of the same parent.
This is necessary to prevent picking unnecessary control variables in do-while like loops *) This is necessary to prevent picking unnecessary control variables in do-while like loops *)
let remove_prune_node_pairs exit_nodes guard_nodes = let remove_prune_node_pairs exit_nodes guard_nodes =
let exit_nodes = Control.GuardNodes.of_list exit_nodes in let except_exit_nodes = GuardNodes.diff guard_nodes exit_nodes in
let except_exit_nodes = Control.GuardNodes.diff guard_nodes exit_nodes in L.(debug Analysis Medium) "Except exit nodes: [%a]\n" GuardNodes.pp except_exit_nodes ;
L.(debug Analysis Medium) "Except exit nodes: [%a]\n" Control.GuardNodes.pp except_exit_nodes ;
except_exit_nodes except_exit_nodes
|> Control.GuardNodes.filter (fun node -> |> GuardNodes.filter (fun node ->
is_prune node is_prune node
&& Procdesc.Node.get_siblings node |> Sequence.hd && Procdesc.Node.get_siblings node |> Sequence.hd
|> Option.value_map ~default:false ~f:(fun sibling -> |> Option.exists ~f:(fun sibling -> not (GuardNodes.mem sibling except_exit_nodes)) )
not (Control.GuardNodes.mem sibling except_exit_nodes) ) ) |> GuardNodes.union exit_nodes
|> Control.GuardNodes.union exit_nodes
(** Since there could be multiple back-edges per loop, collect all source nodes per loop head. (** Since there could be multiple back-edges per loop, collect all source nodes per loop head.
@ -120,63 +118,60 @@ let get_loop_head_to_source_nodes cfg =
set (i.e. target of the back edges) loop_head_to_guard_map : loop_head -> guard_nodes and set (i.e. target of the back edges) loop_head_to_guard_map : loop_head -> guard_nodes and
guard_nodes contains the nodes that may affect the looping behavior, i.e. occur in the guard of guard_nodes contains the nodes that may affect the looping behavior, i.e. occur in the guard of
the loop conditional. *) the loop conditional. *)
let get_control_maps pdesc loop_head_to_source_nodes_map = let get_control_maps loop_head_to_loop_nodes =
let nodes = lazy (Procdesc.get_nodes pdesc) in let acc = (ExitNodeToLoopHeads.empty, LoopHeadToGuardNodes.empty) in
let exit_map, loop_head_to_guard_nodes =
Procdesc.NodeMap.fold
(fun loop_head loop_nodes (exit_map, loop_head_to_guard_nodes) ->
let exit_nodes = get_exit_nodes_in_loop loop_nodes in
L.(debug Analysis Medium) "Exit nodes: [%a]\n" GuardNodes.pp exit_nodes ;
(* find all the prune nodes in the loop guard *)
let guard_prune_nodes =
get_all_nodes_upwards_until loop_head (GuardNodes.elements exit_nodes)
|> remove_prune_node_pairs exit_nodes
|> GuardNodes.filter is_prune
in
let exit_map' =
GuardNodes.fold
(fun exit_node exit_map_acc ->
ExitNodeToLoopHeads.update exit_node
(function
| Some existing_loop_heads ->
Some (LoopHeads.add loop_head existing_loop_heads)
| None ->
Some (LoopHeads.singleton loop_head) )
exit_map_acc )
exit_nodes exit_map
in
let loop_head_to_guard_nodes' =
LoopHeadToGuardNodes.update loop_head
(function
| Some existing_guard_nodes ->
Some (GuardNodes.union existing_guard_nodes guard_prune_nodes)
| None ->
Some guard_prune_nodes )
loop_head_to_guard_nodes
in
(exit_map', loop_head_to_guard_nodes') )
loop_head_to_loop_nodes acc
in
{exit_map; loop_head_to_guard_nodes}
let get_loop_head_to_loop_nodes loop_head_to_source_nodes_map =
Procdesc.NodeMap.fold Procdesc.NodeMap.fold
(fun loop_head source_list (fun loop_head source_list acc ->
(Control.{exit_map; loop_head_to_guard_nodes}, loop_head_to_loop_nodes) ->
L.(debug Analysis Medium)
"Back-edge source list : [%a] --> loop_head: %i \n" (Pp.comma_seq Procdesc.Node.pp)
source_list (nid_int loop_head) ;
let loop_nodes = get_all_nodes_upwards_until loop_head source_list in let loop_nodes = get_all_nodes_upwards_until loop_head source_list in
let exit_nodes = get_exit_nodes_in_loop loop_nodes in LoopInvariant.LoopHeadToLoopNodes.update loop_head
L.(debug Analysis Medium) "Exit nodes: [%a]\n" (Pp.comma_seq Procdesc.Node.pp) exit_nodes ; (function
(* find all the prune nodes in the loop guard *) | Some existing_loop_nodes ->
let guard_prune_nodes = Some (LoopInvariant.LoopNodes.union existing_loop_nodes loop_nodes)
get_all_nodes_upwards_until loop_head exit_nodes | None ->
|> remove_prune_node_pairs exit_nodes Some loop_nodes )
|> Control.GuardNodes.filter is_prune acc )
in loop_head_to_source_nodes_map LoopInvariant.LoopHeadToLoopNodes.empty
let exit_map' =
(List.fold_left ~init:exit_map ~f:(fun exit_map_acc exit_node ->
Control.ExitNodeToLoopHeads.update exit_node let get_loop_control_maps loop_head_to_source_nodes_map =
(function let loop_head_to_loop_nodes = get_loop_head_to_loop_nodes loop_head_to_source_nodes_map in
| Some existing_loop_heads -> get_control_maps loop_head_to_loop_nodes
Some (Control.LoopHeads.add loop_head existing_loop_heads)
| None ->
Some (Control.LoopHeads.singleton loop_head) )
exit_map_acc ))
exit_nodes
in
let loop_head_to_guard_nodes' =
Control.LoopHeadToGuardNodes.update loop_head
(function
| Some existing_guard_nodes ->
Some (Control.GuardNodes.union existing_guard_nodes guard_prune_nodes)
| None ->
Some guard_prune_nodes )
loop_head_to_guard_nodes
in
let loop_head_to_loop_nodes' =
LoopInvariant.LoopHeadToLoopNodes.update loop_head
(function
| Some existing_loop_nodes ->
Some (LoopInvariant.LoopNodes.union existing_loop_nodes loop_nodes)
| None ->
Some loop_nodes )
loop_head_to_loop_nodes
in
let open Control in
( {exit_map= exit_map'; loop_head_to_guard_nodes= loop_head_to_guard_nodes'; nodes}
, loop_head_to_loop_nodes' ) )
loop_head_to_source_nodes_map
( Control.
{ exit_map= Control.ExitNodeToLoopHeads.empty
; loop_head_to_guard_nodes= Control.LoopHeadToGuardNodes.empty
; nodes }
, LoopInvariant.LoopHeadToLoopNodes.empty )
let get_loop_control_maps cfg =
let loop_head_to_source_nodes_map = get_loop_head_to_source_nodes cfg in
get_control_maps cfg loop_head_to_source_nodes_map

@ -6,8 +6,9 @@
*) *)
open! IStd open! IStd
open Control
val get_all_nodes_upwards_until : Procdesc.Node.t -> Procdesc.Node.t list -> Control.GuardNodes.t val get_all_nodes_upwards_until : Procdesc.Node.t -> Procdesc.Node.t list -> GuardNodes.t
(** Starting from the start_nodes, find all the nodes upwards until the target is reached, i.e (** Starting from the start_nodes, find all the nodes upwards until the target is reached, i.e
picking up predecessors which have not been already added to the found_nodes *) picking up predecessors which have not been already added to the found_nodes *)
@ -15,9 +16,12 @@ val get_loop_head_to_source_nodes : Procdesc.t -> Procdesc.Node.t list Procdesc.
(** Since there could be multiple back-edges per loop, collect all source nodes per loop head. (** Since there could be multiple back-edges per loop, collect all source nodes per loop head.
loop_head (target of back-edges) --> source nodes *) loop_head (target of back-edges) --> source nodes *)
val get_loop_control_maps : val get_loop_control_maps : Procdesc.Node.t list Procdesc.NodeMap.t -> loop_control_maps
Procdesc.t -> Control.loop_control_maps * Control.GuardNodes.t Procdesc.NodeMap.t
(** Get a pair of maps (exit_map, loop_head_to_guard_map) where exit_map : exit_node -> loop_head (** Get a pair of maps (exit_map, loop_head_to_guard_map) where exit_map : exit_node -> loop_head
set (i.e. target of the back edges) loop_head_to_guard_map : loop_head -> guard_nodes and set (i.e. target of the back edges) loop_head_to_guard_map : loop_head -> guard_nodes and
guard_nodes contains the nodes that may affect the looping behavior, i.e. occur in the guard of guard_nodes contains the nodes that may affect the looping behavior, i.e. occur in the guard of
the loop conditional. *) the loop conditional. *)
val get_loop_head_to_loop_nodes :
Procdesc.Node.t list Procdesc.NodeMap.t -> GuardNodes.t LoopHeadToGuardNodes.t
(** Get a map from loop head -> all the nodes included in the corresponding loop *)

@ -337,10 +337,27 @@ type bound_map = BasicCost.t Node.IdMap.t
type get_node_nb_exec = Node.t -> BasicCost.t type get_node_nb_exec = Node.t -> BasicCost.t
let compute_bound_map node_cfg inferbo_invariant_map control_dep_invariant_map loop_invmap : let compute_bound_map tenv proc_desc node_cfg inferbo_invariant_map analyze_dependency : bound_map =
bound_map = (* computes reaching defs: node -> (var -> node set) *)
let reaching_defs_invariant_map = ReachingDefs.compute_invariant_map proc_desc in
(* collect all prune nodes that occur in loop guards, needed for ControlDepAnalyzer *)
let loop_head_to_source_nodes = Loop_control.get_loop_head_to_source_nodes node_cfg in
let control_maps = Loop_control.get_loop_control_maps loop_head_to_source_nodes in
(* computes the control dependencies: node -> var set *)
let control_dep_invariant_map = Control.compute_invariant_map proc_desc control_maps in
(* compute loop invariant map for control var analysis *)
let loop_head_to_loop_nodes =
Loop_control.get_loop_head_to_loop_nodes loop_head_to_source_nodes
in
let loop_inv_map =
let get_callee_purity callee_pname =
match analyze_dependency callee_pname with Some (_, (_, _, purity)) -> purity | _ -> None
in
LoopInvariant.get_loop_inv_var_map tenv get_callee_purity reaching_defs_invariant_map
loop_head_to_loop_nodes
in
BoundMap.compute_upperbound_map node_cfg inferbo_invariant_map control_dep_invariant_map BoundMap.compute_upperbound_map node_cfg inferbo_invariant_map control_dep_invariant_map
loop_invmap loop_inv_map
let compute_get_node_nb_exec node_cfg bound_map : get_node_nb_exec = let compute_get_node_nb_exec node_cfg bound_map : get_node_nb_exec =
@ -378,23 +395,9 @@ let checker ({InterproceduralAnalysis.proc_desc; exe_env; analyze_dependency} as
(InterproceduralAnalysis.bind_payload ~f:snd3 analysis_data) (InterproceduralAnalysis.bind_payload ~f:snd3 analysis_data)
in in
let node_cfg = NodeCFG.from_pdesc proc_desc in let node_cfg = NodeCFG.from_pdesc proc_desc in
(* computes reaching defs: node -> (var -> node set) *)
let reaching_defs_invariant_map = ReachingDefs.compute_invariant_map proc_desc in
(* collect all prune nodes that occur in loop guards, needed for ControlDepAnalyzer *)
let control_maps, loop_head_to_loop_nodes = Loop_control.get_loop_control_maps node_cfg in
(* computes the control dependencies: node -> var set *)
let control_dep_invariant_map = Control.compute_invariant_map proc_desc control_maps in
(* compute loop invariant map for control var analysis *)
let loop_inv_map =
let get_callee_purity callee_pname =
match analyze_dependency callee_pname with Some (_, (_, _, purity)) -> purity | _ -> None
in
LoopInvariant.get_loop_inv_var_map tenv get_callee_purity reaching_defs_invariant_map
loop_head_to_loop_nodes
in
(* given the semantics computes the upper bound on the number of times a node could be executed *) (* given the semantics computes the upper bound on the number of times a node could be executed *)
let bound_map = let bound_map =
compute_bound_map node_cfg inferbo_invariant_map control_dep_invariant_map loop_inv_map compute_bound_map tenv proc_desc node_cfg inferbo_invariant_map analyze_dependency
in in
let is_on_ui_thread = let is_on_ui_thread =
(not (Procname.is_objc_method proc_name)) && ConcurrencyModels.runs_on_ui_thread tenv proc_name (not (Procname.is_objc_method proc_name)) && ConcurrencyModels.runs_on_ui_thread tenv proc_name

Loading…
Cancel
Save