Skip to content

Commit 0d57d35

Browse files
committed
better EAUtils.jl
1 parent d17aab3 commit 0d57d35

File tree

1 file changed

+86
-55
lines changed

1 file changed

+86
-55
lines changed

test/compiler/EscapeAnalysis/EAUtils.jl

Lines changed: 86 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,56 +6,6 @@ const CC = Core.Compiler
66
using ..EscapeAnalysis
77
const 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)
157108
end
158109

159110
struct 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
303255
end
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+
305336
end # module EAUtils

0 commit comments

Comments
 (0)