@@ -6,56 +6,6 @@ const CC = Core.Compiler
66using .. EscapeAnalysis
77const EA = EscapeAnalysis
88
9- # entries
10- # -------
11-
12- using Base: IdSet, unwrap_unionall, rewrap_unionall
13- using InteractiveUtils: gen_call_with_extracted_types_and_kwargs
14-
15- """
16- @code_escapes [options...] f(args...)
17-
18- Evaluates the arguments to the function call, determines its types, and then calls
19- [`code_escapes`](@ref) on the resulting expression.
20- As with `@code_typed` and its family, any of `code_escapes` keyword arguments can be given
21- as the optional arguments like `@code_escapes optimize=false myfunc(myargs...)`.
22- """
23- macro code_escapes (ex0... )
24- return gen_call_with_extracted_types_and_kwargs (__module__, :code_escapes , ex0)
25- end
26-
27- """
28- code_escapes(f, argtypes=Tuple{}; [debuginfo::Symbol = :none], [optimize::Bool = true]) -> result::EscapeResult
29-
30- Runs the escape analysis on optimized IR of a generic function call with the given type signature.
31-
32- # Keyword Arguments
33-
34- - `optimize::Bool = true`:
35- if `true` returns escape information of post-inlining IR (used for local optimization),
36- otherwise returns escape information of pre-inlining IR (used for interprocedural escape information generation)
37- - `debuginfo::Symbol = :none`:
38- controls the amount of code metadata present in the output, possible options are `:none` or `:source`.
39- """
40- function code_escapes (@nospecialize (f), @nospecialize (types= Base. default_tt (f));
41- world:: UInt = get_world_counter (),
42- debuginfo:: Symbol = :none )
43- tt = Base. signature_type (f, types)
44- match = Base. _which (tt; world, raise= true )
45- mi = Core. Compiler. specialize_method (match):: MethodInstance
46- interp = EscapeAnalyzer (world, mi)
47- frame = Core. Compiler. typeinf_frame (interp, mi, #= run_optimizer=# true )
48- isdefined (interp, :result ) || error (" optimization didn't happen: maybe everything has been constant folded?" )
49- slotnames = let src = frame. src
50- src isa CodeInfo ? src. slotnames : nothing
51- end
52- return EscapeResult (interp. result. ir, interp. result. estate, interp. result. mi,
53- slotnames, debuginfo === :source , interp)
54- end
55-
56- # in order to run a whole analysis from ground zero (e.g. for benchmarking, etc.)
57- __clear_cache! () = empty! (GLOBAL_EA_CODE_CACHE)
58-
599# AbstractInterpreter
6010# -------------------
6111
@@ -99,10 +49,10 @@ mutable struct EscapeAnalyzer <: AbstractInterpreter
9949 const opt_params:: OptimizationParams
10050 const inf_cache:: Vector{InferenceResult}
10151 const escape_cache:: EscapeCache
102- const entry_mi:: MethodInstance
52+ const entry_mi:: Union{Nothing, MethodInstance}
10353 result:: EscapeResultForEntry
104- function EscapeAnalyzer (world:: UInt , entry_mi :: MethodInstance ,
105- escape_cache :: EscapeCache = GLOBAL_ESCAPE_CACHE )
54+ function EscapeAnalyzer (world:: UInt , escape_cache :: EscapeCache ;
55+ entry_mi :: Union{Nothing,MethodInstance} = nothing )
10656 inf_params = InferenceParams ()
10757 opt_params = OptimizationParams ()
10858 inf_cache = InferenceResult[]
@@ -126,8 +76,9 @@ function CC.ipo_dataflow_analysis!(interp::EscapeAnalyzer, opt::OptimizationStat
12676 estate = try
12777 analyze_escapes (ir, nargs, 𝕃ₒ, get_escape_cache)
12878 catch err
129- @error " error happened within EA, inspect `Main.failed_escapeanalysis`"
130- Main. failed_escapeanalysis = FailedAnalysis (ir, nargs, get_escape_cache)
79+ @error " error happened within EA, inspect `Main.failedanalysis`"
80+ failedanalysis = FailedAnalysis (caller, ir, nargs, get_escape_cache)
81+ Core. eval (Main, :(failedanalysis = $ failedanalysis))
13182 rethrow (err)
13283 end
13384 if caller. linfo === interp. entry_mi
@@ -157,6 +108,7 @@ function ((; escape_cache)::GetEscapeCache)(mi::MethodInstance)
157108end
158109
159110struct FailedAnalysis
111+ caller:: InferenceResult
160112 ir:: IRCode
161113 nargs:: Int
162114 get_escape_cache:: GetEscapeCache
@@ -302,4 +254,83 @@ function print_with_info(preprint, postprint, io::IO, ir::IRCode, source::Bool)
302254 return nothing
303255end
304256
257+ # entries
258+ # -------
259+
260+ using InteractiveUtils: gen_call_with_extracted_types_and_kwargs
261+
262+ """
263+ @code_escapes [options...] f(args...)
264+
265+ Evaluates the arguments to the function call, determines its types, and then calls
266+ [`code_escapes`](@ref) on the resulting expression.
267+ As with `@code_typed` and its family, any of `code_escapes` keyword arguments can be given
268+ as the optional arguments like `@code_escapes optimize=false myfunc(myargs...)`.
269+ """
270+ macro code_escapes (ex0... )
271+ return gen_call_with_extracted_types_and_kwargs (__module__, :code_escapes , ex0)
272+ end
273+
274+ """
275+ code_escapes(f, argtypes=Tuple{}; [world::UInt], [debuginfo::Symbol]) -> result::EscapeResult
276+ code_escapes(mi::MethodInstance; [world::UInt], [interp::EscapeAnalyzer], [debuginfo::Symbol]) -> result::EscapeResult
277+
278+ Runs the escape analysis on optimized IR of a generic function call with the given type signature,
279+ while caching the analysis results.
280+
281+ # Keyword Arguments
282+
283+ - `world::UInt = Base.get_world_counter()`:
284+ controls the world age to use when looking up methods, use current world age if not specified.
285+ - `interp::EscapeAnalyzer = EscapeAnalyzer(world)`:
286+ specifies the escape analyzer to use, by default a new analyzer with the global cache is created.
287+ - `debuginfo::Symbol = :none`:
288+ controls the amount of code metadata present in the output, possible options are `:none` or `:source`.
289+ """
290+ function code_escapes (@nospecialize (f), @nospecialize (types= Base. default_tt (f));
291+ world:: UInt = get_world_counter (),
292+ debuginfo:: Symbol = :none )
293+ tt = Base. signature_type (f, types)
294+ match = Base. _which (tt; world, raise= true )
295+ mi = Core. Compiler. specialize_method (match)
296+ return code_escapes (mi; world, debuginfo)
297+ end
298+
299+ function code_escapes (mi:: MethodInstance ;
300+ world:: UInt = get_world_counter (),
301+ interp:: EscapeAnalyzer = EscapeAnalyzer (world, GLOBAL_ESCAPE_CACHE; entry_mi= mi),
302+ debuginfo:: Symbol = :none )
303+ frame = Core. Compiler. typeinf_frame (interp, mi, #= run_optimizer=# true )
304+ isdefined (interp, :result ) || error (" optimization didn't happen: maybe everything has been constant folded?" )
305+ slotnames = let src = frame. src
306+ src isa CodeInfo ? src. slotnames : nothing
307+ end
308+ return EscapeResult (interp. result. ir, interp. result. estate, interp. result. mi,
309+ slotnames, debuginfo === :source , interp)
310+ end
311+
312+ """
313+ code_escapes(ir::IRCode, nargs::Int; [world::UInt], [interp::AbstractInterpreter]) -> result::EscapeResult
314+
315+ Runs the escape analysis on `ir::IRCode`.
316+ `ir` is supposed to be optimized already, specifically after inlining has been applied.
317+ Note that this version does not cache the analysis results.
318+
319+ # Keyword Arguments
320+
321+ - `world::UInt = Base.get_world_counter()`:
322+ controls the world age to use when looking up methods, use current world age if not specified.
323+ - `interp::AbstractInterpreter = EscapeAnalyzer(world, EscapeCache())`:
324+ specifies the abstract interpreter to use, by default a new `EscapeAnalyzer` with an empty cache is created.
325+ """
326+ function code_escapes (ir:: IRCode , nargs:: Int ;
327+ world:: UInt = get_world_counter (),
328+ interp:: AbstractInterpreter = EscapeAnalyzer (world, EscapeCache ()))
329+ estate = analyze_escapes (ir, nargs, CC. optimizer_lattice (interp), CC. get_escape_cache (interp))
330+ return EscapeResult (ir, estate) # return back the result
331+ end
332+
333+ # in order to run a whole analysis from ground zero (e.g. for benchmarking, etc.)
334+ __clear_cache! () = empty! (GLOBAL_EA_CODE_CACHE)
335+
305336end # module EAUtils
0 commit comments