Skip to content

Commit

Permalink
More aggressive allocation elimination.
Browse files Browse the repository at this point in the history
* Mutable allocations that aren't mutated or escaped
* Allocations that aren't used (Fix #12415)
* Allocations of fields that aren't used or escaped
  • Loading branch information
yuyichao committed Apr 23, 2016
1 parent b12c9fb commit 7e8290e
Showing 1 changed file with 50 additions and 25 deletions.
75 changes: 50 additions & 25 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3149,7 +3149,7 @@ function _getfield_elim_pass!(e::Expr, sv)
e1 = e.args[2]
j = e.args[3]
if isa(e1,Expr)
alloc = is_immutable_allocation(e1, sv)
alloc = is_allocation(e1, sv)
if !is(alloc, false)
flen, fnames = alloc
if isa(j,QuoteNode)
Expand Down Expand Up @@ -3184,16 +3184,16 @@ end

_getfield_elim_pass!(e::ANY, sv) = e

# check if e is a successful allocation of an immutable struct
# check if e is a successful allocation of an struct
# if it is, returns (n,f) such that it is always valid to call
# getfield(..., 1 <= x <= n) or getfield(..., x in f) on the result
function is_immutable_allocation(e :: ANY, sv::InferenceState)
function is_allocation(e :: ANY, sv::InferenceState)
isa(e, Expr) || return false
if is_known_call(e, tuple, sv)
return (length(e.args)-1,())
elseif e.head === :new
typ = widenconst(exprtype(e, sv))
if isleaftype(typ) && !typ.mutable
if isleaftype(typ)
@assert(isa(typ,DataType))
nf = length(e.args)-1
names = fieldnames(typ)
Expand All @@ -3210,7 +3210,7 @@ function is_immutable_allocation(e :: ANY, sv::InferenceState)
false
end

# eliminate allocation of unnecessary immutables
# eliminate allocation of unnecessary objects
# that are only used as arguments to safe getfield calls
function alloc_elim_pass!(linfo::LambdaInfo, sv::InferenceState)
body = linfo.code
Expand All @@ -3221,41 +3221,66 @@ function alloc_elim_pass!(linfo::LambdaInfo, sv::InferenceState)
i = 1
while i < length(body)
e = body[i]
if !(isa(e,Expr) && is(e.head,:(=)) && (isa(e.args[1], GenSym) ||
(isa(e.args[1],Slot) && haskey(vs, e.args[1].id))))
if !isa(e, Expr)
i += 1
continue
end
var = e.args[1]
rhs = e.args[2]
alloc = is_immutable_allocation(rhs, sv)
e = e::Expr
if is(e.head,:(=)) && (isa(e.args[1], GenSym) ||
(isa(e.args[1],Slot) && haskey(vs, e.args[1].id)))
var = e.args[1]
rhs = e.args[2]
else
var = nothing
rhs = e
end
alloc = is_allocation(rhs, sv)
if !is(alloc,false)
nv, field_names = alloc
tup = rhs.args
if occurs_outside_getfield(bexpr, var, sv, nv, field_names)
# This makes sure the value doesn't escape so we can elide
# allocation of mutable types too
if (!is(var, nothing) &&
occurs_outside_getfield(bexpr, var, sv, nv, field_names))
i += 1
continue
end

deleteat!(body, i) # remove tuple allocation
# convert tuple allocation to a series of local var assignments
vals = cell(nv)
n_ins = 0
for j=1:nv
tupelt = tup[j+1]
if isa(tupelt,Number) || isa(tupelt,AbstractString) || isa(tupelt,QuoteNode)
vals[j] = tupelt
else
elty = exprtype(tupelt,sv)
tmpv = newvar!(sv, elty)
tmp = Expr(:(=), tmpv, tupelt)
insert!(body, i+n_ins, tmp)
vals[j] = tmpv
n_ins += 1
if is(var, nothing)
for j=1:nv
tupelt = tup[j+1]
if !(isa(tupelt,Number) || isa(tupelt,AbstractString) ||
isa(tupelt,QuoteNode) || isa(tupelt, GenSym))
insert!(body, i+n_ins, tupelt)
n_ins += 1
end
end
else
vals = cell(nv)
for j=1:nv
tupelt = tup[j+1]
if (isa(tupelt,Number) || isa(tupelt,AbstractString) ||
isa(tupelt,QuoteNode) || isa(tupelt, GenSym))
vals[j] = tupelt
else
elty = exprtype(tupelt,sv)
tmpv = newvar!(sv, elty)
tmp = Expr(:(=), tmpv, tupelt)
insert!(body, i+n_ins, tmp)
vals[j] = tmpv
n_ins += 1
end
end
replace_getfield!(linfo, bexpr, var, vals, field_names,
sv, i + n_ins)
end
i += n_ins
replace_getfield!(linfo, bexpr, var, vals, field_names, sv, i)
# Do not increment counter and do the optimization recursively
# on the allocation of fields too.
# This line can probably be added back for linear IR
# i += n_ins
else
i += 1
end
Expand Down

0 comments on commit 7e8290e

Please sign in to comment.