Skip to content

Commit

Permalink
Merge branch 'master' into jps/finalizers
Browse files Browse the repository at this point in the history
  • Loading branch information
aviatesk committed Feb 3, 2022
2 parents d687586 + 31fb431 commit 6011c86
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 46 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,7 @@ it is more ideal if it runs once and succeeding optimization passes incrementall
Core.Compiler.EscapeAnalysis.analyze_escapes
Core.Compiler.EscapeAnalysis.EscapeState
Core.Compiler.EscapeAnalysis.EscapeInfo
Core.Compiler.EscapeAnalysis.cache_escapes!
Core.Compiler.EscapeAnalysis.ArgEscapeInfo
```
[^LatticeDesign]: Our type inference implementation takes the alternative approach,
Expand Down
58 changes: 35 additions & 23 deletions src/EAUtils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const EA = EscapeAnalysis
# entries
# -------

import Base: unwrap_unionall, rewrap_unionall
import InteractiveUtils: gen_call_with_extracted_types_and_kwargs

"""
Expand All @@ -25,7 +26,6 @@ end

"""
code_escapes(f, argtypes=Tuple{}; [debuginfo::Symbol = :none], [optimize::Bool = true]) -> result::EscapeResult
code_escapes(tt::Type{<:Tuple}; [debuginfo::Symbol = :none], [optimize::Bool = true]) -> result::EscapeResult
Runs the escape analysis on optimized IR of a generic function call with the given type signature.
Expand All @@ -37,13 +37,20 @@ Runs the escape analysis on optimized IR of a generic function call with the giv
- `debuginfo::Symbol = :none`:
controls the amount of code metadata present in the output, possible options are `:none` or `:source`.
"""
function code_escapes(@nospecialize(args...);
function code_escapes(@nospecialize(f), @nospecialize(types=Base.default_tt(f));
world::UInt = get_world_counter(),
interp::Core.Compiler.AbstractInterpreter = Core.Compiler.NativeInterpreter(world),
debuginfo::Symbol = :none,
optimize::Bool = true)
interp = EscapeAnalyzer(interp, optimize)
results = code_typed(args...; optimize=true, world, interp)
ft = Core.Typeof(f)
if isa(types, Type)
u = unwrap_unionall(types)
tt = rewrap_unionall(Tuple{ft, u.parameters...}, types)
else
tt = Tuple{ft, types...}
end
interp = EscapeAnalyzer(interp, tt, optimize)
results = Base.code_typed_by_type(tt; optimize=true, world, interp)
isone(length(results)) || throw(ArgumentError("`code_escapes` only supports single analysis result"))
return EscapeResult(interp.ir, interp.state, interp.linfo, debuginfo===:source)
end
Expand All @@ -67,7 +74,7 @@ import .CC:
InferenceResult, OptimizationState, IRCode, copy as cccopy,
@timeit, convert_to_ircode, slot2reg, compact!, ssa_inlining_pass!, sroa_pass!,
adce_pass!, type_lift_pass!, JLOptions, verify_ir, verify_linetable
import .EA: analyze_escapes, ArgEscapeInfo, EscapeInfo, EscapeState
import .EA: analyze_escapes, ArgEscapeInfo, EscapeInfo, EscapeState, is_ipo_profitable

# when working outside of Core.Compiler,
# cache entire escape state for later inspection and debugging
Expand All @@ -80,12 +87,13 @@ end
mutable struct EscapeAnalyzer{State} <: AbstractInterpreter
native::NativeInterpreter
cache::IdDict{InferenceResult,EscapeCache}
entry_tt
optimize::Bool
ir::IRCode
state::State
linfo::MethodInstance
EscapeAnalyzer(native::NativeInterpreter, optimize::Bool) =
new{EscapeState}(native, IdDict{InferenceResult,EscapeCache}(), optimize)
EscapeAnalyzer(native::NativeInterpreter, @nospecialize(tt), optimize::Bool) =
new{EscapeState}(native, IdDict{InferenceResult,EscapeCache}(), tt, optimize)
end

CC.InferenceParams(interp::EscapeAnalyzer) = InferenceParams(interp.native)
Expand Down Expand Up @@ -199,15 +207,19 @@ function run_passes_with_ea(interp::EscapeAnalyzer, ci::CodeInfo, sv::Optimizati
@timeit "compact 1" ir = compact!(ir)
nargs = let def = sv.linfo.def; isa(def, Method) ? Int(def.nargs) : 0; end
local state
try
@timeit "[IPO EA]" state = analyze_escapes(ir, nargs, false, getargescapes(interp))
cache_escapes!(interp, caller, state, cccopy(ir))
catch err
@error "error happened within [IPO EA], insepct `Main.ir` and `Main.nargs`"
@eval Main (ir = $ir; nargs = $nargs)
rethrow(err)
if is_ipo_profitable(ir, nargs) || caller.linfo.specTypes === interp.entry_tt
try
@timeit "[IPO EA]" begin
state = analyze_escapes(ir, nargs, false, getargescapes(interp))
cache_escapes!(interp, caller, state, cccopy(ir))
end
catch err
@error "error happened within [IPO EA], insepct `Main.ir` and `Main.nargs`"
@eval Main (ir = $ir; nargs = $nargs)
rethrow(err)
end
end
if !interp.optimize
if caller.linfo.specTypes === interp.entry_tt && !interp.optimize
# return back the result
interp.ir = cccopy(ir)
interp.state = state
Expand All @@ -216,14 +228,14 @@ function run_passes_with_ea(interp::EscapeAnalyzer, ci::CodeInfo, sv::Optimizati
@timeit "Inlining" ir = ssa_inlining_pass!(ir, ir.linetable, sv.inlining, ci.propagate_inbounds)
# @timeit "verify 2" verify_ir(ir)
@timeit "compact 2" ir = compact!(ir)
try
@timeit "[Local EA]" state = analyze_escapes(ir, nargs, true, getargescapes(interp))
catch err
@error "error happened within [Local EA], insepct `Main.ir` and `Main.nargs`"
@eval Main (ir = $ir; nargs = $nargs)
rethrow(err)
end
if interp.optimize
if caller.linfo.specTypes === interp.entry_tt && interp.optimize
try
@timeit "[Local EA]" state = analyze_escapes(ir, nargs, true, getargescapes(interp))
catch err
@error "error happened within [Local EA], insepct `Main.ir` and `Main.nargs`"
@eval Main (ir = $ir; nargs = $nargs)
rethrow(err)
end
# return back the result
interp.ir = cccopy(ir)
interp.state = state
Expand Down
21 changes: 19 additions & 2 deletions src/EscapeAnalysis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ baremodule EscapeAnalysis

export
analyze_escapes,
cache_escapes!,
getaliases,
isaliased,
has_no_escape,
Expand All @@ -26,7 +25,8 @@ import ._TOP_MOD: # Base definitions
@label, @goto, !, !==, !=, , +, -, *, , <, , >, &, |, include, error, missing, copy,
Vector, BitSet, IdDict, IdSet, UnitRange, Csize_t, Callable, , , , :, , ,
in, length, get, first, last, haskey, keys, get!, isempty, isassigned,
pop!, push!, pushfirst!, empty!, delete!, max, min, enumerate
pop!, push!, pushfirst!, empty!, delete!, max, min, enumerate, unwrap_unionall,
ismutabletype
import Core.Compiler: # Core.Compiler specific definitions
Bottom, InferenceResult, IRCode, Instruction, Signature,
MethodResultPure, MethodMatchInfo, UnionSplitInfo, ConstCallInfo, InvokeCallInfo,
Expand Down Expand Up @@ -600,6 +600,23 @@ function to_interprocedural(estate::EscapeState)
return argescapes
end

# checks if `ir` has any argument that is "interesting" in terms of their escapability
function is_ipo_profitable(ir::IRCode, nargs::Int)
for i = 1:nargs
t = unwrap_unionall(widenconst(ir.argtypes[i]))
t <: IO && return false # bail out IO-related functions
is_ipo_profitable_type(t) && return true
end
return false
end
function is_ipo_profitable_type(@nospecialize t)
if isa(t, Union)
return is_ipo_profitable_type(t.a) && is_ipo_profitable_type(t.b)
end
(t === String || t === Symbol) && return false
return ismutabletype(t)
end

abstract type Change end
struct EscapeChange <: Change
xidx::Int
Expand Down
40 changes: 20 additions & 20 deletions test/EscapeAnalysis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2335,92 +2335,92 @@ Base.@constprop :aggressive function conditional_escape!(cnd, x)
end

# MethodMatchInfo -- global cache
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
return noescape(x)
end
@test has_no_escape(ignore_argescape(result.state[Argument(2)]))
end
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
identity(x)
return nothing
end
@test has_no_escape(ignore_argescape(result.state[Argument(2)]))
end
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
return identity(x)
end
r = only(findall(isreturn, result.ir.stmts.inst))
@test has_return_escape(result.state[Argument(2)], r)
end
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
return Ref(x)
end
r = only(findall(isreturn, result.ir.stmts.inst))
@test has_return_escape(result.state[Argument(2)], r)
end
let result = code_escapes((String,); optimize=false) do x
r = Ref{String}()
let result = code_escapes((SafeRef{String},); optimize=false) do x
r = Ref{SafeRef{String}}()
r[] = x
return r
end
r = only(findall(isreturn, result.ir.stmts.inst))
@test has_return_escape(result.state[Argument(2)], r)
end
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
global_escape!(x)
end
@test has_all_escape(result.state[Argument(2)])
end
# UnionSplitInfo
let result = code_escapes((Bool,String); optimize=false) do c, s
let result = code_escapes((Bool,Vector{Any}); optimize=false) do c, s
x = c ? s : SafeRef(s)
union_escape!(x)
end
@test has_all_escape(result.state[Argument(3)]) # s
end
let result = code_escapes((Bool,String); optimize=false) do c, s
let result = code_escapes((Bool,Vector{Any}); optimize=false) do c, s
x = c ? SafeRef(s) : SafeRefs(s, s)
union_escape!(x)
end
@test has_no_escape(ignore_argescape(result.state[Argument(2)]))
end
# ConstCallInfo -- local cache
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
return conditional_escape!(false, x)
end
@test has_no_escape(ignore_argescape(result.state[Argument(2)]))
end
# InvokeCallInfo
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
return Base.@invoke noescape(x::Any)
end
@test has_no_escape(ignore_argescape(result.state[Argument(2)]))
end
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
return Base.@invoke conditional_escape!(false::Any, x::Any)
end
@test has_no_escape(ignore_argescape(result.state[Argument(2)]))
end

# ThrownEscape via potential MethodError
identity_if_string(x::String) = nothing
let result = code_escapes((Any,); optimize=false) do x
identity_if_string(x::SafeRef) = nothing
let result = code_escapes((SafeRef{String},); optimize=false) do x
identity_if_string(x)
end
i = only(findall(iscall((result.ir, identity_if_string)), result.ir.stmts.inst))
r = only(findall(isreturn, result.ir.stmts.inst))
@test has_thrown_escape(result.state[Argument(2)], i)
@test !has_thrown_escape(result.state[Argument(2)], i)
@test !has_return_escape(result.state[Argument(2)], r)
end
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((Union{SafeRef{String},Vector{String}},); optimize=false) do x
identity_if_string(x)
end
i = only(findall(iscall((result.ir, identity_if_string)), result.ir.stmts.inst))
r = only(findall(isreturn, result.ir.stmts.inst))
@test !has_thrown_escape(result.state[Argument(2)], i)
@test has_thrown_escape(result.state[Argument(2)], i)
@test !has_return_escape(result.state[Argument(2)], r)
end
let result = code_escapes((String,); optimize=false) do x
let result = code_escapes((SafeRef{String},); optimize=false) do x
try
identity_if_string(x)
catch err
Expand All @@ -2430,7 +2430,7 @@ let result = code_escapes((String,); optimize=false) do x
end
@test !has_all_escape(result.state[Argument(2)])
end
let result = code_escapes((Any,); optimize=false) do x
let result = code_escapes((Union{SafeRef{String},Vector{String}},); optimize=false) do x
try
identity_if_string(x)
catch err
Expand All @@ -2455,7 +2455,7 @@ end
return true
end
target_modules = (EscapeAnalysis,)
interp = EscapeAnalysis.EAUtils.EscapeAnalyzer(Core.Compiler.NativeInterpreter(), true)
interp = EscapeAnalysis.EAUtils.EscapeAnalyzer(Core.Compiler.NativeInterpreter(), Tuple{}, true)
getargescapes = EscapeAnalysis.EAUtils.getargescapes(interp)
sig = Tuple{typeof(analyze_escapes), Core.Compiler.IRCode, Int, Bool, typeof(getargescapes)}
test_opt(sig;
Expand Down

0 comments on commit 6011c86

Please sign in to comment.