[cost] Add cost models for loop invariant functions

Reviewed By: jvillard

Differential Revision: D14832961

fbshipit-source-id: 0d1b3353d
master
Ezgi Çiçek 6 years ago committed by Facebook Github Bot
parent 2682cdb463
commit b802620bc8

@ -297,6 +297,8 @@ module Bound = struct
let of_length_path = of_path Symb.SymbolPath.length ~unsigned:true
let of_modeled_path = of_path Symb.SymbolPath.modeled ~unsigned:true
let is_symbolic : t -> bool = function
| MInf | PInf ->
false

@ -47,6 +47,9 @@ module Bound : sig
val of_length_path :
(unsigned:bool -> Symb.SymbolPath.t -> Symb.Symbol.t) -> Symb.SymbolPath.partial -> t
val of_modeled_path :
(unsigned:bool -> Symb.SymbolPath.t -> Symb.Symbol.t) -> Symb.SymbolPath.partial -> t
val is_zero : t -> bool
val is_not_infty : t -> bool

@ -160,9 +160,11 @@ let memset arr_exp size_exp =
{exec; check}
let eval_string_len arr_exp mem = Dom.Mem.get_c_strlen (Sem.eval_locs arr_exp mem) mem
let strlen arr_exp =
let exec _ ~ret:(id, _) mem =
let v = Dom.Mem.get_c_strlen (Sem.eval_locs arr_exp mem) mem in
let v = eval_string_len arr_exp mem in
Dom.Mem.add_stack (Loc.of_id id) v mem
in
{exec; check= no_check}

@ -376,6 +376,15 @@ end
substituting reachabilities of proof obligations. *)
type eval_mode = EvalNormal | EvalPOCond | EvalPOReachability
let eval_sympath_modeled_partial ~mode p =
match (mode, p) with
| EvalNormal, Symb.SymbolPath.Callsite _ ->
Itv.of_modeled_path p |> Val.of_itv
| _, _ ->
(* We only have modeled modeled function calls created in costModels. *)
assert false
let rec eval_sympath_partial ~mode params p mem =
match p with
| Symb.SymbolPath.Pvar x -> (
@ -421,6 +430,9 @@ and eval_locpath ~mode params p mem =
let eval_sympath ~mode params sympath mem =
match sympath with
| Symb.SymbolPath.Modeled p ->
let v = eval_sympath_modeled_partial ~mode p in
(Val.get_itv v, Val.get_traces v)
| Symb.SymbolPath.Normal p ->
let v = eval_sympath_partial ~mode params p mem in
(Val.get_itv v, Val.get_traces v)

@ -436,6 +436,8 @@ module ItvPure = struct
let of_offset_path = of_path Bound.of_offset_path
let of_length_path = of_path Bound.of_length_path
let of_modeled_path = of_path Bound.of_modeled_path
end
include AbstractDomain.BottomLifted (ItvPure)
@ -658,6 +660,8 @@ let of_offset_path path = NonBottom (ItvPure.of_offset_path path)
let of_length_path path = NonBottom (ItvPure.of_length_path path)
let of_modeled_path path = NonBottom (ItvPure.of_modeled_path path)
let is_offset_path_of path x = eq (of_offset_path path) x
let is_length_path_of path x = eq (of_length_path path) x

@ -235,6 +235,8 @@ val of_offset_path : Symb.SymbolPath.partial -> t
val of_length_path : Symb.SymbolPath.partial -> t
val of_modeled_path : Symb.SymbolPath.partial -> t
val is_offset_path_of : Symb.SymbolPath.partial -> t -> bool
val is_length_path_of : Symb.SymbolPath.partial -> t -> bool

@ -33,7 +33,8 @@ module SymbolPath = struct
| Callsite of {ret_typ: Typ.t; cs: CallSite.t}
[@@deriving compare]
type t = Normal of partial | Offset of partial | Length of partial [@@deriving compare]
type t = Normal of partial | Offset of partial | Length of partial | Modeled of partial
[@@deriving compare]
let equal = [%compare.equal: t]
@ -53,6 +54,8 @@ module SymbolPath = struct
let length p = Length p
let modeled p = Modeled p
let is_this = function Pvar pvar -> Pvar.is_this pvar || Pvar.is_self pvar | _ -> false
let rec get_pvar = function
@ -92,6 +95,8 @@ module SymbolPath = struct
let pp_partial = pp_partial_paren ~paren:false
let pp fmt = function
| Modeled p ->
F.fprintf fmt "%a.modeled" pp_partial p
| Normal p ->
pp_partial fmt p
| Offset p ->
@ -146,7 +151,9 @@ module SymbolPath = struct
false
let exists_str ~f = function Normal p | Offset p | Length p -> exists_str_partial ~f p
let exists_str ~f = function
| Modeled p | Normal p | Offset p | Length p ->
exists_str_partial ~f p
end
module Symbol = struct

@ -29,7 +29,7 @@ module SymbolPath : sig
| Callsite of {ret_typ: Typ.t; cs: CallSite.t}
[@@deriving compare]
type t = private Normal of partial | Offset of partial | Length of partial
type t = private Normal of partial | Offset of partial | Length of partial | Modeled of partial
val equal_partial : partial -> partial -> bool
@ -55,6 +55,8 @@ module SymbolPath : sig
val length : partial -> t
val modeled : partial -> t
val is_this : partial -> bool
val get_pvar : partial -> Pvar.t option

@ -566,9 +566,9 @@ module InstrBasicCost = struct
List.exists allocation_functions ~f:(fun f -> Typ.Procname.equal callee_pname f)
let get_instr_cost_record extras instr_node instr =
let get_instr_cost_record tenv extras instr_node instr =
match instr with
| Sil.Call (_, Exp.Const (Const.Cfun callee_pname), params, _, _) ->
| Sil.Call (ret, Exp.Const (Const.Cfun callee_pname), params, _, _) ->
let {inferbo_invariant_map; integer_type_widths; get_callee_summary_and_formals} =
extras
in
@ -580,9 +580,14 @@ module InstrBasicCost = struct
CostDomain.unit_cost_atomic_operation
| Some inferbo_mem -> (
let loc = InstrCFG.Node.loc instr_node in
match CostModels.Call.dispatch () callee_pname params with
match CostModels.Call.dispatch tenv callee_pname params with
| Some model ->
CostDomain.of_operation_cost (model loc inferbo_mem)
let node_hash = InstrCFG.Node.hash instr_node in
let model_env =
BufferOverrunUtils.ModelEnv.mk_model_env callee_pname ~node_hash loc tenv
integer_type_widths
in
CostDomain.of_operation_cost (model model_env ~ret inferbo_mem)
| None -> (
match get_callee_summary_and_formals callee_pname with
| Some ({CostDomain.post= callee_cost_record}, callee_formals) ->
@ -612,7 +617,7 @@ module InstrBasicCost = struct
CostDomain.zero_record
let get_instr_node_cost_record extras instr_node =
let get_instr_node_cost_record tenv extras instr_node =
let instrs = InstrCFG.instrs instr_node in
let instr =
match IContainer.singleton_or_more instrs ~fold:Instrs.fold with
@ -623,7 +628,7 @@ module InstrBasicCost = struct
| More ->
assert false
in
get_instr_cost_record extras instr_node instr
get_instr_cost_record tenv extras instr_node instr
end
let compute_errlog_extras cost =
@ -663,10 +668,10 @@ module WorstCaseCost = struct
(not (BasicCost.is_top cost)) && not (BasicCost.( <= ) ~lhs:cost ~rhs:threshold)
let exec_node {costs; reports} extras instr_node =
let exec_node tenv {costs; reports} extras instr_node =
let {get_node_nb_exec} = extras in
let node_cost =
let instr_cost_record = InstrBasicCost.get_instr_node_cost_record extras instr_node in
let instr_cost_record = InstrBasicCost.get_instr_node_cost_record tenv extras instr_node in
let node_id = InstrCFG.Node.underlying_node instr_node |> Node.id in
let nb_exec = get_node_nb_exec node_id in
CostDomain.mult_by_scalar instr_cost_record nb_exec
@ -688,25 +693,25 @@ module WorstCaseCost = struct
{costs; reports}
let rec exec_partition astate extras
let rec exec_partition tenv astate extras
(partition : InstrCFG.Node.t WeakTopologicalOrder.Partition.t) =
match partition with
| Empty ->
astate
| Node {node; next} ->
let astate = exec_node astate extras node in
exec_partition astate extras next
let astate = exec_node tenv astate extras node in
exec_partition tenv astate extras next
| Component {head; rest; next} ->
let {costs; reports} = astate in
let {costs} = exec_partition {costs; reports= ThresholdReports.none} extras rest in
let {costs} = exec_partition tenv {costs; reports= ThresholdReports.none} extras rest in
(* Execute head after the loop body to always report at loop head *)
let astate = exec_node {costs; reports} extras head in
exec_partition astate extras next
let astate = exec_node tenv {costs; reports} extras head in
exec_partition tenv astate extras next
let compute extras instr_cfg_wto =
let compute tenv extras instr_cfg_wto =
let initial = {costs= CostDomain.zero_record; reports= ThresholdReports.config} in
exec_partition initial extras instr_cfg_wto
exec_partition tenv initial extras instr_cfg_wto
end
module Check = struct
@ -813,12 +818,12 @@ let compute_get_node_nb_exec node_cfg bound_map : get_node_nb_exec =
ConstraintSolver.get_node_nb_exec equalities
let compute_worst_case_cost integer_type_widths get_callee_summary_and_formals instr_cfg_wto
let compute_worst_case_cost tenv integer_type_widths get_callee_summary_and_formals instr_cfg_wto
inferbo_invariant_map get_node_nb_exec =
let extras =
{inferbo_invariant_map; integer_type_widths; get_node_nb_exec; get_callee_summary_and_formals}
in
WorstCaseCost.compute extras instr_cfg_wto
WorstCaseCost.compute tenv extras instr_cfg_wto
let get_cost_summary astate = CostDomain.{post= astate.WorstCaseCost.costs}
@ -866,7 +871,7 @@ let checker {Callbacks.tenv; proc_desc; integer_type_widths; summary} : Summary.
in
let instr_cfg = InstrCFG.from_pdesc proc_desc in
let instr_cfg_wto = InstrCFG.wto instr_cfg in
compute_worst_case_cost integer_type_widths get_callee_summary_and_formals instr_cfg_wto
compute_worst_case_cost tenv integer_type_widths get_callee_summary_and_formals instr_cfg_wto
inferbo_invariant_map get_node_nb_exec
in
let () =

@ -7,11 +7,12 @@
open! IStd
module BasicCost = CostDomain.BasicCost
open BufferOverrunUtils.ModelEnv
type model = Location.t -> BufferOverrunDomain.Mem.t -> BasicCost.t
type model = model_env -> ret:Ident.t * Typ.t -> BufferOverrunDomain.Mem.t -> BasicCost.t
module Collections = struct
let eval_collection_length coll_exp loc inferbo_mem =
let eval_collection_length coll_exp loc inferbo_mem ~of_function =
let upper_bound =
let itv =
BufferOverrunModels.Collection.eval_collection_length coll_exp inferbo_mem
@ -19,7 +20,7 @@ module Collections = struct
in
match itv with Bottom -> Bounds.Bound.pinf | NonBottom itv_pure -> Itv.ItvPure.ub itv_pure
in
Bounds.NonNegativeBound.of_modeled_function "List.length" loc upper_bound
Bounds.NonNegativeBound.of_modeled_function of_function loc upper_bound
let n_log_n b =
@ -28,13 +29,67 @@ module Collections = struct
BasicCost.mult n log_n
let sort coll_exp loc inferbo_mem =
let length = eval_collection_length coll_exp loc inferbo_mem in
let sort coll_exp {location} ~ret:_ inferbo_mem =
let length = eval_collection_length coll_exp location ~of_function:"List.length" inferbo_mem in
n_log_n length
let linear coll_exp {location} ~ret:_ inferbo_mem =
eval_collection_length coll_exp location ~of_function:"List.contains" inferbo_mem
|> BasicCost.of_non_negative_bound
end
let provider_get {pname; location} ~ret:(_, ret_typ) _ : BasicCost.t =
let callsite = CallSite.make pname location in
let path = Symb.SymbolPath.of_callsite ~ret_typ callsite in
let v =
let itv = Itv.of_modeled_path path in
match itv with Bottom -> Bounds.Bound.pinf | NonBottom itv_pure -> Itv.ItvPure.ub itv_pure
in
Bounds.NonNegativeBound.of_modeled_function "Provider.get" location v
|> BasicCost.of_non_negative_bound
module String = struct
let substring_aux ~begin_idx ~end_v {integer_type_widths; location} inferbo_mem =
let upper_bound =
let begin_v = BufferOverrunSemantics.eval integer_type_widths begin_idx inferbo_mem in
let substring_itv =
Itv.minus (BufferOverrunDomain.Val.get_itv end_v) (BufferOverrunDomain.Val.get_itv begin_v)
in
match substring_itv with
| Bottom ->
Bounds.Bound.pinf
| NonBottom itv_pure ->
Itv.ItvPure.ub itv_pure
in
Bounds.NonNegativeBound.of_modeled_function "String.substring" location upper_bound
|> BasicCost.of_non_negative_bound
let substring exp begin_idx model_env ~ret:_ inferbo_mem =
substring_aux ~begin_idx
~end_v:(BufferOverrunModels.eval_string_len exp inferbo_mem)
model_env inferbo_mem
let substring_no_end begin_idx end_idx ({integer_type_widths} as model_env) ~ret:_ inferbo_mem =
substring_aux ~begin_idx
~end_v:(BufferOverrunSemantics.eval integer_type_widths end_idx inferbo_mem)
model_env inferbo_mem
end
module Call = struct
let dispatch : (unit, model) ProcnameDispatcher.Call.dispatcher =
let dispatch : (Tenv.t, model) ProcnameDispatcher.Call.dispatcher =
let open ProcnameDispatcher.Call in
make_dispatcher [-"java.util.Collections" &:: "sort" $ capt_exp $--> Collections.sort]
make_dispatcher
[ -"java.util.Collections" &:: "sort" $ capt_exp $--> Collections.sort
; +PatternMatch.implements_list &:: "contains" <>$ capt_exp $+...$--> Collections.linear
; +PatternMatch.implements_lang "String"
&:: "substring" <>$ capt_exp $+ capt_exp $--> String.substring
; +PatternMatch.implements_lang "String"
&:: "substring"
$ any_arg_of_typ (+PatternMatch.implements_lang "String")
$+ capt_exp $+ capt_exp $--> String.substring_no_end
; +PatternMatch.implements_inject "Provider" &:: "get" <>--> provider_get ]
end

@ -10,7 +10,11 @@ module InstrCFG = ProcCfg.NormalOneInstrPerNode
module Call = struct
type t =
{loc: Location.t; pname: Typ.Procname.t; node: Procdesc.Node.t; params: (Exp.t * Typ.t) list}
{ loc: Location.t
; pname: Typ.Procname.t
; node: Procdesc.Node.t
; params: (Exp.t * Typ.t) list
; ret: Ident.t * Typ.t }
[@@deriving compare]
let pp fmt {pname; loc} =
@ -30,12 +34,12 @@ module LoopHeadToHoistInstrs = Procdesc.NodeMap
let add_if_hoistable inv_vars instr node source_nodes idom hoistable_calls =
match instr with
| Sil.Call ((ret_id, _), Exp.Const (Const.Cfun pname), params, loc, _)
| Sil.Call (((ret_id, _) as ret), Exp.Const (Const.Cfun pname), params, loc, _)
when (* Check condition (1); N dominates all loop sources *)
List.for_all ~f:(fun source -> Dominators.dominates idom node source) source_nodes
&& (* Check condition (2); id should be invariant already *)
LoopInvariant.InvariantVars.mem (Var.of_id ret_id) inv_vars ->
HoistCalls.add {pname; loc; node; params} hoistable_calls
HoistCalls.add {pname; loc; node; params; ret} hoistable_calls
| _ ->
hoistable_calls
@ -80,24 +84,32 @@ let model_satisfies ~f tenv pname =
InvariantModels.ProcName.dispatch tenv pname |> Option.exists ~f
let is_call_expensive integer_type_widths get_callee_cost_summary_and_formals inferbo_invariant_map
Call.{pname; node; params} =
(* only report if function call has expensive/symbolic cost *)
match get_callee_cost_summary_and_formals pname with
| Some (CostDomain.{post= cost_record}, callee_formals)
when CostDomain.BasicCost.is_symbolic (CostDomain.get_operation_cost cost_record) ->
let is_call_expensive tenv integer_type_widths get_callee_cost_summary_and_formals
inferbo_invariant_map (Call.{pname; node; ret; params} as call) =
let last_node = InstrCFG.last_of_underlying_node node in
let instr_node_id = InstrCFG.Node.id last_node in
let inferbo_mem =
Option.value_exn (BufferOverrunAnalysis.extract_pre instr_node_id inferbo_invariant_map)
in
(* only report if function call has expensive/symbolic cost *)
match get_callee_cost_summary_and_formals call inferbo_mem with
| Some (CostDomain.{post= cost_record}, callee_formals)
when CostDomain.BasicCost.is_symbolic (CostDomain.get_operation_cost cost_record) -> (
let loc = InstrCFG.Node.loc last_node in
let node_hash = InstrCFG.Node.hash last_node in
let model_env =
BufferOverrunUtils.ModelEnv.mk_model_env pname ~node_hash loc tenv integer_type_widths
in
(* get the cost of the function call *)
Cost.instantiate_cost integer_type_widths ~inferbo_caller_mem:inferbo_mem ~callee_pname:pname
~callee_formals ~params
match CostModels.Call.dispatch tenv pname params with
| Some model ->
model model_env ~ret inferbo_mem |> CostDomain.BasicCost.is_symbolic
| None ->
Cost.instantiate_cost integer_type_widths ~inferbo_caller_mem:inferbo_mem
~callee_pname:pname ~callee_formals ~params
~callee_cost:(CostDomain.get_operation_cost cost_record)
~loc
|> CostDomain.BasicCost.is_symbolic
|> CostDomain.BasicCost.is_symbolic )
| _ ->
false
@ -148,15 +160,30 @@ let checker Callbacks.{tenv; summary; proc_desc; integer_type_widths} : Summary.
let inferbo_invariant_map =
BufferOverrunAnalysis.cached_compute_invariant_map proc_desc tenv integer_type_widths
in
let get_callee_cost_summary_and_formals callee_pname =
Ondemand.analyze_proc_name ~caller_pdesc:proc_desc callee_pname
|> Option.bind ~f:(fun summary ->
let get_callee_cost_summary_and_formals Call.{loc; pname; params; node; ret} inferbo_mem =
let callee_pname = pname in
match Ondemand.analyze_proc_name ~caller_pdesc:proc_desc callee_pname with
| Some summary ->
summary.Summary.payloads.Payloads.cost
|> Option.map ~f:(fun cost_summary ->
(cost_summary, Summary.get_proc_desc summary |> Procdesc.get_pvar_formals) )
)
| None -> (
match CostModels.Call.dispatch tenv callee_pname params with
| Some model ->
let last_node = InstrCFG.last_of_underlying_node node in
let node_hash = InstrCFG.Node.hash last_node in
let model_env =
BufferOverrunUtils.ModelEnv.mk_model_env pname ~node_hash loc tenv
integer_type_widths
in
let callee_cost = CostDomain.of_operation_cost (model model_env ~ret inferbo_mem) in
Some
( CostDomain.{post= callee_cost}
, Summary.get_proc_desc summary |> Procdesc.get_pvar_formals )
| None ->
None )
in
is_call_expensive integer_type_widths get_callee_cost_summary_and_formals
is_call_expensive tenv integer_type_widths get_callee_cost_summary_and_formals
inferbo_invariant_map
else fun call -> not (is_call_variant_for_hoisting tenv call)
in

@ -0,0 +1,69 @@
/*
* Copyright (c) 2019-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.
*/
import java.util.ArrayList;
import javax.inject.*;
class HoistModeled {
@Inject private Provider<Integer> mProvider;
void expensive_get_hoist(int size) {
for (int i = 0; i < size; i++) {
mProvider.get();
}
}
void linear_contains_hoist(ArrayList<Integer> list, Integer el) {
int count = 0;
for (int i = 0; i < 10; i++) {
if (list.contains(el)) {
count++;
}
}
}
void constant_contains_dont_hoist(Integer el) {
boolean contains = false;
ArrayList<Integer> mylist = new ArrayList<Integer>();
mylist.add(1);
for (int i = 0; i < 10; i++) {
contains = mylist.contains(el);
}
}
void constant_substring_dont_hoist(String s) {
String sub;
for (int i = 0; i < 10; i++) {
sub = s.substring(2, 10);
}
}
void linear_substring_hoist(String s, ArrayList<Integer> list, Integer el) {
String sub;
int length = s.length();
for (int i = 0; i < 10; i++) {
sub = s.substring(2, length - 1);
}
for (int i = 0; i < 10; i++) {
sub = s.substring(1);
}
}
void call_expensive_hoist(String s, ArrayList<Integer> list, Integer el) {
for (int i = 0; i < 10; i++) {
expensive_get_hoist(10);
}
}
void expensive_get_hoist_hoist_me(String s, ArrayList<Integer> list, Integer el) {
String sub;
int length = s.length();
for (int i = 0; i < 10; i++) {
call_expensive_hoist("ez", list, el);
}
}
}

@ -6,3 +6,16 @@ codetoanalyze/java/hoistingExpensive/HoistExpensive.java, HoistExpensive.symboli
codetoanalyze/java/hoistingExpensive/HoistExpensive.java, HoistExpensive.symbolic_expensive_hoist(int):void, 2, INVARIANT_CALL, no_bucket, ERROR, [The call to void HoistExpensive.cheap_dont_hoist(int) at line 26 is loop-invariant]
codetoanalyze/java/hoistingExpensive/HoistExpensive.java, HoistExpensive.symbolic_expensive_iterator_hoist(int,java.util.ArrayList):void, 0, PURE_FUNCTION, no_bucket, ERROR, [Side-effect free function void HoistExpensive.symbolic_expensive_iterator_hoist(int,ArrayList)]
codetoanalyze/java/hoistingExpensive/HoistExpensive.java, HoistExpensive.symbolic_expensive_iterator_hoist(int,java.util.ArrayList):void, 2, INVARIANT_CALL, no_bucket, ERROR, [The call to void HoistExpensive.cheap_iterator_dont_hoist(ArrayList) at line 48 is loop-invariant]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.call_expensive_hoist(java.lang.String,java.util.ArrayList,java.lang.Integer):void, 0, PURE_FUNCTION, no_bucket, ERROR, [Side-effect free function void HoistModeled.call_expensive_hoist(String,ArrayList,Integer)]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.call_expensive_hoist(java.lang.String,java.util.ArrayList,java.lang.Integer):void, 2, INVARIANT_CALL, no_bucket, ERROR, [The call to void HoistModeled.expensive_get_hoist(int) at line 58 is loop-invariant]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.constant_contains_dont_hoist(java.lang.Integer):void, 0, PURE_FUNCTION, no_bucket, ERROR, [Side-effect free function void HoistModeled.constant_contains_dont_hoist(Integer)]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.constant_substring_dont_hoist(java.lang.String):void, 0, PURE_FUNCTION, no_bucket, ERROR, [Side-effect free function void HoistModeled.constant_substring_dont_hoist(String)]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.expensive_get_hoist(int):void, 0, PURE_FUNCTION, no_bucket, ERROR, [Side-effect free function void HoistModeled.expensive_get_hoist(int)]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.expensive_get_hoist(int):void, 2, LOOP_INVARIANT_CALL, no_bucket, ERROR, [The call to Object Provider.get() at line 16 is loop-invariant]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.expensive_get_hoist_hoist_me(java.lang.String,java.util.ArrayList,java.lang.Integer):void, 0, PURE_FUNCTION, no_bucket, ERROR, [Side-effect free function void HoistModeled.expensive_get_hoist_hoist_me(String,ArrayList,Integer)]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.expensive_get_hoist_hoist_me(java.lang.String,java.util.ArrayList,java.lang.Integer):void, 4, INVARIANT_CALL, no_bucket, ERROR, [The call to void HoistModeled.call_expensive_hoist(String,ArrayList,Integer) at line 66 is loop-invariant]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.linear_contains_hoist(java.util.ArrayList,java.lang.Integer):void, 0, PURE_FUNCTION, no_bucket, ERROR, [Side-effect free function void HoistModeled.linear_contains_hoist(ArrayList,Integer)]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.linear_contains_hoist(java.util.ArrayList,java.lang.Integer):void, 3, LOOP_INVARIANT_CALL, no_bucket, ERROR, [The call to boolean ArrayList.contains(Object) at line 23 is loop-invariant]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.linear_substring_hoist(java.lang.String,java.util.ArrayList,java.lang.Integer):void, 0, PURE_FUNCTION, no_bucket, ERROR, [Side-effect free function void HoistModeled.linear_substring_hoist(String,ArrayList,Integer)]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.linear_substring_hoist(java.lang.String,java.util.ArrayList,java.lang.Integer):void, 4, LOOP_INVARIANT_CALL, no_bucket, ERROR, [The call to String String.substring(int,int) at line 49 is loop-invariant]
codetoanalyze/java/hoistingExpensive/HoistModeled.java, HoistModeled.linear_substring_hoist(java.lang.String,java.util.ArrayList,java.lang.Integer):void, 7, LOOP_INVARIANT_CALL, no_bucket, ERROR, [The call to String String.substring(int) at line 52 is loop-invariant]

Loading…
Cancel
Save