Skip to content

Commit

Permalink
Merge f121363 into c716e63
Browse files Browse the repository at this point in the history
  • Loading branch information
JeffBezanson authored Mar 1, 2017
2 parents c716e63 + f121363 commit 5acf32b
Showing 1 changed file with 131 additions and 53 deletions.
184 changes: 131 additions & 53 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,8 @@ const workq = Vector{InferenceState}() # set of InferenceState objects that can

#### helper functions ####

@inline slot_id(s) = isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id # using a function to ensure we can infer this
@inline slot_id(s::Slot) =
isa(s, SlotNumber) ? (s::SlotNumber).id : (s::TypedSlot).id # using a function to ensure we can infer this

# avoid cycle due to over-specializing `any` when used by inference
function _any(f::ANY, a)
Expand Down Expand Up @@ -2114,7 +2115,7 @@ issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef)
# in a dead branch but can be ignored when analyzing uses/liveness.
is_meta_expr_head(head::Symbol) =
(head === :inbounds || head === :boundscheck || head === :meta ||
head === :line)
head === :line || head === :simdloop)
is_meta_expr(ex::Expr) = is_meta_expr_head(ex.head)

function tmerge(typea::ANY, typeb::ANY)
Expand Down Expand Up @@ -3359,7 +3360,8 @@ function is_pure_builtin(f::ANY)
f === Intrinsics.checked_srem_int ||
f === Intrinsics.checked_urem_int ||
f === Intrinsics.check_top_bit ||
f === Intrinsics.sqrt_llvm)
f === Intrinsics.sqrt_llvm ||
f === Intrinsics.cglobal) # cglobal throws an error for symbol-not-found
return true
end
end
Expand Down Expand Up @@ -3387,12 +3389,17 @@ function effect_free(e::ANY, src::CodeInfo, mod::Module, allow_volatile::Bool)
return (isdefined(e.mod, e.name) && (allow_volatile || isconst(e.mod, e.name)))
elseif isa(e, Symbol)
return allow_volatile
elseif isa(e, Slot)
return src.slotflags[slot_id(e)] & Slot_UsedUndef == 0
elseif isa(e, Expr)
e = e::Expr
head = e.head
if head === :static_parameter || is_meta_expr_head(head)
return true
end
if e.typ === Bottom
return false
end
ea = e.args
if head === :call && !isa(e.args[1], SSAValue) && !isa(e.args[1], Slot)
if is_known_call_p(e, is_pure_builtin, src, mod)
Expand Down Expand Up @@ -3453,8 +3460,7 @@ struct InvokeData
texpr
end

function inline_as_constant(val::ANY, argexprs, sv::InferenceState,
invoke_data::ANY)
function inline_as_constant(val::ANY, argexprs, sv::InferenceState, invoke_data::ANY)
if invoke_data === nothing
invoke_fexpr = nothing
invoke_texpr = nothing
Expand Down Expand Up @@ -3647,8 +3653,9 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference

if (f === typeassert || ft typeof(typeassert)) && length(atypes)==3
# typeassert(x::S, T) => x, when S<:T
if isType(atypes[3]) && isleaftype(atypes[3]) &&
atypes[2] atypes[3].parameters[1]
a3 = atypes[3]
if (isType(a3) && isleaftype(a3) && atypes[2] a3.parameters[1]) ||
(isa(a3,Const) && isa(a3.val,Type) && atypes[2] a3.val)
return (argexprs[2], ())
end
end
Expand All @@ -3657,7 +3664,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference
if sv.params.inlining
if isa(e.typ, Const) # || isconstType(e.typ)
if (f === apply_type || f === fieldtype || f === typeof ||
istopfunction(topmod, f, :typejoin) ||
istopfunction(topmod, f, :typejoin) || f === (===) ||
istopfunction(topmod, f, :isbits) ||
istopfunction(topmod, f, :promote_type) ||
(f === Core.kwfunc && length(argexprs) == 2))
Expand Down Expand Up @@ -4485,62 +4492,123 @@ function is_known_call_p(e::Expr, pred::ANY, src::CodeInfo, mod::Module)
return (isa(f, Const) && pred(f.val)) || (isType(f) && pred(f.parameters[1]))
end

function delete_var!(src::CodeInfo, id, T)
function record_used(e::ANY, T::ANY, used::Vector{Bool})
if isa(e,T)
used[e.id+1] = true
elseif isa(e,Expr)
i0 = e.head === :(=) ? 2 : 1
for i = i0:length(e.args)
record_used(e.args[i], T, used)
end
end
end

function remove_unused_vars!(src::CodeInfo)
used = fill(false, length(src.slotnames)+1)
used_ssa = fill(false, length(src.ssavaluetypes)+1)
for i = 1:length(src.code)
record_used(src.code[i], Slot, used)
record_used(src.code[i], SSAValue, used_ssa)
end
for i = 1:length(src.code)
e = src.code[i]
if isa(e,NewvarNode) && !used[e.slot.id+1]
src.code[i] = nothing
elseif isa(e,Expr) && e.head === :(=)
if (isa(e.args[1],Slot) && !used[e.args[1].id+1]) ||
(isa(e.args[1],SSAValue) && !used_ssa[e.args[1].id+1])
src.code[i] = e.args[2]
end
end
end
end

function var_matches(a::Union{Slot,SSAValue}, b::Union{Slot,SSAValue})
return ((isa(a,SSAValue) && isa(b,SSAValue)) || (isa(a,Slot) && isa(b,Slot))) && a.id == b.id
end

var_matches(a::ANY, b::ANY) = false

function delete_var!(src::CodeInfo, v::Union{Slot,SSAValue})
filter!(x->!(isa(x,Expr) && (x.head === :(=) || x.head === :const) &&
isa(x.args[1],T) && x.args[1].id == id),
var_matches(x.args[1], v)),
src.code)
return src
end

function slot_replace!(src::CodeInfo, id::Int, rhs::ANY, T::ANY)
function slot_replace!(src::CodeInfo, v::Union{Slot,SSAValue}, rhs::ANY)
for i = 1:length(src.code)
src.code[i] = _slot_replace!(src.code[i], id, rhs, T)
src.code[i] = _slot_replace!(src.code[i], v, rhs)
end
return src
end

function _slot_replace!(e, id::Int, rhs::ANY, T::ANY)
if isa(e,T) && e.id == id
function _slot_replace!(e::ANY, v::Union{Slot,SSAValue}, rhs::ANY)
if var_matches(e, v)
return rhs
end
if isa(e,Expr)
for i = 1:length(e.args)
e.args[i] = _slot_replace!(e.args[i], id, rhs, T)
e.args[i] = _slot_replace!(e.args[i], v, rhs)
end
end
return e
end

occurs_undef(var::Int, expr, flags) =
flags[var] & Slot_UsedUndef != 0 && occurs_more(expr, e -> (isa(e, Slot) && slot_id(e) == var), 0) > 0

is_argument(nargs::Int, v::Slot) = slot_id(v) <= nargs

normslot(s::SlotNumber) = s
normslot(s::TypedSlot) = SlotNumber(slot_id(s))

# given a single-assigned var and its initializer `init`, return what we can
# replace `var` with, or `var` itself if we shouldn't replace it
function get_replacement(table, var::Union{SlotNumber, SSAValue}, init::ANY, nargs, slottypes, ssavaluetypes)
#if isa(init, QuoteNode) # this can cause slight code size increases
# return init
if isa(init, Expr) && init.head === :static_parameter
return init
elseif isa(init, Slot) && is_argument(nargs, init::Slot)
# the transformation is not ideal if the assignment
# is present for the auto-unbox functionality
# (from inlining improved type inference information)
# and this transformation would worsen the type information
# everywhere later in the function
ityp = isa(init, TypedSlot) ? init.typ : slottypes[(init::SlotNumber).id]
if ityp (isa(var,SSAValue) ? ssavaluetypes[var.id + 1] : slottypes[var.id])
return init
end
elseif isa(init, SSAValue)
if haskey(table, init)
return get_replacement(table, init, table[init], nargs, slottypes, ssavaluetypes)
end
return init
elseif isa(init, SlotNumber) && haskey(table, init)
return get_replacement(table, init, table[init], nargs, slottypes, ssavaluetypes)
elseif isa(init, TypedSlot)
sl = normslot(init)
if haskey(table, sl)
rep = get_replacement(table, sl, table[sl], nargs, slottypes, ssavaluetypes)
if isa(rep, SlotNumber)
rep = TypedSlot(rep.id, init.typ)
end
return rep
end
end
return var
end

# remove all single-assigned vars v in "v = x" where x is an argument.
# "sa" is the result of find_sa_vars
# T: Slot or SSAValue
function remove_redundant_temp_vars(src::CodeInfo, nargs::Int, sa, T)
function remove_redundant_temp_vars!(src::CodeInfo, nargs::Int, sa)
flags = src.slotflags
ssavalue_types = src.ssavaluetypes
bexpr = Expr(:block)
bexpr.args = src.code
slottypes = src.slottypes
ssavaluetypes = src.ssavaluetypes
for (v, init) in sa
if isa(init, Slot) && is_argument(nargs, init::Slot)
# this transformation is not valid for vars used before def.
# we need to preserve the point of assignment to know where to
# throw errors (issue #4645).
if T === SSAValue || !occurs_undef(v, bexpr, flags)
# the transformation is not ideal if the assignment
# is present for the auto-unbox functionality
# (from inlining improved type inference information)
# and this transformation would worsen the type information
# everywhere later in the function
ityp = isa(init, TypedSlot) ? init.typ : src.slottypes[(init::SlotNumber).id]
if ityp (T === SSAValue ? ssavalue_types[v + 1] : src.slottypes[v])
delete_var!(src, v, T)
slot_replace!(src, v, init, T)
end
end
repl = get_replacement(sa, v, init, nargs, slottypes, ssavaluetypes)
compare = isa(repl,TypedSlot) ? normslot(repl) : repl
if compare !== v
delete_var!(src, v)
slot_replace!(src, v, repl)
end
end
return src
Expand All @@ -4551,27 +4619,31 @@ function find_sa_vars(src::CodeInfo, nargs::Int)
body = src.code
av = ObjectIdDict()
av2 = ObjectIdDict()
gss = ObjectIdDict()
for i = 1:length(body)
e = body[i]
if isa(e,Expr) && e.head === :(=)
lhs = e.args[1]
if isa(lhs, SSAValue)
gss[lhs.id] = e.args[2]
av[lhs] = e.args[2]
elseif isa(lhs, Slot)
id = slot_id(lhs)
if id > nargs # exclude args
if !haskey(av, id)
av[id] = e.args[2]
lhs = normslot(lhs)
id = lhs.id
# exclude args and used undef vars
# this transformation is not valid for vars used before def.
# we need to preserve the point of assignment to know where to
# throw errors (issue #4645).
if id > nargs && (src.slotflags[id] & Slot_UsedUndef == 0)
if !haskey(av, lhs)
av[lhs] = e.args[2]
else
av2[id] = true
av2[lhs] = true
end
end
end
end
end
filter!((id, _) -> !haskey(av2, id), av)
return (av, gss)
filter!((v, _) -> !haskey(av2, v), av)
return av
end

symequal(x::SSAValue, y::SSAValue) = x.id === y.id
Expand Down Expand Up @@ -4632,7 +4704,13 @@ function void_use_elim_pass!(sv::InferenceState)
elseif isa(ex, GlobalRef)
ex = ex::GlobalRef
return !isdefined(ex.mod, ex.name)
elseif (isa(ex, Expr) || isa(ex, GotoNode) || isa(ex, LineNumberNode) ||
elseif isa(ex, Expr)
h = ex.head
if h === :return || h === :(=) || h === :gotoifnot || is_meta_expr_head(h)
return true
end
return !effect_free(ex, sv.src, sv.mod, false)
elseif (isa(ex, GotoNode) || isa(ex, LineNumberNode) ||
isa(ex, NewvarNode) || isa(ex, Symbol) || isa(ex, LabelNode))
# This is a list of special type handled by the compiler
return true
Expand Down Expand Up @@ -5009,9 +5087,9 @@ function alloc_elim_pass!(sv::InferenceState)
body = sv.src.code
bexpr = Expr(:block)
bexpr.args = body
vs, gs = find_sa_vars(sv.src, sv.nargs)
remove_redundant_temp_vars(sv.src, sv.nargs, vs, Slot)
remove_redundant_temp_vars(sv.src, sv.nargs, gs, SSAValue)
vs = find_sa_vars(sv.src, sv.nargs)
remove_redundant_temp_vars!(sv.src, sv.nargs, vs)
remove_unused_vars!(sv.src)
i = 1
while i < length(body)
e = body[i]
Expand All @@ -5021,7 +5099,7 @@ function alloc_elim_pass!(sv::InferenceState)
end
e = e::Expr
if e.head === :(=) && (isa(e.args[1], SSAValue) ||
(isa(e.args[1], Slot) && haskey(vs, slot_id(e.args[1]))))
(isa(e.args[1], Slot) && haskey(vs, normslot(e.args[1]))))
var = e.args[1]
rhs = e.args[2]
# Need to make sure LLVM can recognize this as LLVM ssa value too
Expand Down

0 comments on commit 5acf32b

Please sign in to comment.