[control] Fix dangling node

Summary:
Consider the below program,

```
const int gvar = 0;
enum {
  cvar = gvar + 1,

};

bool dangling_cvar(int x) {
  for (int i = 0; i < cvar; i++){
  }
}
```
In the prune node, we don't have `cvar` but its inlined version, i.e. we have  ` i < n$2 + 1` and the variable `n$2` is defined not in predecessor nodes to the prune node (as normally one would expect) but in a separate dangling node (see the {F236910156})

:

```
6: BinaryOperatorStmt:Add n$2=*&#GB<test.cpp|ice>$gvar:int [line 38, column 10]
```
When computing the control var of this loop, previously we gave up and simply raised an error.

With this diff, let's handle this case by looking inside this dangling node.

Reviewed By: skcho

Differential Revision: D21525569

fbshipit-source-id: bd4371493
master
Ezgi Çiçek 5 years ago committed by Facebook GitHub Bot
parent 2e9c1bd81f
commit 92dcbdc202

@ -191,6 +191,8 @@ module Node = struct
(** Get the predecessors of the node *) (** Get the predecessors of the node *)
let get_preds node = node.preds let get_preds node = node.preds
let is_dangling node = List.is_empty (get_preds node) && List.is_empty (get_succs node)
(** Get siblings *) (** Get siblings *)
let get_siblings node = let get_siblings node =
get_preds node get_preds node

@ -162,6 +162,9 @@ module Node : sig
val get_wto_index : t -> int val get_wto_index : t -> int
val is_dangling : t -> bool
(** Returns true if the node is dangling, i.e. no successors and predecessors *)
val hash : t -> int val hash : t -> int
(** Hash function for nodes *) (** Hash function for nodes *)

@ -49,7 +49,8 @@ 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
@ -75,7 +76,7 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct
None None
let get_vars_in_exp exp prune_node loop_head = let get_vars_in_exp nodes exp prune_node loop_head =
let program_control_vars = let program_control_vars =
Exp.program_vars exp Exp.program_vars exp
|> Sequence.fold ~init:ControlDepSet.empty ~f:(fun acc pvar -> |> Sequence.fold ~init:ControlDepSet.empty ~f:(fun acc pvar ->
@ -88,15 +89,32 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct
with with
| Some deps -> | Some deps ->
ControlDepSet.union deps acc ControlDepSet.union deps acc
| None -> | None -> (
L.internal_error (* the variables in the prone node do not appear in
"Failed to get the definition of the control variable %a in exp %a \n" Ident.pp id predecessor nodes. This could happen if the variable
Exp.pp exp ; is defined in a dangling node due to a frontend
acc ) issue (when reading from a global variable). In that
case, we find that node among all the nodes of the
cfg and pick up the control variables. *)
let all_nodes = force nodes in
match
List.find_map all_nodes ~f:(fun node ->
if Procdesc.Node.is_dangling node then
let instrs = Procdesc.Node.get_instrs node in
Instrs.find_map instrs ~f:(find_vars_in_decl id loop_head prune_node)
else None )
with
| Some deps ->
ControlDepSet.union deps acc
| None ->
L.internal_error
"Failed to get the definition of the control variable %a in exp %a \n" Ident.pp
id Exp.pp exp ;
acc ) )
(* extract vars from the prune instructions in the node *) (* extract vars from the prune instructions in the node *)
let get_control_vars loop_nodes loop_head = let get_control_vars nodes loop_nodes loop_head =
GuardNodes.fold GuardNodes.fold
(fun prune_node acc -> (fun prune_node acc ->
let instrs = Procdesc.Node.get_instrs prune_node in let instrs = Procdesc.Node.get_instrs prune_node in
@ -104,7 +122,7 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct
~f:(fun astate instr -> ~f:(fun astate instr ->
match instr with match instr with
| Sil.Prune (exp, _, _, _) -> | Sil.Prune (exp, _, _, _) ->
Domain.union (get_vars_in_exp exp prune_node loop_head) astate Domain.union (get_vars_in_exp nodes exp prune_node loop_head) astate
| _ -> | _ ->
(* prune nodes include other instructions like REMOVE_TEMPS or loads *) (* prune nodes include other instructions like REMOVE_TEMPS or loads *)
astate ) astate )
@ -117,12 +135,12 @@ 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; loop_head_to_guard_nodes} (node : CFG.Node.t) _ = let exec_instr astate {exit_map; nodes; 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
| Some loop_nodes -> | Some loop_nodes ->
Domain.union astate (get_control_vars loop_nodes node) Domain.union astate (get_control_vars nodes loop_nodes node)
| _ -> | _ ->
astate astate
in in
@ -137,7 +155,7 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct
L.(debug Analysis Medium) L.(debug Analysis Medium)
"@\n>>>Exit node %a, Loop head %a, guard nodes=%a @\n\n" Procdesc.Node.pp node "@\n>>>Exit node %a, Loop head %a, guard nodes=%a @\n\n" Procdesc.Node.pp node
Procdesc.Node.pp loop_head GuardNodes.pp guard_nodes ; Procdesc.Node.pp loop_head GuardNodes.pp guard_nodes ;
get_control_vars guard_nodes loop_head |> Domain.diff astate_acc get_control_vars nodes guard_nodes loop_head |> Domain.diff astate_acc
| _ -> | _ ->
(* Every loop head must have a guard node *) (* Every loop head must have a guard node *)
assert false ) assert false )

@ -24,7 +24,8 @@ 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

@ -120,7 +120,8 @@ 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 loop_head_to_source_nodes_map = let get_control_maps pdesc loop_head_to_source_nodes_map =
let nodes = lazy (Procdesc.get_nodes pdesc) in
Procdesc.NodeMap.fold Procdesc.NodeMap.fold
(fun loop_head source_list (fun loop_head source_list
(Control.{exit_map; loop_head_to_guard_nodes}, loop_head_to_loop_nodes) -> (Control.{exit_map; loop_head_to_guard_nodes}, loop_head_to_loop_nodes) ->
@ -166,15 +167,16 @@ let get_control_maps loop_head_to_source_nodes_map =
loop_head_to_loop_nodes loop_head_to_loop_nodes
in in
let open Control in let open Control in
( {exit_map= exit_map'; loop_head_to_guard_nodes= loop_head_to_guard_nodes'} ( {exit_map= exit_map'; loop_head_to_guard_nodes= loop_head_to_guard_nodes'; nodes}
, loop_head_to_loop_nodes' ) ) , loop_head_to_loop_nodes' ) )
loop_head_to_source_nodes_map loop_head_to_source_nodes_map
( Control. ( Control.
{ exit_map= Control.ExitNodeToLoopHeads.empty { exit_map= Control.ExitNodeToLoopHeads.empty
; loop_head_to_guard_nodes= Control.LoopHeadToGuardNodes.empty } ; loop_head_to_guard_nodes= Control.LoopHeadToGuardNodes.empty
; nodes }
, LoopInvariant.LoopHeadToLoopNodes.empty ) , LoopInvariant.LoopHeadToLoopNodes.empty )
let get_loop_control_maps cfg = let get_loop_control_maps cfg =
let loop_head_to_source_nodes_map = get_loop_head_to_source_nodes cfg in let loop_head_to_source_nodes_map = get_loop_head_to_source_nodes cfg in
get_control_maps loop_head_to_source_nodes_map get_control_maps cfg loop_head_to_source_nodes_map

Loading…
Cancel
Save