[cost] Add FB-specific cost models

Summary: This diff adds some FB-specific cost models.

Reviewed By: ezgicicek

Differential Revision: D17787903

fbshipit-source-id: cc49fad83
master
Sungkeun Cho 5 years ago committed by Facebook Github Bot
parent d3dc73a96e
commit c509f1c178

@ -165,6 +165,10 @@ DIRECT_TESTS += \
java_topl \ java_topl \
java_tracing \ java_tracing \
ifeq ($(IS_FACEBOOK_TREE),yes)
DIRECT_TESTS += java_fb-performance
endif
ifneq ($(ANT),no) ifneq ($(ANT),no)
BUILD_SYSTEMS_TESTS += ant BUILD_SYSTEMS_TESTS += ant
endif endif

@ -721,6 +721,16 @@ module Call = struct
None None
let merge_dispatchers :
('context, 'f) dispatcher -> ('context, 'f) dispatcher -> ('context, 'f) dispatcher =
fun dispatcher1 dispatcher2 context procname args ->
match dispatcher1 context procname args with
| Some _ as r ->
r
| None ->
dispatcher2 context procname args
(* Function args *) (* Function args *)
let no_marker_checker _markers = true let no_marker_checker _markers = true

@ -228,6 +228,10 @@ module Call : sig
Common Common
with type ('context, 'f) dispatcher = 'context -> Typ.Procname.t -> FuncArg.t list -> 'f option with type ('context, 'f) dispatcher = 'context -> Typ.Procname.t -> FuncArg.t list -> 'f option
val merge_dispatchers :
('context, 'f) dispatcher -> ('context, 'f) dispatcher -> ('context, 'f) dispatcher
(** Merges two dispatchers into a dispatcher *)
type ('context, 'f_in, 'f_proc_out, 'f_out, 'captured_types, 'markers) args_matcher type ('context, 'f_in, 'f_proc_out, 'f_out, 'captured_types, 'markers) args_matcher
type ('context, 'arg_in, 'arg_out, 'f_in, 'f_out, 'captured_types, 'markers) one_arg type ('context, 'arg_in, 'arg_out, 'f_in, 'f_out, 'captured_types, 'markers) one_arg

@ -30,6 +30,9 @@ val is_subtype : Tenv.t -> Typ.Name.t -> Typ.Name.t -> bool
val is_subtype_of_str : Tenv.t -> Typ.Name.t -> string -> bool val is_subtype_of_str : Tenv.t -> Typ.Name.t -> string -> bool
(** Resolve [typ_str] in [tenv], then check [typ] <: [typ_str] *) (** Resolve [typ_str] in [tenv], then check [typ] <: [typ_str] *)
val implements : string -> Tenv.t -> string -> bool
(** Check whether class implements a given class *)
val implements_arrays : Tenv.t -> string -> bool val implements_arrays : Tenv.t -> string -> bool
(** Check whether class implements Java's Arrays *) (** Check whether class implements Java's Arrays *)

@ -9,8 +9,6 @@ open! IStd
module BasicCost = CostDomain.BasicCost module BasicCost = CostDomain.BasicCost
open BufferOverrunUtils.ModelEnv open BufferOverrunUtils.ModelEnv
type model = model_env -> ret:Ident.t * Typ.t -> BufferOverrunDomain.Mem.t -> BasicCost.t
module type S = sig module type S = sig
val length : Exp.t -> BufferOverrunDomain.Mem.t -> BufferOverrunDomain.Val.t val length : Exp.t -> BufferOverrunDomain.Mem.t -> BufferOverrunDomain.Val.t
end end
@ -27,48 +25,25 @@ module Collection : S = struct
BufferOverrunModels.Collection.eval_collection_length coll_exp inferbo_mem BufferOverrunModels.Collection.eval_collection_length coll_exp inferbo_mem
end end
let of_itv ~(itv : Itv.t) ~degree_kind ~of_function loc =
let upper_bound =
match itv with Bottom -> Bounds.Bound.pinf | NonBottom itv_pure -> Itv.ItvPure.ub itv_pure
in
Bounds.NonNegativeBound.of_modeled_function of_function loc upper_bound
|> BasicCost.of_non_negative_bound ~degree_kind
let linear exp ~of_function {integer_type_widths; location} ~ret:_ inferbo_mem = let linear exp ~of_function {integer_type_widths; location} ~ret:_ inferbo_mem =
let itv = let itv =
BufferOverrunSemantics.eval integer_type_widths exp inferbo_mem BufferOverrunSemantics.eval integer_type_widths exp inferbo_mem
|> BufferOverrunDomain.Val.get_itv |> BufferOverrunDomain.Val.get_itv
in in
of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function location CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function location
let modeled ~of_function {pname; location} ~ret:(_, ret_typ) _ : BasicCost.t = let modeled ~of_function {pname; location} ~ret:(_, ret_typ) _ : BasicCost.t =
let callsite = CallSite.make pname location in let callsite = CallSite.make pname location in
let path = Symb.SymbolPath.of_callsite ~ret_typ callsite in let path = Symb.SymbolPath.of_callsite ~ret_typ callsite in
let itv = Itv.of_modeled_path path in let itv = Itv.of_modeled_path path in
of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function location CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function location
(** Given a string of length n and an optional starting index i (0 by
default), return itv [0, n_u-i_l] *)
let string_len_range_itv model_env exp ~from mem =
let itv =
BufferOverrunModels.JavaString.get_length model_env exp mem |> BufferOverrunDomain.Val.get_itv
in
Option.value_map from ~default:itv ~f:(fun (start_exp, integer_type_widths) ->
let start_itv =
BufferOverrunSemantics.eval integer_type_widths start_exp mem
|> BufferOverrunDomain.Val.get_itv
in
Itv.minus itv start_itv )
|> Itv.set_lb_zero
module BoundsOf (Container : S) = struct module BoundsOf (Container : S) = struct
let of_length exp {location} ~ret:_ mem ~of_function ~degree_kind = let of_length exp {location} ~ret:_ mem ~of_function ~degree_kind =
let itv = Container.length exp mem |> BufferOverrunDomain.Val.get_itv in let itv = Container.length exp mem |> BufferOverrunDomain.Val.get_itv in
of_itv ~itv ~degree_kind ~of_function location CostUtils.of_itv ~itv ~degree_kind ~of_function location
let linear_length = of_length ~degree_kind:Polynomials.DegreeKind.Linear let linear_length = of_length ~degree_kind:Polynomials.DegreeKind.Linear
@ -87,7 +62,8 @@ module JavaString = struct
let itv = let itv =
Itv.minus (BufferOverrunDomain.Val.get_itv end_v) (BufferOverrunDomain.Val.get_itv begin_v) Itv.minus (BufferOverrunDomain.Val.get_itv end_v) (BufferOverrunDomain.Val.get_itv begin_v)
in in
of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function:"String.substring" location CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear
~of_function:"String.substring" location
let substring exp begin_idx model_env ~ret:_ inferbo_mem = let substring exp begin_idx model_env ~ret:_ inferbo_mem =
@ -104,17 +80,21 @@ module JavaString = struct
(** O(|m|) where m is the given string and |.| is the length function *) (** O(|m|) where m is the given string and |.| is the length function *)
let indexOf_char exp ({location} as model_env) ~ret:_ inferbo_mem = let indexOf_char exp ({location} as model_env) ~ret:_ inferbo_mem =
let itv = string_len_range_itv model_env exp ~from:None inferbo_mem in let itv = CostUtils.string_len_range_itv model_env exp ~from:None inferbo_mem in
of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function:"String.indexOf" location CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function:"String.indexOf"
location
(** O(|m|-|n|) where m is the given string and n is the index to start searching from *) (** O(|m|-|n|) where m is the given string and n is the index to start searching from *)
let indexOf_char_starting_from exp start_exp ({integer_type_widths; location} as model_env) let indexOf_char_starting_from exp start_exp ({integer_type_widths; location} as model_env)
~ret:_ inferbo_mem = ~ret:_ inferbo_mem =
let itv = let itv =
string_len_range_itv model_env exp ~from:(Some (start_exp, integer_type_widths)) inferbo_mem CostUtils.string_len_range_itv model_env exp
~from:(Some (start_exp, integer_type_widths))
inferbo_mem
in in
of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function:"String.indexOf" location CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function:"String.indexOf"
location
(** O(|m|.|n|) where m and n are the given strings *) (** O(|m|.|n|) where m and n are the given strings *)
@ -128,10 +108,11 @@ module JavaString = struct
|> BufferOverrunDomain.Val.get_itv |> BufferOverrunDomain.Val.get_itv
in in
let n = let n =
of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear ~of_function:"String.indexOf" location CostUtils.of_itv ~itv ~degree_kind:Polynomials.DegreeKind.Linear
~of_function:"String.indexOf" location
in in
let m = let m =
of_itv ~itv:index_itv ~degree_kind:Polynomials.DegreeKind.Linear CostUtils.of_itv ~itv:index_itv ~degree_kind:Polynomials.DegreeKind.Linear
~of_function:"String.indexOf" location ~of_function:"String.indexOf" location
in in
BasicCost.mult n m BasicCost.mult n m
@ -141,62 +122,65 @@ module BoundsOfCollection = BoundsOf (Collection)
module BoundsOfArray = BoundsOf (Array) module BoundsOfArray = BoundsOf (Array)
module Call = struct module Call = struct
let dispatch : (Tenv.t, model) ProcnameDispatcher.Call.dispatcher = let dispatch : (Tenv.t, CostUtils.model) ProcnameDispatcher.Call.dispatcher =
let open ProcnameDispatcher.Call in let open ProcnameDispatcher.Call in
let int_typ = Typ.mk (Typ.Tint Typ.IInt) in let int_typ = Typ.mk (Typ.Tint Typ.IInt) in
make_dispatcher let dispatcher =
[ +PatternMatch.implements_collections make_dispatcher
&:: "sort" $ capt_exp [ +PatternMatch.implements_collections
$+...$--> BoundsOfCollection.n_log_n_length ~of_function:"Collections.sort" &:: "sort" $ capt_exp
; +PatternMatch.implements_list &:: "sort" $ capt_exp $+...$--> BoundsOfCollection.n_log_n_length ~of_function:"Collections.sort"
$+...$--> BoundsOfCollection.n_log_n_length ~of_function:"List.sort" ; +PatternMatch.implements_list &:: "sort" $ capt_exp
; +PatternMatch.implements_arrays &:: "sort" $ capt_exp $+...$--> BoundsOfCollection.n_log_n_length ~of_function:"List.sort"
$+...$--> BoundsOfArray.n_log_n_length ~of_function:"Arrays.sort" ; +PatternMatch.implements_arrays &:: "sort" $ capt_exp
; +PatternMatch.implements_list &:: "contains" <>$ capt_exp $+...$--> BoundsOfArray.n_log_n_length ~of_function:"Arrays.sort"
$+...$--> BoundsOfCollection.linear_length ~of_function:"List.contains" ; +PatternMatch.implements_list &:: "contains" <>$ capt_exp
; +PatternMatch.implements_collections $+...$--> BoundsOfCollection.linear_length ~of_function:"List.contains"
&:: "binarySearch" <>$ capt_exp ; +PatternMatch.implements_collections
$+...$--> BoundsOfCollection.logarithmic_length ~of_function:"Collections.binarySearch" &:: "binarySearch" <>$ capt_exp
; +PatternMatch.implements_arrays &:: "binarySearch" <>$ capt_exp $+...$--> BoundsOfCollection.logarithmic_length ~of_function:"Collections.binarySearch"
$+...$--> BoundsOfArray.logarithmic_length ~of_function:"Arrays.binarySearch" ; +PatternMatch.implements_arrays &:: "binarySearch" <>$ capt_exp
; +PatternMatch.implements_arrays &:: "copyOf" <>$ any_arg $+ capt_exp $+...$--> BoundsOfArray.logarithmic_length ~of_function:"Arrays.binarySearch"
$+...$--> linear ~of_function:"Arrays.copyOf" ; +PatternMatch.implements_arrays &:: "copyOf" <>$ any_arg $+ capt_exp
; +PatternMatch.implements_collections $+...$--> linear ~of_function:"Arrays.copyOf"
&:: "copy" <>$ capt_exp ; +PatternMatch.implements_collections
$+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.copy" &:: "copy" <>$ capt_exp
; +PatternMatch.implements_collections $+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.copy"
&:: "fill" <>$ capt_exp ; +PatternMatch.implements_collections
$+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.fill" &:: "fill" <>$ capt_exp
; +PatternMatch.implements_arrays &:: "fill" <>$ capt_exp $+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.fill"
$+...$--> BoundsOfArray.linear_length ~of_function:"Arrays.fill" ; +PatternMatch.implements_arrays &:: "fill" <>$ capt_exp
; +PatternMatch.implements_collections $+...$--> BoundsOfArray.linear_length ~of_function:"Arrays.fill"
&:: "reverse" <>$ capt_exp ; +PatternMatch.implements_collections
$+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.reverse" &:: "reverse" <>$ capt_exp
; +PatternMatch.implements_collections $+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.reverse"
&:: "max" <>$ capt_exp ; +PatternMatch.implements_collections
$+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.max" &:: "max" <>$ capt_exp
; +PatternMatch.implements_collections $+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.max"
&:: "min" <>$ capt_exp ; +PatternMatch.implements_collections
$+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.min" &:: "min" <>$ capt_exp
; +PatternMatch.implements_collections $+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.min"
&:: "shuffle" <>$ capt_exp ; +PatternMatch.implements_collections
$+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.shuffle" &:: "shuffle" <>$ capt_exp
; +PatternMatch.implements_lang "String" $+...$--> BoundsOfCollection.linear_length ~of_function:"Collections.shuffle"
&:: "substring" <>$ capt_exp $+ capt_exp $--> JavaString.substring ; +PatternMatch.implements_lang "String"
; +PatternMatch.implements_lang "String" &:: "substring" <>$ capt_exp $+ capt_exp $--> JavaString.substring
&:: "indexOf" <>$ capt_exp ; +PatternMatch.implements_lang "String"
$+ capt_exp_of_typ (+PatternMatch.implements_lang "String") &:: "indexOf" <>$ capt_exp
$--> JavaString.indexOf_str $+ capt_exp_of_typ (+PatternMatch.implements_lang "String")
; +PatternMatch.implements_lang "String" $--> JavaString.indexOf_str
&:: "indexOf" <>$ capt_exp $+ any_arg_of_prim_typ int_typ $+ capt_exp ; +PatternMatch.implements_lang "String"
$--> JavaString.indexOf_char_starting_from &:: "indexOf" <>$ capt_exp $+ any_arg_of_prim_typ int_typ $+ capt_exp
; +PatternMatch.implements_lang "String" $--> JavaString.indexOf_char_starting_from
&:: "indexOf" <>$ capt_exp $+ any_arg_of_prim_typ int_typ $--> JavaString.indexOf_char ; +PatternMatch.implements_lang "String"
; +PatternMatch.implements_lang "String" &:: "indexOf" <>$ capt_exp $+ any_arg_of_prim_typ int_typ $--> JavaString.indexOf_char
&:: "substring" ; +PatternMatch.implements_lang "String"
$ any_arg_of_typ (+PatternMatch.implements_lang "String") &:: "substring"
$+ capt_exp $+ capt_exp $--> JavaString.substring_no_end $ any_arg_of_typ (+PatternMatch.implements_lang "String")
; +PatternMatch.implements_inject "Provider" $+ capt_exp $+ capt_exp $--> JavaString.substring_no_end
&:: "get" ; +PatternMatch.implements_inject "Provider"
<>--> modeled ~of_function:"Provider.get" ] &:: "get"
<>--> modeled ~of_function:"Provider.get" ]
in
merge_dispatchers dispatcher FbCostModels.Call.dispatch
end end

@ -0,0 +1,34 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 BasicCost = CostDomain.BasicCost
open BufferOverrunUtils.ModelEnv
type model = model_env -> ret:Ident.t * Typ.t -> BufferOverrunDomain.Mem.t -> BasicCost.t
let of_itv ~(itv : Itv.t) ~degree_kind ~of_function loc =
let upper_bound =
match itv with Bottom -> Bounds.Bound.pinf | NonBottom itv_pure -> Itv.ItvPure.ub itv_pure
in
Bounds.NonNegativeBound.of_modeled_function of_function loc upper_bound
|> BasicCost.of_non_negative_bound ~degree_kind
(** Given a string of length n and an optional starting index i (0 by
default), return itv [0, n_u-i_l] *)
let string_len_range_itv model_env exp ~from mem =
let itv =
BufferOverrunModels.JavaString.get_length model_env exp mem |> BufferOverrunDomain.Val.get_itv
in
Option.value_map from ~default:itv ~f:(fun (start_exp, integer_type_widths) ->
let start_itv =
BufferOverrunSemantics.eval integer_type_widths start_exp mem
|> BufferOverrunDomain.Val.get_itv
in
Itv.minus itv start_itv )
|> Itv.set_lb_zero

@ -0,0 +1,12 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 Call = struct
let dispatch = ProcnameDispatcher.Call.make_dispatcher []
end

@ -0,0 +1,12 @@
(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 Call : sig
val dispatch : (Tenv.t, CostUtils.model) ProcnameDispatcher.Call.dispatcher
end

@ -0,0 +1,13 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
TESTS_DIR = ../../..
INFER_OPTIONS = --cost-only --bufferoverrun --debug-exceptions --use-cost-threshold \
--report-force-relative-path
INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard *.java)
include $(TESTS_DIR)/javac.make
Loading…
Cancel
Save