[cost] Increase autoreleasepool size when non-ARC call ARC

Summary:
This diff increases autoreleasepool size when

* caller is non-ARC-compiled
* callee is ARC-compiled
* return type is a pointer to objc object

To distinguish non-ARC-/ARC-compiled:

* extended `translation_unit_decl_info` to have a boolean field `is_objc_arc_on`
* then copied it to `ProcAttributes.t` for each procedures.

Reviewed By: ezgicicek

Differential Revision: D23565003

fbshipit-source-id: dee22ea82
master
Sungkeun Cho 4 years ago committed by Facebook GitHub Bot
parent e1806cbbb2
commit 76ad9915a1

@ -1360,6 +1360,7 @@ void ASTExporter<ATDWriter>::dumpIntegerTypeWidths(const TargetInfo &info) {
//@atd input_path : source_file; //@atd input_path : source_file;
//@atd input_kind : input_kind; //@atd input_kind : input_kind;
//@atd integer_type_widths : integer_type_widths; //@atd integer_type_widths : integer_type_widths;
//@atd ~is_objc_arc_on : bool;
//@atd types : c_type list; //@atd types : c_type list;
//@atd } <ocaml field_prefix="tudi_"> //@atd } <ocaml field_prefix="tudi_">
template <class ATDWriter> template <class ATDWriter>
@ -1367,7 +1368,8 @@ void ASTExporter<ATDWriter>::VisitTranslationUnitDecl(
const TranslationUnitDecl *D) { const TranslationUnitDecl *D) {
VisitDecl(D); VisitDecl(D);
VisitDeclContext(D); VisitDeclContext(D);
ObjectScope Scope(OF, 4); bool IsObjCArcOn = D->getASTContext().getLangOpts().ObjCAutoRefCount;
ObjectScope Scope(OF, 4 + IsObjCArcOn);
OF.emitTag("input_path"); OF.emitTag("input_path");
OF.emitString( OF.emitString(
Options.normalizeSourcePath(Options.inputFile.getFile().str().c_str())); Options.normalizeSourcePath(Options.inputFile.getFile().str().c_str()));
@ -1375,6 +1377,7 @@ void ASTExporter<ATDWriter>::VisitTranslationUnitDecl(
dumpInputKind(Options.inputFile.getKind()); dumpInputKind(Options.inputFile.getKind());
OF.emitTag("integer_type_widths"); OF.emitTag("integer_type_widths");
dumpIntegerTypeWidths(Context.getTargetInfo()); dumpIntegerTypeWidths(Context.getTargetInfo());
OF.emitFlag("is_objc_arc_on", IsObjCArcOn);
OF.emitTag("types"); OF.emitTag("types");
const auto &types = Context.getTypes(); const auto &types = Context.getTypes();
ArrayScope aScope(OF, types.size() + 1); // + 1 for nullptr ArrayScope aScope(OF, types.size() + 1); // + 1 for nullptr

@ -1363,6 +1363,7 @@
#d7cd079d: 64, #d7cd079d: 64,
#048ad2a1: 64 #048ad2a1: 64
}, },
#247f0baa: true,
#1acb7079: [ #1acb7079: [
<#22d546dd: ({ #d121c0bd: 67 }, <#392cef74>)>, <#22d546dd: ({ #d121c0bd: 67 }, <#392cef74>)>,
<#22d546dd: ({ #d121c0bd: 122 }, <#2bf4b04a>)>, <#22d546dd: ({ #d121c0bd: 122 }, <#2bf4b04a>)>,

@ -1015,6 +1015,7 @@
#d7cd079d: 64, #d7cd079d: 64,
#048ad2a1: 64 #048ad2a1: 64
}, },
#247f0baa: true,
#1acb7079: [ #1acb7079: [
<#22d546dd: ({ #d121c0bd: 75 }, <#392cef74>)>, <#22d546dd: ({ #d121c0bd: 75 }, <#392cef74>)>,
<#22d546dd: ({ #d121c0bd: 92 }, <#2bf4b04a>)>, <#22d546dd: ({ #d121c0bd: 92 }, <#2bf4b04a>)>,

@ -9420,6 +9420,7 @@
#d7cd079d: 64, #d7cd079d: 64,
#048ad2a1: 64 #048ad2a1: 64
}, },
#247f0baa: true,
#1acb7079: [ #1acb7079: [
<#22d546dd: ({ #d121c0bd: 69 }, <#392cef74>)>, <#22d546dd: ({ #d121c0bd: 69 }, <#392cef74>)>,
<#22d546dd: ({ #d121c0bd: 373 }, <#2bf4b04a>)>, <#22d546dd: ({ #d121c0bd: 373 }, <#2bf4b04a>)>,

@ -303,6 +303,7 @@ tests/available_expression.m:8:20: note: add a super class to fix this problem
#d7cd079d: 64, #d7cd079d: 64,
#048ad2a1: 64 #048ad2a1: 64
}, },
#247f0baa: true,
#1acb7079: [ #1acb7079: [
<#22d546dd: ({ #d121c0bd: 18 }, <#392cef74>)>, <#22d546dd: ({ #d121c0bd: 18 }, <#392cef74>)>,
<#22d546dd: ({ #d121c0bd: 28 }, <#2bf4b04a>)>, <#22d546dd: ({ #d121c0bd: 28 }, <#2bf4b04a>)>,

@ -91,6 +91,7 @@
#d7cd079d: 64, #d7cd079d: 64,
#048ad2a1: 64 #048ad2a1: 64
}, },
#247f0baa: true,
#1acb7079: [ #1acb7079: [
<#22d546dd: ({ #d121c0bd: 15 }, <#392cef74>)>, <#22d546dd: ({ #d121c0bd: 15 }, <#392cef74>)>,
<#22d546dd: ({ #d121c0bd: 16 }, <#2bf4b04a>)>, <#22d546dd: ({ #d121c0bd: 16 }, <#2bf4b04a>)>,

@ -777,6 +777,7 @@
#d7cd079d: 64, #d7cd079d: 64,
#048ad2a1: 64 #048ad2a1: 64
}, },
#247f0baa: true,
#1acb7079: [ #1acb7079: [
<#22d546dd: ({ #d121c0bd: 17 }, <#392cef74>)>, <#22d546dd: ({ #d121c0bd: 17 }, <#392cef74>)>,
<#22d546dd: ({ #d121c0bd: 48 }, <#2bf4b04a>)>, <#22d546dd: ({ #d121c0bd: 48 }, <#2bf4b04a>)>,

@ -2600,6 +2600,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
["BuiltinType" , [ ["BuiltinType" , [
{ {

@ -1983,6 +1983,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
["BuiltinType" , [ ["BuiltinType" , [
{ {

@ -12362,6 +12362,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
["BuiltinType" , [ ["BuiltinType" , [
{ {

@ -260,6 +260,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
["BuiltinType" , [ ["BuiltinType" , [
{ {

@ -1302,6 +1302,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
["BuiltinType" , [ ["BuiltinType" , [
{ {

@ -2600,6 +2600,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
<"BuiltinType" : ( <"BuiltinType" : (
{ {

@ -1983,6 +1983,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
<"BuiltinType" : ( <"BuiltinType" : (
{ {

@ -12362,6 +12362,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
<"BuiltinType" : ( <"BuiltinType" : (
{ {

@ -676,6 +676,7 @@ tests/available_expression.m:8:20: note: add a super class to fix this problem
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
<"BuiltinType" : ( <"BuiltinType" : (
{ {

@ -260,6 +260,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
<"BuiltinType" : ( <"BuiltinType" : (
{ {

@ -1302,6 +1302,7 @@
"long_type" : 64, "long_type" : 64,
"longlong_type" : 64 "longlong_type" : 64
}, },
"is_objc_arc_on" : true,
"types" : [ "types" : [
<"BuiltinType" : ( <"BuiltinType" : (
{ {

@ -59,6 +59,7 @@ type t =
(** Present if the procedure is an Objective-C block that has been passed to the given (** Present if the procedure is an Objective-C block that has been passed to the given
method in a position annotated with the NS_NOESCAPE attribute. *) method in a position annotated with the NS_NOESCAPE attribute. *)
; is_no_return: bool (** the procedure is known not to return *) ; is_no_return: bool (** the procedure is known not to return *)
; is_objc_arc_on: bool (** the ObjC procedure is compiled with ARC *)
; is_specialized: bool (** the procedure is a clone specialized for dynamic dispatch handling *) ; is_specialized: bool (** the procedure is a clone specialized for dynamic dispatch handling *)
; is_synthetic_method: bool (** the procedure is a synthetic method *) ; is_synthetic_method: bool (** the procedure is a synthetic method *)
; is_variadic: bool (** the procedure is variadic, only supported for Clang procedures *) ; is_variadic: bool (** the procedure is variadic, only supported for Clang procedures *)
@ -123,6 +124,7 @@ let default translation_unit proc_name =
; is_java_synchronized_method= false ; is_java_synchronized_method= false
; passed_as_noescape_block_to= None ; passed_as_noescape_block_to= None
; is_no_return= false ; is_no_return= false
; is_objc_arc_on= false
; is_specialized= false ; is_specialized= false
; specialized_with_blocks_info= None ; specialized_with_blocks_info= None
; is_synthetic_method= false ; is_synthetic_method= false
@ -173,6 +175,7 @@ let pp f
; is_java_synchronized_method ; is_java_synchronized_method
; passed_as_noescape_block_to ; passed_as_noescape_block_to
; is_no_return ; is_no_return
; is_objc_arc_on
; is_specialized ; is_specialized
; specialized_with_blocks_info ; specialized_with_blocks_info
; is_synthetic_method ; is_synthetic_method
@ -221,6 +224,7 @@ let pp f
F.fprintf f "; passed_as_noescape_block_to %a" (Pp.option Procname.pp) F.fprintf f "; passed_as_noescape_block_to %a" (Pp.option Procname.pp)
passed_as_noescape_block_to ; passed_as_noescape_block_to ;
pp_bool_default ~default:default.is_no_return "is_no_return" is_no_return f () ; pp_bool_default ~default:default.is_no_return "is_no_return" is_no_return f () ;
pp_bool_default ~default:default.is_objc_arc_on "is_objc_arc_on" is_objc_arc_on f () ;
pp_bool_default ~default:default.is_specialized "is_specialized" is_specialized f () ; pp_bool_default ~default:default.is_specialized "is_specialized" is_specialized f () ;
if if
not not

@ -41,6 +41,7 @@ type t =
(** Present if the procedure is an Objective-C block that has been passed to the given (** Present if the procedure is an Objective-C block that has been passed to the given
method in a position annotated with the NS_NOESCAPE attribute. *) method in a position annotated with the NS_NOESCAPE attribute. *)
; is_no_return: bool (** the procedure is known not to return *) ; is_no_return: bool (** the procedure is known not to return *)
; is_objc_arc_on: bool (** the ObjC procedure is compiled with ARC *)
; is_specialized: bool (** the procedure is a clone specialized for dynamic dispatch handling *) ; is_specialized: bool (** the procedure is a clone specialized for dynamic dispatch handling *)
; is_synthetic_method: bool (** the procedure is a synthetic method *) ; is_synthetic_method: bool (** the procedure is a synthetic method *)
; is_variadic: bool (** the procedure is variadic, only supported for Clang procedures *) ; is_variadic: bool (** the procedure is variadic, only supported for Clang procedures *)

@ -529,6 +529,8 @@ let is_defined pdesc = pdesc.attributes.is_defined
let is_java_synchronized pdesc = pdesc.attributes.is_java_synchronized_method let is_java_synchronized pdesc = pdesc.attributes.is_java_synchronized_method
let is_objc_arc_on pdesc = pdesc.attributes.is_objc_arc_on
let iter_nodes f pdesc = List.iter ~f (get_nodes pdesc) let iter_nodes f pdesc = List.iter ~f (get_nodes pdesc)
let iter_instrs f pdesc = let iter_instrs f pdesc =

@ -271,6 +271,9 @@ val is_defined : t -> bool
val is_java_synchronized : t -> bool val is_java_synchronized : t -> bool
(** Return [true] if the procedure signature has the Java synchronized keyword *) (** Return [true] if the procedure signature has the Java synchronized keyword *)
val is_objc_arc_on : t -> bool
(** Return [true] iff the ObjC procedure is compiled with ARC *)
val iter_instrs : (Node.t -> Sil.instr -> unit) -> t -> unit val iter_instrs : (Node.t -> Sil.instr -> unit) -> t -> unit
(** iterate over all nodes and their instructions *) (** iterate over all nodes and their instructions *)

@ -569,6 +569,8 @@ let is_struct typ = match typ.desc with Tstruct _ -> true | _ -> false
let is_pointer_to_cpp_class typ = match typ.desc with Tptr (t, _) -> is_cpp_class t | _ -> false let is_pointer_to_cpp_class typ = match typ.desc with Tptr (t, _) -> is_cpp_class t | _ -> false
let is_pointer_to_objc_class typ = match typ.desc with Tptr (t, _) -> is_objc_class t | _ -> false
let is_pointer_to_void typ = match typ.desc with Tptr ({desc= Tvoid}, _) -> true | _ -> false let is_pointer_to_void typ = match typ.desc with Tptr ({desc= Tvoid}, _) -> true | _ -> false
let is_void typ = match typ.desc with Tvoid -> true | _ -> false let is_void typ = match typ.desc with Tvoid -> true | _ -> false

@ -324,6 +324,8 @@ val is_cpp_class : t -> bool
val is_pointer_to_cpp_class : t -> bool val is_pointer_to_cpp_class : t -> bool
val is_pointer_to_objc_class : t -> bool
val is_pointer_to_void : t -> bool val is_pointer_to_void : t -> bool
val is_void : t -> bool val is_void : t -> bool

@ -1291,10 +1291,11 @@ module BoundTrace = struct
| Loop of Location.t | Loop of Location.t
| Call of {callee_pname: Procname.t; callee_trace: t; location: Location.t} | Call of {callee_pname: Procname.t; callee_trace: t; location: Location.t}
| ModeledFunction of {pname: string; location: Location.t} | ModeledFunction of {pname: string; location: Location.t}
| ArcFromNonArc of {pname: string; location: Location.t}
[@@deriving compare] [@@deriving compare]
let rec length = function let rec length = function
| Loop _ | ModeledFunction _ -> | Loop _ | ModeledFunction _ | ArcFromNonArc _ ->
1 1
| Call {callee_trace} -> | Call {callee_trace} ->
1 + length callee_trace 1 + length callee_trace
@ -1309,6 +1310,8 @@ module BoundTrace = struct
F.fprintf f "Loop (%a)" Location.pp loc F.fprintf f "Loop (%a)" Location.pp loc
| ModeledFunction {pname; location} -> | ModeledFunction {pname; location} ->
F.fprintf f "ModeledFunction `%s` (%a)" pname Location.pp location F.fprintf f "ModeledFunction `%s` (%a)" pname Location.pp location
| ArcFromNonArc {pname; location} ->
F.fprintf f "ArcFromNonArc `%s` (%a)" pname Location.pp location
| Call {callee_pname; callee_trace; location} -> | Call {callee_pname; callee_trace; location} ->
F.fprintf f "%a -> Call `%a` (%a)" pp callee_trace Procname.pp callee_pname Location.pp F.fprintf f "%a -> Call `%a` (%a)" pp callee_trace Procname.pp callee_pname Location.pp
location location
@ -1327,11 +1330,16 @@ module BoundTrace = struct
| ModeledFunction {pname; location} -> | ModeledFunction {pname; location} ->
let desc = F.asprintf "Modeled call to %s" pname in let desc = F.asprintf "Modeled call to %s" pname in
[Errlog.make_trace_element depth location desc []] [Errlog.make_trace_element depth location desc []]
| ArcFromNonArc {pname; location} ->
let desc = F.asprintf "ARC function call to %s from non-ARC caller" pname in
[Errlog.make_trace_element depth location desc []]
let of_loop location = Loop location let of_loop location = Loop location
let of_modeled_function pname location = ModeledFunction {pname; location} let of_modeled_function pname location = ModeledFunction {pname; location}
let of_arc_from_non_arc pname location = ArcFromNonArc {pname; location}
end end
(** A NonNegativeBound is a Bound that is either non-negative or symbolic but will be evaluated to a (** A NonNegativeBound is a Bound that is either non-negative or symbolic but will be evaluated to a

@ -156,6 +156,8 @@ module BoundTrace : sig
val of_loop : Location.t -> t val of_loop : Location.t -> t
val of_modeled_function : string -> Location.t -> t val of_modeled_function : string -> Location.t -> t
val of_arc_from_non_arc : string -> Location.t -> t
end end
type ('c, 's, 't) valclass = Constant of 'c | Symbolic of 's | ValTop of 't type ('c, 's, 't) valclass = Constant of 'c | Symbolic of 's | ValTop of 't

@ -69,7 +69,8 @@ let run_clang_frontend ast_source =
; long_width= widths.itw_long_type ; long_width= widths.itw_long_type
; longlong_width= widths.itw_longlong_type } ; longlong_width= widths.itw_longlong_type }
in in
{CFrontend_config.source_file; lang; integer_type_widths} let is_objc_arc_on = info.Clang_ast_t.tudi_is_objc_arc_on in
{CFrontend_config.source_file; lang; integer_type_widths; is_objc_arc_on}
| _ -> | _ ->
assert false assert false
in in

@ -14,7 +14,10 @@ type clang_lang = C | CPP | ObjC | ObjCPP [@@deriving compare]
let equal_clang_lang = [%compare.equal: clang_lang] let equal_clang_lang = [%compare.equal: clang_lang]
type translation_unit_context = type translation_unit_context =
{lang: clang_lang; source_file: SourceFile.t; integer_type_widths: Typ.IntegerWidths.t} { lang: clang_lang
; source_file: SourceFile.t
; integer_type_widths: Typ.IntegerWidths.t
; is_objc_arc_on: bool }
type decl_trans_context = [`DeclTraversal | `Translation | `CppLambdaExprTranslation] type decl_trans_context = [`DeclTraversal | `Translation | `CppLambdaExprTranslation]

@ -14,7 +14,10 @@ type clang_lang = C | CPP | ObjC | ObjCPP [@@deriving compare]
val equal_clang_lang : clang_lang -> clang_lang -> bool val equal_clang_lang : clang_lang -> clang_lang -> bool
type translation_unit_context = type translation_unit_context =
{lang: clang_lang; source_file: SourceFile.t; integer_type_widths: Typ.IntegerWidths.t} { lang: clang_lang
; source_file: SourceFile.t
; integer_type_widths: Typ.IntegerWidths.t
; is_objc_arc_on: bool }
type decl_trans_context = [`DeclTraversal | `Translation | `CppLambdaExprTranslation] type decl_trans_context = [`DeclTraversal | `Translation | `CppLambdaExprTranslation]

@ -267,6 +267,7 @@ let create_local_procdesc ?(set_objc_accessor_attr = false) ?(record_lambda_capt
; is_biabduction_model= Config.biabduction_models_mode ; is_biabduction_model= Config.biabduction_models_mode
; passed_as_noescape_block_to= ms.CMethodSignature.passed_as_noescape_block_to ; passed_as_noescape_block_to= ms.CMethodSignature.passed_as_noescape_block_to
; is_no_return= ms.CMethodSignature.is_no_return ; is_no_return= ms.CMethodSignature.is_no_return
; is_objc_arc_on= trans_unit_ctx.CFrontend_config.is_objc_arc_on
; is_variadic= ms.CMethodSignature.is_variadic ; is_variadic= ms.CMethodSignature.is_variadic
; sentinel_attr= find_sentinel_attribute ms.CMethodSignature.attributes ; sentinel_attr= find_sentinel_attribute ms.CMethodSignature.attributes
; loc= loc_start ; loc= loc_start

@ -22,7 +22,8 @@ type extras_WorstCaseCost =
; inferbo_get_summary: BufferOverrunAnalysisSummary.get_summary ; inferbo_get_summary: BufferOverrunAnalysisSummary.get_summary
; get_node_nb_exec: Node.t -> BasicCost.t ; get_node_nb_exec: Node.t -> BasicCost.t
; get_summary: Procname.t -> CostDomain.summary option ; get_summary: Procname.t -> CostDomain.summary option
; get_formals: Procname.t -> (Pvar.t * Typ.t) list option } ; get_formals: Procname.t -> (Pvar.t * Typ.t) list option
; proc_resolve_attributes: Procname.t -> ProcAttributes.t option }
let instantiate_cost integer_type_widths ~inferbo_caller_mem ~callee_pname ~callee_formals ~params let instantiate_cost integer_type_widths ~inferbo_caller_mem ~callee_pname ~callee_formals ~params
~callee_cost ~loc = ~callee_cost ~loc =
@ -55,9 +56,15 @@ module InstrBasicCostWithReason = struct
String.equal (Procname.get_method callee_pname) "autorelease" String.equal (Procname.get_method callee_pname) "autorelease"
let get_instr_cost_record tenv extras instr_node instr = let is_objc_call_from_no_arc_to_arc {proc_resolve_attributes} caller_pdesc callee_pname =
(not (Procdesc.is_objc_arc_on caller_pdesc))
&& Option.exists (proc_resolve_attributes callee_pname)
~f:(fun {ProcAttributes.is_objc_arc_on} -> is_objc_arc_on)
let get_instr_cost_record tenv extras cfg instr_node instr =
match instr with match instr with
| Sil.Call (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
@ -66,7 +73,7 @@ module InstrBasicCostWithReason = struct
; get_formals } = ; get_formals } =
extras extras
in in
let operation_cost = let cost =
match match
BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map BufferOverrunAnalysis.extract_pre (InstrCFG.Node.id instr_node) inferbo_invariant_map
with with
@ -107,14 +114,25 @@ module InstrBasicCostWithReason = struct
callee_pname) ; callee_pname) ;
CostDomain.unit_cost_atomic_operation ) ) CostDomain.unit_cost_atomic_operation ) )
in in
let cost =
if is_allocation_function callee_pname then if is_allocation_function callee_pname then
CostDomain.plus CostDomain.unit_cost_allocation operation_cost CostDomain.plus CostDomain.unit_cost_allocation cost
else if is_autorelease_function callee_pname then else cost
CostDomain.plus in
(CostDomain.unit_cost_autoreleasepool_size if is_autorelease_function callee_pname then
~autoreleasepool_trace:(Bounds.BoundTrace.of_modeled_function "autorelease" location)) let autoreleasepool_trace =
operation_cost Bounds.BoundTrace.of_modeled_function "autorelease" location
else operation_cost in
CostDomain.plus cost (CostDomain.unit_cost_autoreleasepool_size ~autoreleasepool_trace)
else if
is_objc_call_from_no_arc_to_arc extras cfg callee_pname
&& Typ.is_pointer_to_objc_class ret_typ
then
let autoreleasepool_trace =
Bounds.BoundTrace.of_arc_from_non_arc (Procname.to_string callee_pname) location
in
CostDomain.plus 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 ->
@ -134,7 +152,7 @@ module InstrBasicCostWithReason = struct
CostDomain.zero_record CostDomain.zero_record
let get_instr_node_cost_record tenv extras instr_node = let get_instr_node_cost_record tenv extras cfg instr_node =
let instrs = InstrCFG.instrs instr_node in let instrs = InstrCFG.instrs instr_node in
let instr = let instr =
match IContainer.singleton_or_more instrs ~fold:Instrs.fold with match IContainer.singleton_or_more instrs ~fold:Instrs.fold with
@ -145,7 +163,7 @@ module InstrBasicCostWithReason = struct
| More -> | More ->
assert false assert false
in in
let cost = get_instr_cost_record tenv extras instr_node instr in let cost = get_instr_cost_record tenv extras cfg instr_node instr in
let operation_cost = CostDomain.get_operation_cost cost in let operation_cost = CostDomain.get_operation_cost cost in
let log_msg top_or_bottom = let log_msg top_or_bottom =
Logging.d_printfln_escaped "Statement cost became %s at %a (%a)." top_or_bottom Logging.d_printfln_escaped "Statement cost became %s at %a (%a)." top_or_bottom
@ -169,10 +187,10 @@ let compute_errlog_extras cost =
module WorstCaseCost = struct module WorstCaseCost = struct
(** We don't report when the cost is Top as it corresponds to subsequent 'don't know's. Instead, (** We don't report when the cost is Top as it corresponds to subsequent 'don't know's. Instead,
we report Top cost only at the top level per function. *) we report Top cost only at the top level per function. *)
let exec_node tenv extras instr_node = let exec_node tenv extras cfg instr_node =
let {get_node_nb_exec} = extras in let {get_node_nb_exec} = extras in
let instr_cost_record = let instr_cost_record =
InstrBasicCostWithReason.get_instr_node_cost_record tenv extras instr_node InstrBasicCostWithReason.get_instr_node_cost_record tenv extras cfg instr_node
in in
let node = InstrCFG.Node.underlying_node instr_node in let node = InstrCFG.Node.underlying_node instr_node in
let nb_exec = get_node_nb_exec node in let nb_exec = get_node_nb_exec node in
@ -187,7 +205,7 @@ module WorstCaseCost = struct
let cost = let cost =
let nodes_in_autoreleasepool = CostUtils.get_nodes_in_autoreleasepool cfg in let nodes_in_autoreleasepool = CostUtils.get_nodes_in_autoreleasepool cfg in
InstrCFG.fold_nodes cfg ~init ~f:(fun acc ((node, _) as pair) -> InstrCFG.fold_nodes cfg ~init ~f:(fun acc ((node, _) as pair) ->
let cost = exec_node tenv extras pair in let cost = exec_node tenv extras cfg pair in
let cost = let cost =
if Procdesc.NodeSet.mem node nodes_in_autoreleasepool then if Procdesc.NodeSet.mem node nodes_in_autoreleasepool then
CostDomain.set_autoreleasepool_size_zero cost CostDomain.set_autoreleasepool_size_zero cost
@ -326,7 +344,8 @@ let checker ({InterproceduralAnalysis.proc_desc; exe_env; analyze_dependency} as
; integer_type_widths ; integer_type_widths
; get_node_nb_exec ; get_node_nb_exec
; get_summary ; get_summary
; get_formals } ; get_formals
; proc_resolve_attributes= AnalysisCallbacks.proc_resolve_attributes }
in in
AnalysisCallbacks.html_debug_new_node_session (NodeCFG.start_node node_cfg) AnalysisCallbacks.html_debug_new_node_session (NodeCFG.start_node node_cfg)
~pp_name:(fun f -> F.pp_print_string f "cost(worst-case)") ~pp_name:(fun f -> F.pp_print_string f "cost(worst-case)")

@ -0,0 +1,23 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
@interface ArcCallee : NSObject
+ (ArcCallee*)allocObject;
+ (ArcCallee*)newObject;
+ (ArcCallee*)copyObject:(ArcCallee*)obj;
+ (ArcCallee*)mutableCopyObject:(ArcCallee*)obj;
+ (ArcCallee*)giveMeObject;
+ (int)giveMeInt;
@end

@ -0,0 +1,39 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import "arc_callee.h"
@implementation ArcCallee
+ (ArcCallee*)allocObject {
return [ArcCallee alloc];
}
+ (ArcCallee*)newObject {
return [ArcCallee new];
}
/* Since the checker cares about calling `autorelease` only, we define the
following functions as returning a new object simply. */
+ (ArcCallee*)copyObject:(ArcCallee*)obj {
return [ArcCallee new];
}
+ (ArcCallee*)mutableCopyObject:(ArcCallee*)obj {
return [ArcCallee new];
}
+ (ArcCallee*)giveMeObject {
return [ArcCallee new];
}
+ (int)giveMeInt {
return 100;
}
@end

@ -2,6 +2,13 @@ ${XCODE_ISYSROOT}/System/Library/Frameworks/Foundation.framework/Headers/NSArray
codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.callIndexOfObjectPassingTest_linear_FN:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.callIndexOfObjectPassingTest_linear_FN:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.dealloc, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_block.m, ArcBlock.dealloc, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_block.m, objc_blockArcBlock.callIndexOfObjectPassingTest_linear_FN:_1, 1, OnUIThread:false, [autorelease,Call to NoArcCallee.giveMeObject,Modeled call to autorelease] codetoanalyze/objc/autoreleasepool/arc_block.m, objc_blockArcBlock.callIndexOfObjectPassingTest_linear_FN:_1, 1, OnUIThread:false, [autorelease,Call to NoArcCallee.giveMeObject,Modeled call to autorelease]
codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.allocObject, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.copyObject:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.dealloc, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.giveMeInt, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.giveMeObject, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.mutableCopyObject:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_callee.m, ArcCallee.newObject, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callAllocObject_zero:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callAllocObject_zero:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callCopyObject_zero:x:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callCopyObject_zero:x:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_autoreleasepool_zero:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/arc_caller.m, ArcCaller.callGiveMeObject_autoreleasepool_zero:, 0, OnUIThread:false, []
@ -27,3 +34,11 @@ codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.dealloc, 0, OnU
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.giveMeObject, 1, OnUIThread:false, [autorelease,Modeled call to autorelease] codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.giveMeObject, 1, OnUIThread:false, [autorelease,Modeled call to autorelease]
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.mutableCopyObject:, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.mutableCopyObject:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.newObject, 0, OnUIThread:false, [] codetoanalyze/objc/autoreleasepool/no_arc_callee.m, NoArcCallee.newObject, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callAllocObject_zero_FP:, n, OnUIThread:false, [{n},Loop,autorelease,ARC function call to ArcCallee.allocObject from non-ARC caller]
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callCopyObject_zero_FP:x:, n, OnUIThread:false, [{n},Loop,autorelease,ARC function call to ArcCallee.copyObject: from non-ARC caller]
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callGiveMeInt_zero, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callGiveMeObject_autoreleasepool_zero:, 0, OnUIThread:false, []
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callGiveMeObject_linear:, n, OnUIThread:false, [{n},Loop,autorelease,ARC function call to ArcCallee.giveMeObject from non-ARC caller]
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callMutableCopyObject_zero_FP:x:, n, OnUIThread:false, [{n},Loop,autorelease,ARC function call to ArcCallee.mutableCopyObject: from non-ARC caller]
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.callNewObject_zero_FP:, n, OnUIThread:false, [{n},Loop,autorelease,ARC function call to ArcCallee.newObject from non-ARC caller]
codetoanalyze/objc/autoreleasepool/no_arc_caller.m, NoArcCaller.dealloc, 0, OnUIThread:false, []

@ -0,0 +1,57 @@
/*
* 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.
*/
#import <Foundation/Foundation.h>
#import "arc_callee.h"
@interface NoArcCaller : NSObject
@end
@implementation NoArcCaller
- (void)callAllocObject_zero_FP:(int)n {
for (int i = 0; i < n; i++) {
ArcCallee* obj = [ArcCallee allocObject];
}
}
- (void)callNewObject_zero_FP:(int)n {
for (int i = 0; i < n; i++) {
ArcCallee* obj = [ArcCallee newObject];
}
}
- (void)callCopyObject_zero_FP:(int)n x:(ArcCallee*)x {
for (int i = 0; i < n; i++) {
ArcCallee* obj = [ArcCallee copyObject:x];
}
}
- (void)callMutableCopyObject_zero_FP:(int)n x:(ArcCallee*)x {
for (int i = 0; i < n; i++) {
ArcCallee* obj = [ArcCallee mutableCopyObject:x];
}
}
- (void)callGiveMeObject_linear:(int)n {
for (int i = 0; i < n; i++) {
ArcCallee* obj = [ArcCallee giveMeObject];
}
}
- (void)callGiveMeObject_autoreleasepool_zero:(int)n {
for (int i = 0; i < n; i++) {
@autoreleasepool {
ArcCallee* obj = [ArcCallee giveMeObject];
}
}
}
- (void)callGiveMeInt_zero {
int i = [ArcCallee giveMeInt];
}
@end
Loading…
Cancel
Save