[cost] Refactoring: autorelease size models

Summary:
This diff simply changes the type of autorelease size models as `CostUtils.model` instead of
`unit`.  In the following diff, it will add a model that has more complicated semantics that does not return the unit cost.

Reviewed By: ezgicicek

Differential Revision: D23785159

fbshipit-source-id: 300808574
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent ecb409504d
commit 336e1c7b99

@ -52,10 +52,6 @@ module InstrBasicCostWithReason = struct
List.exists allocation_functions ~f:(fun f -> Procname.equal callee_pname f) List.exists allocation_functions ~f:(fun f -> Procname.equal callee_pname f)
let is_autorelease_function tenv callee_pname fun_arg_list =
CostAutoreleaseModels.Call.dispatch tenv callee_pname fun_arg_list |> Option.is_some
(** The methods whose name start with one of the prefixes return an object that is owned by the (** The methods whose name start with one of the prefixes return an object that is owned by the
caller. Therefore ARC will not add any objects they return into an autorelease pool. *) caller. Therefore ARC will not add any objects they return into an autorelease pool. *)
let return_object_owned_by_caller = let return_object_owned_by_caller =
@ -74,7 +70,7 @@ module InstrBasicCostWithReason = struct
let get_instr_cost_record tenv extras cfg instr_node instr = let get_instr_cost_record tenv extras cfg instr_node instr =
match instr with match instr with
| Sil.Call (((_, ret_typ) as ret), Exp.Const (Const.Cfun callee_pname), params, location, _) | Sil.Call (((_, ret_typ) as ret), Exp.Const (Const.Cfun callee_pname), params, location, _)
when Config.inclusive_cost -> when Config.inclusive_cost -> (
let { inferbo_invariant_map let { inferbo_invariant_map
; integer_type_widths ; integer_type_widths
; inferbo_get_summary ; inferbo_get_summary
@ -86,29 +82,30 @@ module InstrBasicCostWithReason = struct
List.map params ~f:(fun (exp, typ) -> List.map params ~f:(fun (exp, typ) ->
ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} ) ProcnameDispatcher.Call.FuncArg.{exp; typ; arg_payload= ()} )
in in
let cost = let inferbo_mem_opt =
match
BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map
with in
let model_env =
lazy
(let node_hash = InstrCFG.Node.hash instr_node in
BufferOverrunUtils.ModelEnv.mk_model_env callee_pname ~node_hash location tenv
integer_type_widths inferbo_get_summary )
in
let cost =
match inferbo_mem_opt with
| None -> | None ->
CostDomain.unit_cost_atomic_operation CostDomain.unit_cost_atomic_operation
| Some inferbo_mem -> ( | Some inferbo_mem -> (
let loc = InstrCFG.Node.loc instr_node in
match CostModels.Call.dispatch tenv callee_pname fun_arg_list with match CostModels.Call.dispatch tenv callee_pname fun_arg_list with
| Some model -> | Some model ->
let node_hash = InstrCFG.Node.hash instr_node in CostDomain.of_operation_cost (model (Lazy.force model_env) ~ret inferbo_mem)
let model_env =
BufferOverrunUtils.ModelEnv.mk_model_env callee_pname ~node_hash loc tenv
integer_type_widths inferbo_get_summary
in
CostDomain.of_operation_cost (model model_env ~ret inferbo_mem)
| None -> ( | None -> (
match (get_summary callee_pname, get_formals callee_pname) with match (get_summary callee_pname, get_formals callee_pname) with
| Some {CostDomain.post= callee_cost_record}, Some callee_formals -> ( | Some {CostDomain.post= callee_cost_record}, Some callee_formals -> (
let instantiated_cost = let instantiated_cost =
CostDomain.map callee_cost_record ~f:(fun callee_cost -> CostDomain.map callee_cost_record ~f:(fun callee_cost ->
instantiate_cost integer_type_widths ~inferbo_caller_mem:inferbo_mem instantiate_cost integer_type_widths ~inferbo_caller_mem:inferbo_mem
~callee_pname ~callee_formals ~params ~callee_cost ~loc ) ~callee_pname ~callee_formals ~params ~callee_cost ~loc:location )
in in
match CostDomain.get_operation_cost callee_cost_record with match CostDomain.get_operation_cost callee_cost_record with
| {cost; top_pname_opt= None} when BasicCost.is_top cost -> | {cost; top_pname_opt= None} when BasicCost.is_top cost ->
@ -119,8 +116,7 @@ module InstrBasicCostWithReason = struct
| _, _ -> | _, _ ->
ScubaLogging.cost_log_message ~label:"unmodeled_function_cost_analysis" ScubaLogging.cost_log_message ~label:"unmodeled_function_cost_analysis"
~message: ~message:
(F.asprintf "Unmodeled Function[Cost Analysis] : %a" Procname.pp (F.asprintf "Unmodeled Function[Cost Analysis] : %a" Procname.pp callee_pname) ;
callee_pname) ;
CostDomain.unit_cost_atomic_operation ) ) CostDomain.unit_cost_atomic_operation ) )
in in
let cost = let cost =
@ -128,12 +124,14 @@ module InstrBasicCostWithReason = struct
CostDomain.plus CostDomain.unit_cost_allocation cost CostDomain.plus CostDomain.unit_cost_allocation cost
else cost else cost
in in
if is_autorelease_function tenv callee_pname fun_arg_list then match
let autoreleasepool_trace = (inferbo_mem_opt, CostAutoreleaseModels.Call.dispatch tenv callee_pname fun_arg_list)
Bounds.BoundTrace.of_modeled_function (Procname.to_string callee_pname) location with
in | Some inferbo_mem, Some model ->
CostDomain.plus cost (CostDomain.unit_cost_autoreleasepool_size ~autoreleasepool_trace) let autoreleasepool_size = model (Lazy.force model_env) ~ret inferbo_mem in
else if CostDomain.plus_autoreleasepool_size autoreleasepool_size cost
| _, _ ->
if
is_objc_call_from_no_arc_to_arc extras cfg callee_pname is_objc_call_from_no_arc_to_arc extras cfg callee_pname
&& Typ.is_pointer_to_objc_class ret_typ && Typ.is_pointer_to_objc_class ret_typ
&& not (return_object_owned_by_caller callee_pname) && not (return_object_owned_by_caller callee_pname)
@ -141,8 +139,9 @@ module InstrBasicCostWithReason = struct
let autoreleasepool_trace = let autoreleasepool_trace =
Bounds.BoundTrace.of_arc_from_non_arc (Procname.to_string callee_pname) location Bounds.BoundTrace.of_arc_from_non_arc (Procname.to_string callee_pname) location
in in
CostDomain.plus cost (CostDomain.unit_cost_autoreleasepool_size ~autoreleasepool_trace) CostDomain.plus cost
else cost (CostDomain.unit_cost_autoreleasepool_size ~autoreleasepool_trace)
else cost )
| Sil.Call (_, Exp.Const (Const.Cfun _), _, _, _) -> | Sil.Call (_, Exp.Const (Const.Cfun _), _, _, _) ->
CostDomain.zero_record CostDomain.zero_record
| Sil.Load {id= lhs_id} when Ident.is_none lhs_id -> | Sil.Load {id= lhs_id} when Ident.is_none lhs_id ->

@ -6,19 +6,28 @@
*) *)
open! IStd open! IStd
module BasicCost = CostDomain.BasicCost
open BufferOverrunUtils.ModelEnv
let unit_cost {pname; location} ~ret:_ _inferbo_mem =
let autoreleasepool_trace =
Bounds.BoundTrace.of_modeled_function (Procname.to_string pname) location
in
BasicCost.one ~autoreleasepool_trace ()
module Call = struct module Call = struct
let dispatch : (Tenv.t, unit, unit) ProcnameDispatcher.Call.dispatcher = let dispatch : (Tenv.t, CostUtils.model, unit) ProcnameDispatcher.Call.dispatcher =
let open ProcnameDispatcher.Call in let open ProcnameDispatcher.Call in
make_dispatcher make_dispatcher
[ +PatternMatch.ObjectiveC.implements "NSObject" &:: "autorelease" &--> () [ +PatternMatch.ObjectiveC.implements "NSObject" &:: "autorelease" &--> unit_cost
; -"CFAutorelease" &--> () ; -"CFAutorelease" &--> unit_cost
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "initForReadingFromData:error:" &--> () &:: "initForReadingFromData:error:" &--> unit_cost
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "initForReadingWithData:" &--> () &:: "initForReadingWithData:" &--> unit_cost
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "unarchivedObjectOfClass:fromData:error:" &--> () &:: "unarchivedObjectOfClass:fromData:error:" &--> unit_cost
; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver" ; +PatternMatch.ObjectiveC.implements "NSKeyedUnarchiver"
&:: "unarchivedObjectOfClasses:fromData:error:" &--> () ] &:: "unarchivedObjectOfClasses:fromData:error:" &--> unit_cost ]
end end

@ -8,5 +8,5 @@
open! IStd open! IStd
module Call : sig module Call : sig
val dispatch : (Tenv.t, unit, unit) ProcnameDispatcher.Call.dispatcher val dispatch : (Tenv.t, CostUtils.model, unit) ProcnameDispatcher.Call.dispatcher
end end

@ -120,6 +120,16 @@ let plus cost_record1 cost_record2 =
cost_record1 cost_record2 cost_record1 cost_record2
let plus_autoreleasepool_size autoreleasepool_size cost =
VariantCostMap.update AutoreleasepoolSize
(function
| None ->
Some {BasicCostWithReason.cost= autoreleasepool_size; top_pname_opt= None}
| Some prev ->
Some {prev with cost= BasicCost.plus prev.cost autoreleasepool_size} )
cost
let unit_cost_atomic_operation = VariantCostMap.increment CostKind.OperationCost zero_record let unit_cost_atomic_operation = VariantCostMap.increment CostKind.OperationCost zero_record
let unit_cost_allocation = VariantCostMap.increment CostKind.AllocationCost zero_record let unit_cost_allocation = VariantCostMap.increment CostKind.AllocationCost zero_record

@ -75,6 +75,9 @@ val mult_by : t -> nb_exec:BasicCost.t -> t
val plus : t -> t -> t val plus : t -> t -> t
(** Union of two maps where common costs are added together *) (** Union of two maps where common costs are added together *)
val plus_autoreleasepool_size : BasicCost.t -> t -> t
(** Add an autoreleasepool size to the cost map *)
val unit_cost_atomic_operation : t val unit_cost_atomic_operation : t
(** Map representing cost record \{OperationCost:1; AllocationCost:0; AutoreleasepoolSize:0\} *) (** Map representing cost record \{OperationCost:1; AllocationCost:0; AutoreleasepoolSize:0\} *)

Loading…
Cancel
Save