Skip to content

Commit d90ac45

Browse files
author
Ian Atol
committed
Add simple immutable array memory optimization
1 parent 572a6fe commit d90ac45

File tree

3 files changed

+29
-78
lines changed

3 files changed

+29
-78
lines changed

base/compiler/optimize.jl

+8-6
Original file line numberDiff line numberDiff line change
@@ -551,12 +551,14 @@ function run_passes(ci::CodeInfo, sv::OptimizationState, caller::InferenceResult
551551
@timeit "Inlining" ir = ssa_inlining_pass!(ir, ir.linetable, sv.inlining, ci.propagate_inbounds)
552552
# @timeit "verify 2" verify_ir(ir)
553553
@timeit "compact 2" ir = compact!(ir)
554-
@timeit "SROA" ir, memory_opt = linear_pass!(ir)
555-
if memory_opt
556-
@timeit "memory_opt_pass!" begin
557-
@timeit "Local EA" estate = analyze_escapes(ir,
558-
nargs, #=call_resolved=#true, null_escape_cache)
559-
@timeit "memory_opt_pass!" ir = memory_opt_pass!(ir, estate)
554+
@timeit "SROA" ir, memory_opt, imarray_memory_opt = linear_pass!(ir)
555+
if memory_opt || imarray_memory_opt
556+
@timeit "Local EA" estate = analyze_escapes(ir, nargs, #=call_resolved=#true, null_escape_cache)
557+
if memory_opt
558+
@timeit "Memory opt" ir = memory_opt_pass!(ir, estate)
559+
end
560+
if imarray_memory_opt
561+
@timeit "imarray opt" ir = imarray_memoryopt_pass!(ir, estate)
560562
end
561563
end
562564
@timeit "ADCE" ir = adce_pass!(ir)

base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import ._TOP_MOD: # Base definitions
2727
pop!, push!, pushfirst!, empty!, delete!, max, min, enumerate, unwrap_unionall,
2828
ismutabletype
2929
import Core.Compiler: # Core.Compiler specific definitions
30-
Bottom, InferenceResult, IRCode, IR_FLAG_EFFECT_FREE,
30+
Arrayish, Bottom, InferenceResult, IRCode, IR_FLAG_EFFECT_FREE,
3131
isbitstype, isexpr, is_meta_expr_head, println, widenconst, argextype, singleton_type,
3232
fieldcount_noerror, try_compute_field, try_compute_fieldidx, hasintersect, ,
3333
intrinsic_nothrow, array_builtin_common_typecheck, arrayset_typecheck,
@@ -1608,7 +1608,7 @@ function escape_builtin!(::typeof(arrayref), astate::AnalysisState, pc::Int, arg
16081608
argtypes = Any[argextype(args[i], astate.ir) for i in 2:length(args)]
16091609
boundcheckt = argtypes[1]
16101610
aryt = argtypes[2]
1611-
if !array_builtin_common_typecheck(Array, boundcheckt, aryt, argtypes, 3)
1611+
if !array_builtin_common_typecheck(Arrayish, boundcheckt, aryt, argtypes, 3)
16121612
add_thrown_escapes!(astate, pc, args, 2)
16131613
end
16141614
ary = args[3]

base/compiler/ssair/passes.jl

+19-70
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,11 @@ function linear_pass!(ir::IRCode)
547547
compact = IncrementalCompact(ir)
548548
lifting_cache = IdDict{Pair{AnySSAValue, Any}, AnySSAValue}()
549549
local memory_opt = false # whether or not to run the memory_opt_pass! pass later
550+
local imarray_memory_opt = false
550551
for ((_, idx), stmt) in compact
552+
# presence of arrayfreeze means possible copy elision opportunity, so run imarray_memoryopt_pass!
553+
isa(stmt, GlobalRef) && stmt.name === :arrayfreeze && (imarray_memory_opt = true)
554+
551555
isa(stmt, Expr) || continue
552556
field_ordering = :unspecified
553557
if isexpr(stmt, :new)
@@ -663,7 +667,7 @@ function linear_pass!(ir::IRCode)
663667
non_dce_finish!(compact)
664668
simple_dce!(compact)
665669
ir = complete(compact)
666-
return ir, memory_opt
670+
return ir, memory_opt, imarray_memory_opt
667671
end
668672

669673
function form_new_preserves(origex::Expr, intermediates::Vector{Int}, new_preserves::Vector{Any})
@@ -1761,76 +1765,21 @@ function cfg_simplify!(ir::IRCode)
17611765
return finish(compact)
17621766
end
17631767

1764-
function is_allocation(stmt)
1765-
isexpr(stmt, :foreigncall) || return false
1766-
s = stmt.args[1]
1767-
isa(s, QuoteNode) && (s = s.value)
1768-
return s === :jl_alloc_array_1d
1769-
end
1770-
1771-
function memory_opt!(ir::IRCode)
1772-
compact = IncrementalCompact(ir, false)
1773-
uses = IdDict{Int, Vector{Int}}()
1774-
relevant = IdSet{Int}()
1775-
revisit = Int[]
1776-
function mark_val(val)
1777-
isa(val, SSAValue) || return
1778-
val.id in relevant && pop!(relevant, val.id)
1779-
end
1780-
for ((_, idx), stmt) in compact
1781-
if isa(stmt, ReturnNode)
1782-
isdefined(stmt, :val) || continue
1783-
val = stmt.val
1784-
if isa(val, SSAValue) && val.id in relevant
1785-
(haskey(uses, val.id)) || (uses[val.id] = Int[])
1786-
push!(uses[val.id], idx)
1787-
end
1788-
continue
1789-
end
1790-
(isexpr(stmt, :call) || isexpr(stmt, :foreigncall)) || continue
1791-
if is_allocation(stmt)
1792-
push!(relevant, idx)
1793-
# TODO: Mark everything else here
1794-
continue
1795-
end
1796-
# TODO: Replace this by interprocedural escape analysis
1797-
if is_known_call(stmt, arrayset, compact)
1798-
# The value being set escapes, everything else doesn't
1799-
mark_val(stmt.args[4])
1800-
arr = stmt.args[3]
1801-
if isa(arr, SSAValue) && arr.id in relevant
1802-
(haskey(uses, arr.id)) || (uses[arr.id] = Int[])
1803-
push!(uses[arr.id], idx)
1804-
end
1805-
elseif is_known_call(stmt, Core.arrayfreeze, compact) && isa(stmt.args[2], SSAValue)
1806-
push!(revisit, idx)
1807-
else
1808-
# For now we assume everything escapes
1809-
# TODO: We could handle PhiNodes specially and improve this
1810-
for ur in userefs(stmt)
1811-
mark_val(ur[])
1812-
end
1813-
end
1814-
end
1815-
ir = finish(compact)
1816-
isempty(revisit) && return ir
1817-
domtree = construct_domtree(ir.cfg.blocks)
1818-
for idx in revisit
1819-
# Make sure that the value we reference didn't escape
1820-
id = ir.stmts[idx][:inst].args[2].id
1821-
(id in relevant) || continue
1822-
1823-
# We're ok to steal the memory if we don't dominate any uses
1824-
ok = true
1825-
for use in uses[id]
1826-
if ssadominates(ir, domtree, idx, use)
1827-
ok = false
1828-
break
1768+
function imarray_memoryopt_pass!(ir::IRCode, estate::EscapeState)
1769+
# mark statements that possibly can be optimized
1770+
for idx in 1:length(ir.stmts)
1771+
stmt = ir.stmts[idx][:inst]
1772+
isexpr(stmt, :call) || continue
1773+
if is_known_call(stmt, Core.arrayfreeze, ir)
1774+
# array as SSA value might have been initialized within this frame
1775+
# (thus potentially doesn't escape to anywhere)
1776+
ary = stmt.args[2]
1777+
if isa(ary, SSAValue)
1778+
# if array doesn't escape, we can just change the tag and avoid allocation
1779+
has_no_escape(estate[ary]) || continue
1780+
stmt.args[1] = GlobalRef(Core, :mutating_arrayfreeze)
18291781
end
18301782
end
1831-
ok || continue
1832-
1833-
ir.stmts[idx][:inst].args[1] = Core.mutating_arrayfreeze
18341783
end
18351784
return ir
1836-
end
1785+
end

0 commit comments

Comments
 (0)