diff --git a/infer/src/checkers/control.ml b/infer/src/checkers/control.ml index d67990fa2..2728e27ed 100644 --- a/infer/src/checkers/control.ml +++ b/infer/src/checkers/control.ml @@ -22,7 +22,18 @@ module L = Logging module VarSet = AbstractDomain.FiniteSet (Var) module CFG = ProcCfg.Normal -module ControlDepSet = AbstractDomain.FiniteSet (Var) +module LoopHead = Procdesc.Node + +(* For each CV, track the loop head where it is originating from. This + is needed for invariant analysis. *) +module CVar = struct + type t = {cvar: Var.t; loop_head: LoopHead.t} [@@deriving compare] + + let pp fmt {cvar; loop_head} = + F.fprintf fmt "(cvar : %a; loop_head : %a)" Var.pp cvar LoopHead.pp loop_head +end + +module ControlDepSet = AbstractDomain.FiniteSet (CVar) module GuardNodes = AbstractDomain.FiniteSet (Procdesc.Node) module LoopHeads = Procdesc.NodeSet @@ -44,28 +55,31 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct type extras = loop_control_maps - let collect_vars_in_exp exp = + let collect_vars_in_exp exp loop_head = Var.get_all_vars_in_exp exp - |> Sequence.fold ~init:ControlDepSet.empty ~f:(fun acc cvar -> ControlDepSet.add cvar acc) + |> Sequence.fold ~init:ControlDepSet.empty ~f:(fun acc cvar -> + ControlDepSet.add {cvar; loop_head} acc ) - let find_vars_in_decl id _ = function + let find_vars_in_decl id loop_head _ = function | Sil.Load (lhs_id, exp, _, _) when Ident.equal lhs_id id -> - collect_vars_in_exp exp |> Option.some + collect_vars_in_exp exp loop_head |> Option.some | Sil.Call ((lhs_id, _), _, arg_list, _, _) when Ident.equal lhs_id id -> List.fold_left arg_list ~init:ControlDepSet.empty ~f:(fun deps (exp, _) -> - collect_vars_in_exp exp |> ControlDepSet.union deps ) + collect_vars_in_exp exp loop_head |> ControlDepSet.union deps ) |> Option.some | _ -> None - let get_vars_in_exp exp prune_node = + let get_vars_in_exp exp prune_node loop_head = assert (Exp.program_vars exp |> Sequence.is_empty) ; (* We should never have program variables in prune nodes *) Exp.free_vars exp |> Sequence.fold ~init:ControlDepSet.empty ~f:(fun acc id -> - match Procdesc.Node.find_in_node_or_preds prune_node ~f:(find_vars_in_decl id) with + match + Procdesc.Node.find_in_node_or_preds prune_node ~f:(find_vars_in_decl id loop_head) + with | Some deps -> ControlDepSet.union deps acc | None -> @@ -75,7 +89,7 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct (* extract vars from the prune instructions in the node *) - let get_control_vars loop_nodes = + let get_control_vars loop_nodes loop_head = GuardNodes.fold (fun prune_node acc -> let instrs = Procdesc.Node.get_instrs prune_node in @@ -83,7 +97,7 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct ~f:(fun astate instr -> match instr with | Sil.Prune (exp, _, _, _) -> - Domain.union (get_vars_in_exp exp prune_node) astate + Domain.union (get_vars_in_exp exp prune_node loop_head) astate | _ -> (* prune nodes include other instructions like REMOVE_TEMPS or loads *) astate ) @@ -92,7 +106,8 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct (* Each time we pass through - - a loop header, add control variables of its guard nodes, + - a loop header, add control variables(CVs) of its guard nodes, + along with the loop header that CV is originating from - a loop exit node, remove control variables of its guard nodes This is correct because the CVs are only going to be temporaries. *) let exec_instr astate {ProcData.extras= {exit_map; loop_head_to_guard_nodes}} (node: CFG.Node.t) @@ -101,7 +116,7 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct let astate' = match LoopHeadToGuardNodes.find_opt node loop_head_to_guard_nodes with | Some loop_nodes -> - Domain.union astate (get_control_vars loop_nodes) + Domain.union astate (get_control_vars loop_nodes node) | _ -> astate in @@ -116,7 +131,7 @@ module TransferFunctionsControlDeps (CFG : ProcCfg.S) = struct L.(debug Analysis Medium) "@\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 ; - get_control_vars guard_nodes |> Domain.diff astate_acc + get_control_vars guard_nodes loop_head |> Domain.diff astate_acc | _ -> (* Every loop head must have a guard node *) assert false ) @@ -135,7 +150,20 @@ end module ControlDepAnalyzer = AbstractInterpreter.Make (CFG) (TransferFunctionsControlDeps) -let compute_control_vars control_invariant_map node = +(* Filter CVs which are invariant in the loop where the CV originated from *) +let remove_invariant_vars control_vars loop_inv_map = + ControlDepSet.fold + (fun {cvar; loop_head} acc -> + match LoopInvariant.LoopHeadToInvVars.find_opt loop_head loop_inv_map with + | Some inv_vars -> + if LoopInvariant.InvariantVars.mem cvar inv_vars then acc else VarSet.add cvar acc + | _ -> + (* Each cvar is always attached to a loop head *) + assert false ) + control_vars VarSet.empty + + +let compute_control_vars control_invariant_map loop_inv_map node = let node_id = CFG.Node.id node in let deps = VarSet.empty in ControlDepAnalyzer.extract_post node_id control_invariant_map @@ -144,5 +172,5 @@ let compute_control_vars control_invariant_map node = L.(debug Analysis Medium) "@\n>>> Control dependencies of node %a : %a @\n" Procdesc.Node.pp node ControlDepSet.pp control_deps ; - control_deps ) + remove_invariant_vars control_deps loop_inv_map ) |> Option.value ~default:deps diff --git a/infer/src/checkers/cost.ml b/infer/src/checkers/cost.ml index d3a68d6a6..a3f046996 100644 --- a/infer/src/checkers/cost.ml +++ b/infer/src/checkers/cost.ml @@ -144,7 +144,7 @@ module BoundMap = struct false - let compute_upperbound_map node_cfg inferbo_invariant_map control_invariant_map = + let compute_upperbound_map node_cfg inferbo_invariant_map control_invariant_map loop_inv_map = let pname = Procdesc.get_proc_name node_cfg in let formal_pvars = Procdesc.get_formals node_cfg |> List.map ~f:(fun (m, _) -> Pvar.mk m pname) @@ -162,7 +162,9 @@ module BoundMap = struct match exit_state_opt with | Some entry_mem -> (* compute control vars, i.e. set of variables that affect the execution count *) - let control_vars = Control.compute_control_vars control_invariant_map node in + let control_vars = + Control.compute_control_vars control_invariant_map loop_inv_map node + in L.(debug Analysis Medium) "@\n>>> All dependencies for node = %a : %a @\n\n" Procdesc.Node.pp node Control.VarSet.pp control_vars ; @@ -988,8 +990,14 @@ let checker ({Callbacks.tenv; proc_desc} as callback_args) : Summary.t = BufferOverrunChecker.compute_invariant_map_and_check callback_args in let node_cfg = NodeCFG.from_pdesc proc_desc in + let proc_data = ProcData.make_default proc_desc tenv in + (* computes reaching defs: node -> (var -> node set) *) + let reaching_defs_invariant_map = + ReachingDefs.Analyzer.exec_cfg node_cfg proc_data ~initial:ReachingDefs.ReachingDefsMap.empty + ~debug:true + in (* collect all prune nodes that occur in loop guards, needed for ControlDepAnalyzer *) - let control_maps = Loop_control.get_control_maps node_cfg in + let control_maps, loop_head_to_loop_nodes = Loop_control.get_control_maps node_cfg in (* computes the control dependencies: node -> var set *) let control_dep_invariant_map = let proc_data = ProcData.make proc_desc tenv control_maps in @@ -1003,9 +1011,14 @@ let checker ({Callbacks.tenv; proc_desc} as callback_args) : Summary.t = AnalyzerNodesBasicCost.exec_cfg instr_cfg proc_data ~initial:NodesBasicCostDomain.empty ~debug:true in + (* compute loop invariant map for control var analysis *) + let loop_inv_map = + LoopInvariant.get_loop_inv_var_map 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 *) let bound_map = BoundMap.compute_upperbound_map node_cfg inferbo_invariant_map control_dep_invariant_map + loop_inv_map in let pp_extra, get_node_nb_exec = let equalities = ConstraintSolver.collect_constraints node_cfg in diff --git a/infer/src/checkers/loopInvariant.ml b/infer/src/checkers/loopInvariant.ml new file mode 100644 index 000000000..de162e058 --- /dev/null +++ b/infer/src/checkers/loopInvariant.ml @@ -0,0 +1,127 @@ +(* + * Copyright (c) 2018-present, Facebook, Inc. + * + * 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 L = Logging +module InvariantVars = AbstractDomain.FiniteSet (Var) +module LoopNodes = AbstractDomain.FiniteSet (Procdesc.Node) + +(** Map loop header node -> all nodes in the loop *) +module LoopHeadToLoopNodes = Procdesc.NodeMap + +let is_defined_outside loop_nodes reaching_defs var = + ReachingDefs.ReachingDefsMap.find_opt var reaching_defs + |> Option.map ~f:(fun def_nodes -> LoopNodes.inter def_nodes loop_nodes |> LoopNodes.is_empty) + |> Option.value ~default:true + + +(* check if the def of var is unique and satisfies function f_exp *) +let is_def_unique_and_satisfy var (loop_nodes: LoopNodes.t) f_exp = + (* Use O(1) is_singleton check *) + (* tedious parameter wrangling to make IContainer's fold interface happy *) + IContainer.is_singleton + ~fold:(fun s ~init ~f -> LoopNodes.fold (fun acc x -> f x acc) s init) + loop_nodes + && LoopNodes.for_all + (fun node -> + Procdesc.Node.get_instrs node + |> Instrs.exists ~f:(function + | Sil.Load (id, exp_rhs, _, _) when Var.equal (Var.of_id id) var && f_exp exp_rhs -> + true + | Sil.Store (exp_lhs, _, exp_rhs, _) + when Exp.equal exp_lhs (Var.to_exp var) && f_exp exp_rhs -> + true (* function calls are ignored at the moment *) + | _ -> + false ) ) + loop_nodes + + +let is_exp_invariant inv_vars loop_nodes reaching_defs exp = + Var.get_all_vars_in_exp exp + |> Sequence.for_all ~f:(fun var -> + InvariantVars.mem var inv_vars || is_defined_outside loop_nodes reaching_defs var ) + + +let get_vars_in_loop loop_nodes = + LoopNodes.fold + (fun node acc -> + Procdesc.Node.get_instrs node + |> Instrs.fold + ~f:(fun acc instr -> + match instr with + | Sil.Load (id, exp_rhs, _, _) -> + Var.get_all_vars_in_exp exp_rhs + |> Sequence.fold + ~init:(InvariantVars.add (Var.of_id id) acc) + ~f:(fun acc var -> InvariantVars.add var acc) + | Sil.Store (exp_lhs, _, exp_rhs, _) -> + Var.get_all_vars_in_exp exp_rhs + |> Sequence.append (Var.get_all_vars_in_exp exp_lhs) + |> Sequence.fold ~init:acc ~f:(fun acc var -> InvariantVars.add var acc) + (* function calls are ignored at the moment *) + | _ -> + acc ) + ~init:acc ) + loop_nodes InvariantVars.empty + + +(* A variable is invariant if + - its reaching definition is outside of the loop + - o.w. its definition is constant or invariant itself *) +let get_inv_vars_in_loop reaching_defs_invariant_map loop_head loop_nodes = + let process_var_once var inv_vars = + (* if a variable is marked invariant once, it can't be invalidated + (i.e. invariance is monotonic) *) + if InvariantVars.mem var inv_vars then (inv_vars, false) + else + let loop_head_id = Procdesc.Node.get_id loop_head in + ReachingDefs.Analyzer.extract_post loop_head_id reaching_defs_invariant_map + |> Option.map ~f:(fun reaching_defs -> + ReachingDefs.ReachingDefsMap.find_opt var reaching_defs + |> Option.map ~f:(fun def_nodes -> + let in_loop_defs = LoopNodes.inter loop_nodes def_nodes in + (* reaching definition is outside of the loop *) + if LoopNodes.is_empty in_loop_defs then (InvariantVars.add var inv_vars, true) + else if + (* its definition is unique and invariant *) + is_def_unique_and_satisfy var in_loop_defs + (is_exp_invariant inv_vars loop_nodes reaching_defs) + then (InvariantVars.add var inv_vars, true) + else (inv_vars, false) ) + |> Option.value (* if a var is not declared, it must be invariant *) + ~default:(InvariantVars.add var inv_vars, true) ) + |> Option.value ~default:(inv_vars, false) + in + let vars_in_loop = get_vars_in_loop loop_nodes in + (* until there are no changes to inv_vars, keep repeatedly + processing all the variables that occur in the loop nodes *) + let rec find_fixpoint inv_vars = + let inv_vars', modified = + InvariantVars.fold + (fun var (inv_vars, is_mod) -> + let inv_vars', is_mod' = process_var_once var inv_vars in + (inv_vars', is_mod || is_mod') ) + vars_in_loop (inv_vars, false) + in + if modified then find_fixpoint inv_vars' else inv_vars' + in + find_fixpoint InvariantVars.empty + + +(** Map loop head -> invariant vars in loop *) +module LoopHeadToInvVars = Procdesc.NodeMap + +let get_loop_inv_var_map reaching_defs_invariant_map loop_head_to_loop_nodes = + LoopHeadToLoopNodes.fold + (fun loop_head loop_nodes inv_map -> + let inv_vars_in_loop = + get_inv_vars_in_loop reaching_defs_invariant_map loop_head loop_nodes + in + L.(debug Analysis Medium) + "@\n>>> loop head: %a --> inv vars: %a @\n" Procdesc.Node.pp loop_head InvariantVars.pp + inv_vars_in_loop ; + LoopHeadToInvVars.add loop_head inv_vars_in_loop inv_map ) + loop_head_to_loop_nodes LoopHeadToInvVars.empty diff --git a/infer/src/checkers/loop_control.ml b/infer/src/checkers/loop_control.ml index 717896c9f..40b4a4bf8 100644 --- a/infer/src/checkers/loop_control.ml +++ b/infer/src/checkers/loop_control.ml @@ -109,7 +109,8 @@ let get_control_maps cfg = loop_head_to_source_list ) in Procdesc.NodeMap.fold - (fun loop_head source_list Control.({exit_map; loop_head_to_guard_nodes}) -> + (fun loop_head source_list + (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) ; @@ -140,8 +141,20 @@ let get_control_maps cfg = Some guard_prune_nodes) loop_head_to_guard_nodes in - Control.{exit_map= exit_map'; loop_head_to_guard_nodes= loop_head_to_guard_nodes'} ) + 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'} + , loop_head_to_loop_nodes' ) ) loop_head_to_source_nodes_map - Control. + ( (let open Control in { exit_map= Control.ExitNodeToLoopHeads.empty - ; loop_head_to_guard_nodes= Control.LoopHeadToGuardNodes.empty } + ; loop_head_to_guard_nodes= Control.LoopHeadToGuardNodes.empty }) + , LoopInvariant.LoopHeadToLoopNodes.empty ) diff --git a/infer/src/checkers/reachingDefs.ml b/infer/src/checkers/reachingDefs.ml new file mode 100644 index 000000000..98861b69f --- /dev/null +++ b/infer/src/checkers/reachingDefs.ml @@ -0,0 +1,59 @@ +(* + * Copyright (c) 2018-present, Facebook, Inc. + * + * 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 F = Format + +(** The node in which the reaching definition x := e is defined. + + A definition x :=e, declared at node N, reaches the current node + if there is a path from node N to the current node such that x is + not modified along the path **) +module Defs = AbstractDomain.FiniteSet (Procdesc.Node) + +(* even though we only add singletons (defs), the set is needed for joins *) + +(** Map var -> its reaching definition **) +module ReachingDefsMap = AbstractDomain.Map (Var) (Defs) + +(* forward transfer function for reaching definitions *) +module TransferFunctionsReachingDefs (CFG : ProcCfg.S) = struct + module CFG = CFG + module Domain = ReachingDefsMap + + type extras = ProcData.no_extras + + (* for each x := e at node n, remove x's definitions and introduce x -> n *) + let exec_instr astate _ (node: CFG.Node.t) instr = + let node = CFG.Node.underlying_node node in + let strong_update_def astate var = Domain.add var (Defs.singleton node) astate in + let weak_update_def astate var = + Domain.update var + (function Some defs -> Some (Defs.add node defs) | None -> Some (Defs.singleton node)) + astate + in + match instr with + | 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, _, _, _) | Sil.Call ((id, _), _, _, _, _) -> + strong_update_def astate (Var.of_id id) + (* only strong update for assigning to a pvar *) + | Sil.Store (Lvar pvar, _, _, _) -> + strong_update_def astate (Var.of_pvar pvar) + (* by default use weak update *) + | Sil.Store (exp_lhs, _, _, _) -> + let vars = Var.get_all_vars_in_exp exp_lhs in + Sequence.fold ~init:astate ~f:weak_update_def vars + | _ -> + astate + + + let pp_session_name node fmt = + F.fprintf fmt "reaching defs analysis %a" CFG.Node.pp_id (CFG.Node.id node) +end + +module Analyzer = AbstractInterpreter.Make (ProcCfg.Normal) (TransferFunctionsReachingDefs) diff --git a/infer/src/istd/IContainer.ml b/infer/src/istd/IContainer.ml index e4bea2c3d..a87a8610b 100644 --- a/infer/src/istd/IContainer.ml +++ b/infer/src/istd/IContainer.ml @@ -17,6 +17,8 @@ let singleton_or_more ~fold t = match acc with Empty -> Singleton item | _ -> return More ) ) +let is_singleton ~fold t = match singleton_or_more ~fold t with Singleton _ -> true | _ -> false + let mem_nth ~fold t index = With_return.with_return (fun {return} -> let _ : int = diff --git a/infer/src/istd/IContainer.mli b/infer/src/istd/IContainer.mli index de286c021..3d0b9594a 100644 --- a/infer/src/istd/IContainer.mli +++ b/infer/src/istd/IContainer.mli @@ -13,6 +13,10 @@ type 'a singleton_or_more = Empty | Singleton of 'a | More val singleton_or_more : fold:('t, 'a, 'a singleton_or_more) Container.fold -> 't -> 'a singleton_or_more +(* O(1) *) + +val is_singleton : fold:('t, 'a, 'a singleton_or_more) Container.fold -> 't -> bool + val mem_nth : fold:('t, _, int) Container.fold -> 't -> int -> bool val forto : (int, int, 'accum) Container.fold diff --git a/infer/tests/codetoanalyze/c/performance/invariant.c b/infer/tests/codetoanalyze/c/performance/invariant.c index 1012cf3c6..3d81a532b 100644 --- a/infer/tests/codetoanalyze/c/performance/invariant.c +++ b/infer/tests/codetoanalyze/c/performance/invariant.c @@ -4,9 +4,63 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -/* We get quadratic bound for this example but we should get linear bound */ +/* Depending on whether p is positive, the program might loop forever */ +int while_unique_def_FN(int p) { + int j = 0; + while (j < 10) { + if (p > 0) { + j = 500; + } else { + j = -1; + } + } + return 0; +} + +/* Theta(n) */ void do_k_times(int n) { int k = n; for (int i = 0; i < k; i++) { } } + +/* Theta(n) */ +void do_k_times_array(int n) { + int k = n; + int a[10]; + for (int i = 0; i < k; i++) { + a[k] = 4 + k; + } +} + +/* Expected Theta(n * m), but can't handle nested loops over symbols yet */ +void do_n_m_times_nested_FP(int n, int m) { + int k = n; + int p = m; + for (int i = 0; i < k; i++) { + for (int j = 0; j < p; j++) { + } + } +} + +/* Expected Theta(m) but can't handle nested loops over symbols yet. + Also inner loop will have t as invariant */ +void two_loops_nested_invariant_FP(int p) { + int t = 0; + int m = p; + for (int i = 0; i < m; i++) { + t = 3; + for (int j = 0; j < t; j++) { + } + } +} + +/* since the guard i is invariant, we will remove it from control + variables, and hence the total cost will be constant, but the + program diverges */ +int while_infinite_FN() { + int i = 0; + while (i < 10) { + } + return 0; +} diff --git a/infer/tests/codetoanalyze/c/performance/issues.exp b/infer/tests/codetoanalyze/c/performance/issues.exp index d8dedd3e7..bd4668d9b 100644 --- a/infer/tests/codetoanalyze/c/performance/issues.exp +++ b/infer/tests/codetoanalyze/c/performance/issues.exp @@ -77,8 +77,13 @@ codetoanalyze/c/performance/instantiate.c, do_m2_times, 1, EXPENSIVE_EXECUTION_T codetoanalyze/c/performance/instantiate.c, do_m2_times, 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 3 + 10 * s$1 + 6 * s$1^2] codetoanalyze/c/performance/instantiate.c, do_n_times, 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 3 + 6 * s$1] codetoanalyze/c/performance/instantiate.c, do_n_times, 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 3 + 6 * s$1] -codetoanalyze/c/performance/invariant.c, do_k_times, 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * s$1^2] -codetoanalyze/c/performance/invariant.c, do_k_times, 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * s$1^2] +codetoanalyze/c/performance/invariant.c, do_k_times, 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * s$1] +codetoanalyze/c/performance/invariant.c, do_k_times, 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * s$1] +codetoanalyze/c/performance/invariant.c, do_k_times_array, 3, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 8 * s$1] +codetoanalyze/c/performance/invariant.c, do_k_times_array, 4, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 8 * s$1] +codetoanalyze/c/performance/invariant.c, do_n_m_times_nested_FP, 0, INFINITE_EXECUTION_TIME_CALL, no_bucket, ERROR, [] +codetoanalyze/c/performance/invariant.c, two_loops_nested_invariant_FP, 0, INFINITE_EXECUTION_TIME_CALL, no_bucket, ERROR, [] +codetoanalyze/c/performance/invariant.c, while_infinite_FN, 2, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [] codetoanalyze/c/performance/jump_inside_loop.c, jump_inside_loop, 7, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 2002] codetoanalyze/c/performance/jump_inside_loop.c, jump_inside_loop, 9, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 2002] codetoanalyze/c/performance/jump_inside_loop.c, jump_inside_loop, 11, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 2004] diff --git a/infer/tests/codetoanalyze/java/performance/issues.exp b/infer/tests/codetoanalyze/java/performance/issues.exp index 5f5db834e..e9afaa208 100644 --- a/infer/tests/codetoanalyze/java/performance/issues.exp +++ b/infer/tests/codetoanalyze/java/performance/issues.exp @@ -2,8 +2,8 @@ codetoanalyze/java/performance/ArrayCost.java, boolean ArrayCost.isPowOfTwo_FP(i codetoanalyze/java/performance/ArrayCost.java, boolean ArrayCost.isPowOfTwo_FP(int), 4, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 882] codetoanalyze/java/performance/ArrayCost.java, boolean ArrayCost.isPowOfTwo_FP(int), 5, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 882] codetoanalyze/java/performance/ArrayCost.java, boolean ArrayCost.isPowOfTwo_FP(int), 12, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 889] -codetoanalyze/java/performance/ArrayCost.java, void ArrayCost.ArrayCost(int[]), 5, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * u$7^2] -codetoanalyze/java/performance/ArrayCost.java, void ArrayCost.ArrayCost(int[]), 5, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 6 + 5 * u$7^2] +codetoanalyze/java/performance/ArrayCost.java, void ArrayCost.ArrayCost(int[]), 5, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 5 + 5 * u$7] +codetoanalyze/java/performance/ArrayCost.java, void ArrayCost.ArrayCost(int[]), 5, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 6 + 5 * u$7] codetoanalyze/java/performance/Break.java, int Break.break_constant(int), 2, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 8 + 7 * s$1] codetoanalyze/java/performance/Break.java, int Break.break_loop(int,int), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 1 + 7 * s$1] codetoanalyze/java/performance/Break.java, int Break.break_loop(int,int), 1, EXPENSIVE_EXECUTION_TIME_CALL, no_bucket, ERROR, [with estimated cost 2 + 7 * s$1]