Skip to content

Commit

Permalink
better IPO handling
Browse files Browse the repository at this point in the history
  • Loading branch information
aviatesk committed Feb 4, 2022
1 parent deade7e commit 08a2314
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 49 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,6 @@ 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.ArgEscapeInfo
```
[^LatticeDesign]: Our type inference implementation takes the alternative approach,
Expand Down
20 changes: 10 additions & 10 deletions src/EAUtils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ 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, is_ipo_profitable
import .EA: analyze_escapes, ArgEscapeCache, EscapeInfo, EscapeState, is_ipo_profitable

# when working outside of Core.Compiler,
# cache entire escape state for later inspection and debugging
struct EscapeCache
argescapes::Vector{ArgEscapeInfo}
cache::ArgEscapeCache
state::EscapeState # preserved just for debugging purpose
ir::IRCode # preserved just for debugging purpose
end
Expand Down Expand Up @@ -182,20 +182,20 @@ and then caches it into a global cache for later interprocedural propagation.
"""
function cache_escapes!(interp::EscapeAnalyzer,
caller::InferenceResult, estate::EscapeState, cacheir::IRCode)
argescapes = EscapeAnalysis.to_interprocedural(estate)
cache = EscapeCache(argescapes, estate, cacheir)
interp.cache[caller] = cache
return argescapes
cache = ArgEscapeCache(estate)
ecache = EscapeCache(cache, estate, cacheir)
interp.cache[caller] = ecache
return cache
end

function getargescapes(interp::EscapeAnalyzer)
function get_escape_cache(interp::EscapeAnalyzer)
return function (linfo::Union{InferenceResult,MethodInstance})
if isa(linfo, InferenceResult)
ecache = get(interp.cache, linfo, nothing)
else
ecache = get(GLOBAL_ESCAPE_CACHE, linfo, nothing)
end
return ecache !== nothing ? ecache.argescapes : nothing
return ecache !== nothing ? ecache.cache : nothing
end
end

Expand All @@ -210,7 +210,7 @@ function run_passes_with_ea(interp::EscapeAnalyzer, ci::CodeInfo, sv::Optimizati
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))
state = analyze_escapes(ir, nargs, false, get_escape_cache(interp))
cache_escapes!(interp, caller, state, cccopy(ir))
end
catch err
Expand All @@ -230,7 +230,7 @@ function run_passes_with_ea(interp::EscapeAnalyzer, ci::CodeInfo, sv::Optimizati
@timeit "compact 2" ir = compact!(ir)
if caller.linfo.specTypes === interp.entry_tt && interp.optimize
try
@timeit "[Local EA]" state = analyze_escapes(ir, nargs, true, getargescapes(interp))
@timeit "[Local EA]" state = analyze_escapes(ir, nargs, true, get_escape_cache(interp))
catch err
@error "error happened within [Local EA], insepct `Main.ir` and `Main.nargs`"
@eval Main (ir = $ir; nargs = $nargs)
Expand Down
67 changes: 32 additions & 35 deletions src/EscapeAnalysis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import Core:
import ._TOP_MOD: # Base definitions
@__MODULE__, @eval, @assert, @specialize, @nospecialize, @inbounds, @inline, @noinline,
@label, @goto, !, !==, !=, , +, -, *, , <, , >, &, |, <<, include, error, missing, copy,
Vector, BitSet, IdDict, IdSet, UnitRange, Csize_t, Callable, , , , :, , ,
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, unwrap_unionall,
ismutabletype
Expand Down Expand Up @@ -538,22 +538,15 @@ isaliased(x::Union{Argument,SSAValue}, y::Union{Argument,SSAValue}, estate::Esca
isaliased(xidx::Int, yidx::Int, estate::EscapeState) =
in_same_set(estate.aliasset, xidx, yidx)

"""
ArgEscapeInfo(x::EscapeInfo) -> x′::ArgEscapeInfo
The data structure for caching `x::EscapeInfo` for interprocedural propagation,
which is slightly more efficient than the original `x::EscapeInfo` object.
"""
struct ArgEscapeInfo
EscapeBits::UInt8
ArgAliasing::Union{Nothing,Vector{Int}}
end
function ArgEscapeInfo(x::EscapeInfo, ArgAliasing::Union{Nothing,Vector{Int}})
x ===&& return ArgEscapeInfo(ARG_ALL_ESCAPE, nothing)
function ArgEscapeInfo(x::EscapeInfo)
x ===&& return ArgEscapeInfo(ARG_ALL_ESCAPE)
EscapeBits = 0x00
has_return_escape(x) && (EscapeBits |= ARG_RETURN_ESCAPE)
has_thrown_escape(x) && (EscapeBits |= ARG_THROWN_ESCAPE)
return ArgEscapeInfo(EscapeBits, ArgAliasing)
return ArgEscapeInfo(EscapeBits)
end

const ARG_ALL_ESCAPE = 0x01 << 0
Expand All @@ -565,24 +558,31 @@ has_all_escape(x::ArgEscapeInfo) = x.EscapeBits & ARG_ALL_ESCAPE ≠ 0
has_return_escape(x::ArgEscapeInfo) = x.EscapeBits & ARG_RETURN_ESCAPE 0
has_thrown_escape(x::ArgEscapeInfo) = x.EscapeBits & ARG_THROWN_ESCAPE 0

function to_interprocedural(estate::EscapeState)
struct ArgAliasing
aidx::Int
bidx::Int
end

struct ArgEscapeCache
argescapes::Vector{ArgEscapeInfo}
argaliases::Vector{ArgAliasing}
end

function ArgEscapeCache(estate::EscapeState)
nargs = estate.nargs
argescapes = Vector{ArgEscapeInfo}(undef, nargs)
argaliases = ArgAliasing[]
for i = 1:nargs
info = estate.escapes[i]
@assert info.AliasInfo === true
ArgAliasing = nothing
argescapes[i] = ArgEscapeInfo(info)
for j = (i+1):nargs
if isaliased(i, j, estate)
if ArgAliasing === nothing
ArgAliasing = Int[]
end
push!(ArgAliasing, j)
push!(argaliases, ArgAliasing(i, j))
end
end
argescapes[i] = ArgEscapeInfo(info, ArgAliasing)
end
return argescapes
return ArgEscapeCache(argescapes, argaliases)
end

# checks if `ir` has any argument that is "interesting" in terms of their escapability
Expand Down Expand Up @@ -625,7 +625,7 @@ struct AnalysisState{T<:Callable}
ir::IRCode
estate::EscapeState
changes::Changes
getargescapes::T
get_escape_cache::T
end

function getinst(ir::IRCode, idx::Int)
Expand All @@ -638,22 +638,22 @@ function getinst(ir::IRCode, idx::Int)
end

"""
analyze_escapes(ir::IRCode, nargs::Int, call_resolved::Bool, getargescapes::Callable)
analyze_escapes(ir::IRCode, nargs::Int, call_resolved::Bool, get_escape_cache::Callable)
-> estate::EscapeState
Analyzes escape information in `ir`:
- `nargs`: the number of actual arguments of the analyzed call
- `call_resolved`: if interprocedural calls are already resolved by `ssa_inlining_pass!`
- `getargescapes(::Union{InferenceResult,MethodInstance})`: retrieves argument escape cache
- `get_escape_cache(::Union{InferenceResult,MethodInstance})`: retrieves argument escape cache
"""
function analyze_escapes(ir::IRCode, nargs::Int, call_resolved::Bool, getargescapes::T) where T<:Callable
function analyze_escapes(ir::IRCode, nargs::Int, call_resolved::Bool, get_escape_cache::T) where T<:Callable
stmts = ir.stmts
nstmts = length(stmts) + length(ir.new_nodes.stmts)

tryregions, arrayinfo, callinfo = compute_frameinfo(ir, call_resolved)
estate = EscapeState(nargs, nstmts, arrayinfo)
changes = Changes() # keeps changes that happen at current statement
astate = AnalysisState(ir, estate, changes, getargescapes)
astate = AnalysisState(ir, estate, changes, get_escape_cache)

local debug_itr_counter = 0
while true
Expand Down Expand Up @@ -1227,15 +1227,15 @@ escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}) =
function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any},
linfo::Linfo, first_idx::Int, last_idx::Int = length(args))
if isa(linfo, InferenceResult)
argescapes = astate.getargescapes(linfo)
cache = astate.get_escape_cache(linfo)
linfo = linfo.linfo
else
argescapes = astate.getargescapes(linfo)
cache = astate.get_escape_cache(linfo)
end
if argescapes === nothing
if cache === nothing
return add_conservative_changes!(astate, pc, args, 2)
else
argescapes = argescapes::Vector{ArgEscapeInfo}
cache = cache::ArgEscapeCache
end
ret = SSAValue(pc)
retinfo = astate.estate[ret] # escape information imposed on the call statement
Expand All @@ -1248,7 +1248,7 @@ function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any},
# COMBAK will this be invalid once we take alias information into account?
i = nargs
end
arginfo = argescapes[i]
arginfo = cache.argescapes[i]
info = from_interprocedural(arginfo, retinfo, pc)
if has_return_escape(arginfo)
# if this argument can be "returned", in addition to propagating
Expand All @@ -1261,12 +1261,9 @@ function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any},
# the escape information imposed on this call argument within the callee
add_escape_change!(astate, arg, info)
end
ArgAliasing = arginfo.ArgAliasing
if ArgAliasing !== nothing
for j in ArgAliasing
add_alias_change!(astate, arg, args[j-(first_idx-1)])
end
end
end
for (; aidx, bidx) in cache.argaliases
add_alias_change!(astate, args[aidx-(first_idx-1)], args[bidx-(first_idx-1)])
end
# we should disable the alias analysis on this newly introduced object
add_escape_change!(astate, ret, EscapeInfo(retinfo, true))
Expand Down
6 changes: 3 additions & 3 deletions test/EscapeAnalysis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2455,8 +2455,8 @@ end
end
target_modules = (EscapeAnalysis,)
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)}
get_escape_cache = EscapeAnalysis.EAUtils.get_escape_cache(interp)
sig = Tuple{typeof(analyze_escapes), Core.Compiler.IRCode, Int, Bool, typeof(get_escape_cache)}
test_opt(sig;
function_filter,
target_modules,
Expand All @@ -2466,7 +2466,7 @@ end
sig = m.sig
Base._methods_by_ftype(sig, 1, Base.get_world_counter()) === false && continue
types = collect(sig.parameters)
types[2] = EscapeAnalysis.AnalysisState{typeof(getargescapes)}
types[2] = EscapeAnalysis.AnalysisState{typeof(get_escape_cache)}
sig = Tuple{types...}
test_opt(sig;
function_filter,
Expand Down

0 comments on commit 08a2314

Please sign in to comment.