Reviewed By: mbouaziz Differential Revision: D7586777 fbshipit-source-id: 8752679master
							parent
							
								
									cfa2dd5f83
								
							
						
					
					
						commit
						76300d55c7
					
				| @ -0,0 +1,166 @@ | |||||||
|  | (* | ||||||
|  |  * Copyright (c) 2016 - present Facebook, Inc. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * This source code is licensed under the BSD style license found in the | ||||||
|  |  * LICENSE file in the root directory of this source tree. An additional grant | ||||||
|  |  * of patent rights can be found in the PATENTS file in the same directory. | ||||||
|  |  *) | ||||||
|  | 
 | ||||||
|  | open! IStd | ||||||
|  | module F = Format | ||||||
|  | module L = Logging | ||||||
|  | (* forward dependency analysis for computing set of variables that affect the control flow at each program point | ||||||
|  |    1. perform a control flow dependency analysis CF dependency analysis by | ||||||
|  |       getting all the variables that appear in the control flow path up to that node. | ||||||
|  |    2. perform a data dependency analysis | ||||||
|  |    3. for each control dependency per node, find its respective data dependencies *) | ||||||
|  | 
 | ||||||
|  | module VarSet = AbstractDomain.FiniteSet (Var) | ||||||
|  | module DataDepSet = VarSet | ||||||
|  | module DataDepMap = AbstractDomain.Map (Var) (DataDepSet) | ||||||
|  | 
 | ||||||
|  | (* forward transfer function for collecting data dependencies of a variable *) | ||||||
|  | module TransferFunctionsDataDeps (CFG : ProcCfg.S) = struct | ||||||
|  |   module CFG = CFG | ||||||
|  |   module Domain = DataDepMap | ||||||
|  | 
 | ||||||
|  |   type extras = ProcData.no_extras | ||||||
|  | 
 | ||||||
|  |   (* compute data the dependencies in an eager way, i.e. get the transitive closure *) | ||||||
|  |   let add_data_dep_exp lhs_var exp astate = | ||||||
|  |     let add_dependency_to_var lhs_var free_var astate_acc = | ||||||
|  |       (* add lhs_var -> {free var} *) | ||||||
|  |       let deps_of_free_var = | ||||||
|  |         DataDepMap.find_opt free_var astate_acc |> Option.value ~default:DataDepSet.empty | ||||||
|  |       in | ||||||
|  |       DataDepMap.add lhs_var (DataDepSet.add free_var deps_of_free_var) astate_acc | ||||||
|  |     in | ||||||
|  |     let astate' = | ||||||
|  |       Exp.free_vars exp | ||||||
|  |       |> Sequence.fold ~init:astate ~f:(fun astate_acc id_var -> | ||||||
|  |              add_dependency_to_var lhs_var (Var.of_id id_var) astate_acc ) | ||||||
|  |     in | ||||||
|  |     Exp.program_vars exp | ||||||
|  |     |> Sequence.fold ~init:astate' ~f:(fun astate_acc pvar -> | ||||||
|  |            add_dependency_to_var lhs_var (Var.of_pvar pvar) astate_acc ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   let exec_instr astate _ _ = function | ||||||
|  |     | Sil.Load (lhs_id, _, _, _) when Ident.is_none lhs_id -> | ||||||
|  |         (* dummy deref inserted by frontend--don't count as a read *) | ||||||
|  |         astate | ||||||
|  |     | Sil.Load (id, exp, _, _) -> | ||||||
|  |         add_data_dep_exp (Var.of_id id) exp astate | ||||||
|  |     | Sil.Store (exp_lhs, _, exp_rhs, _) -> | ||||||
|  |         let astate' = | ||||||
|  |           Exp.free_vars exp_lhs | ||||||
|  |           |> Sequence.fold ~init:astate ~f:(fun astate_acc id -> | ||||||
|  |                  add_data_dep_exp (Var.of_id id) exp_rhs astate_acc ) | ||||||
|  |         in | ||||||
|  |         Exp.program_vars exp_lhs | ||||||
|  |         |> Sequence.fold ~init:astate' ~f:(fun astate_acc pvar -> | ||||||
|  |                add_data_dep_exp (Var.of_pvar pvar) exp_rhs astate_acc ) | ||||||
|  |     | Sil.Call (lhs, _, arg_list, _, _) -> ( | ||||||
|  |       match lhs with | ||||||
|  |       | Some (id, _) -> | ||||||
|  |           List.fold_left arg_list ~init:astate ~f:(fun astate_acc (exp, _) -> | ||||||
|  |               add_data_dep_exp (Var.of_id id) exp astate_acc ) | ||||||
|  |       | None -> | ||||||
|  |           astate ) | ||||||
|  |     | Sil.Prune _ | Declare_locals _ | Remove_temps _ | Abstract _ | Nullify _ -> | ||||||
|  |         astate | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   let pp_session_name node fmt = | ||||||
|  |     F.fprintf fmt "data depenedency analysis %a" CFG.pp_id (CFG.id node) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | module ControlDepSet = VarSet | ||||||
|  | 
 | ||||||
|  | (* forward transfer function for control dependencies *) | ||||||
|  | module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct | ||||||
|  |   module CFG = CFG | ||||||
|  |   module Domain = ControlDepSet | ||||||
|  | 
 | ||||||
|  |   type extras = ProcData.no_extras | ||||||
|  | 
 | ||||||
|  |   let exec_instr astate _ _ instr = | ||||||
|  |     match instr with | ||||||
|  |     | Sil.Prune (exp, _, _, _) -> | ||||||
|  |         let astate' = | ||||||
|  |           Exp.free_vars exp |> Sequence.map ~f:Var.of_id |> Sequence.to_list | ||||||
|  |           |> ControlDepSet.of_list |> Domain.union astate | ||||||
|  |         in | ||||||
|  |         Exp.program_vars exp |> Sequence.map ~f:Var.of_pvar |> Sequence.to_list | ||||||
|  |         |> ControlDepSet.of_list |> Domain.union astate' | ||||||
|  |     | Sil.Load _ | ||||||
|  |     | Sil.Store _ | ||||||
|  |     | Sil.Call _ | ||||||
|  |     | Declare_locals _ | ||||||
|  |     | Remove_temps _ | ||||||
|  |     | Abstract _ | ||||||
|  |     | Nullify _ -> | ||||||
|  |         astate | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   let pp_session_name node fmt = | ||||||
|  |     F.fprintf fmt "control depenedency analysis %a" CFG.pp_id (CFG.id node) | ||||||
|  | end | ||||||
|  | 
 | ||||||
|  | module CFG = ProcCfg.Normal | ||||||
|  | module DataDepAnalyzer = AbstractInterpreter.Make (CFG) (TransferFunctionsDataDeps) | ||||||
|  | module ControlDepAnalyzer = AbstractInterpreter.Make (CFG) (TransferFunctionsControlDeps) | ||||||
|  | 
 | ||||||
|  | let report_deps data_map = | ||||||
|  |   Sequence.iter ~f:(fun x -> | ||||||
|  |       match DataDepMap.find_opt x data_map with | ||||||
|  |       | Some d_vars -> | ||||||
|  |           L.(debug Analysis Medium) "@\n>>> var = %a --> %a  @\n\n" Var.pp x DataDepSet.pp d_vars | ||||||
|  |       | None -> | ||||||
|  |           () ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | let report_data_deps data_map node = | ||||||
|  |   List.iter (Procdesc.Node.get_instrs node) ~f:(fun instr -> | ||||||
|  |       List.iter (Sil.instr_get_exps instr) ~f:(fun exp -> | ||||||
|  |           L.(debug Analysis Medium) | ||||||
|  |             "@\n>>>Data dependencies of node = %a @\n" Procdesc.Node.pp node ; | ||||||
|  |           let free_vars = Exp.free_vars exp |> Sequence.map ~f:Var.of_id in | ||||||
|  |           let program_vars = Exp.program_vars exp |> Sequence.map ~f:Var.of_pvar in | ||||||
|  |           L.(debug Analysis Medium) "@\n>>>for exp = %a : @\n\n" Exp.pp exp ; | ||||||
|  |           report_deps data_map free_vars ; | ||||||
|  |           report_deps data_map program_vars ) ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | let report_control_deps control_map node = | ||||||
|  |   List.iter (Procdesc.Node.get_instrs node) ~f:(fun instr -> | ||||||
|  |       L.(debug Analysis Medium) "@\n>>>Control dependencies of node = %a @\n" Procdesc.Node.pp node ; | ||||||
|  |       List.iter (Sil.instr_get_exps instr) ~f:(fun exp -> | ||||||
|  |           L.(debug Analysis Medium) | ||||||
|  |             "@\n>>>for exp = %a : %a @\n\n" Exp.pp exp ControlDepSet.pp control_map ) ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | let gather_all_deps control_map data_map = | ||||||
|  |   ControlDepSet.fold | ||||||
|  |     (fun x deps -> | ||||||
|  |       DataDepMap.find_opt x data_map | ||||||
|  |       |> Option.map ~f:(fun data_deps -> | ||||||
|  |              DataDepSet.filter Var.appears_in_source_code data_deps |> DataDepSet.union deps ) | ||||||
|  |       |> Option.value ~default:deps ) | ||||||
|  |     control_map VarSet.empty | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | let compute_all_deps data_invariant_map control_invariant_map node = | ||||||
|  |   let und_node = CFG.underlying_node node in | ||||||
|  |   let node_id = Procdesc.Node.get_id und_node in | ||||||
|  |   let deps = VarSet.empty in | ||||||
|  |   ControlDepAnalyzer.extract_post node_id control_invariant_map | ||||||
|  |   |> Option.map ~f:(fun control_deps -> | ||||||
|  |          DataDepAnalyzer.extract_post node_id data_invariant_map | ||||||
|  |          |> Option.map ~f:(fun data_deps -> | ||||||
|  |                 report_data_deps data_deps node ; | ||||||
|  |                 report_control_deps control_deps node ; | ||||||
|  |                 gather_all_deps control_deps data_deps ) | ||||||
|  |          |> Option.value ~default:deps ) | ||||||
|  |   |> Option.value ~default:deps | ||||||
| @ -0,0 +1,98 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2018 - present Facebook, Inc. | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * This source code is licensed under the BSD style license found in the | ||||||
|  |  * LICENSE file in the root directory of this source tree. An additional grant | ||||||
|  |  * of patent rights can be found in the PATENTS file in the same directory. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | // Tests that exercise precision of the analysis on control variables
 | ||||||
|  | 
 | ||||||
|  | // -- We computed infinity before for the following two tests--
 | ||||||
|  | 
 | ||||||
|  | // Loop's execution count doesn't depend on values of p,t,k
 | ||||||
|  | int loop_no_dep1(int k) { | ||||||
|  |   int p = 0; | ||||||
|  |   int t = 2 + k; | ||||||
|  |   for (int i = 0; i < 100; i++) { | ||||||
|  |     p++; | ||||||
|  |   } | ||||||
|  |   return p; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int foo(int i, int j) { return i + j; } | ||||||
|  | 
 | ||||||
|  | // Loop's execution count doesn't depend on values of p,t,k
 | ||||||
|  | int loop_no_dep2(int k) { | ||||||
|  |   int p = 0; | ||||||
|  |   int t = foo(p, k); | ||||||
|  |   for (int i = 0; i < 100; i++) { | ||||||
|  |     p++; | ||||||
|  |   } | ||||||
|  |   return p; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // -- Below examples should have worked, but due to the imprecision of CF
 | ||||||
|  | // analysis, they don't
 | ||||||
|  | //
 | ||||||
|  | // This example doesn't work since for the loop
 | ||||||
|  | // control vars={p,j} and since j in [-oo.+oo], we get oo count.
 | ||||||
|  | int if_bad(int j) { | ||||||
|  |   int p = 10; | ||||||
|  |   int i = 0; | ||||||
|  |   if (p < 10 + j) { | ||||||
|  |     p++; | ||||||
|  |   } else { | ||||||
|  |     p = j + 3; | ||||||
|  |     for (int k = 0; k < 100; k++) { | ||||||
|  |       j += 3; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return p; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // We get +oo for this program, but if you take the first loop out,
 | ||||||
|  | // fake dependency disappears and we can get a proper bound
 | ||||||
|  | //
 | ||||||
|  | int two_loops() { | ||||||
|  |   int p = 10; | ||||||
|  |   int k = 3; | ||||||
|  |   int t = 2 + k; | ||||||
|  |   for (int j = 0; j < 6; j++) { | ||||||
|  |     k++; | ||||||
|  |   } | ||||||
|  |   for (int i = 0; i < 100; i++) { | ||||||
|  |     p = 3; | ||||||
|  |   } | ||||||
|  |   return p; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // We can't get program point A's execution count as 5 due to the weakness in CF
 | ||||||
|  | // analysis
 | ||||||
|  | int nested_loop() { | ||||||
|  |   int k = 0; | ||||||
|  |   for (int i = 0; i < 5; i++) { | ||||||
|  |   A: | ||||||
|  |     k = 0; | ||||||
|  |     for (int j = 0; j < 100; j++) { | ||||||
|  |       k = 3; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return k; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Unlike the above program, B will be inside the inner loop, hence executed
 | ||||||
|  | int simulated_nested_loop(int p) { | ||||||
|  |   int k = 0; | ||||||
|  |   int t = 5; | ||||||
|  |   int j = 0; | ||||||
|  |   for (int i = 0; i < 5; i++) { | ||||||
|  |   B: | ||||||
|  |     t = 3; | ||||||
|  |     j++; | ||||||
|  |     if (j < 100) | ||||||
|  |       goto B; // continue;
 | ||||||
|  |   } | ||||||
|  |   return k; | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue