Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 78 additions & 51 deletions src/codeedges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,10 @@ function postprint_linelinks(io::IO, idx::Int, src::CodeInfo, cl::CodeLinks, bbc
printstyled(io, bbchanged ? " " : "│", color=:light_black)
printstyled(io, " # ", color=:yellow)
stmt = src.code[idx]
if is_assignment_like(stmt)
lhs = normalize_defsig(stmt.args[1], cl.thismod)
lhs_rhs = get_lhs_rhs(stmt)
if lhs_rhs !== nothing
lhs, _ = lhs_rhs
lhs = normalize_defsig(lhs, cl.thismod)
if @issslotnum(lhs)
# id = lhs.id
# preds, succs = cl.slotpreds[id], cl.slotsuccs[id]
Expand All @@ -190,38 +192,51 @@ function namedkeys(cl::CodeLinks)
return ukeys
end

is_assignment_like(stmt::Expr) = isexpr(stmt, :(=)) || (isexpr(stmt, :const) && length(stmt.args) == 2)
is_assignment_like(@nospecialize stmt) = false
function get_lhs_rhs(@nospecialize stmt)
if isexpr(stmt, :(=))
return Pair{Any,Any}(stmt.args[1], stmt.args[2])
elseif isexpr(stmt, :const) && length(stmt.args) == 2
return Pair{Any,Any}(stmt.args[1], stmt.args[2])
elseif isexpr(stmt, :call) && length(stmt.args) == 4
f = stmt.args[1]
if isa(f, GlobalRef) && f.name === :setglobal!
mod = stmt.args[2]
mod isa Module || return nothing
name = stmt.args[3]
if name isa QuoteNode
name = name.value
end
name isa Symbol || return nothing
lhs = GlobalRef(mod, name)
return Pair{Any,Any}(lhs, stmt.args[4])
end
end
return nothing
end

function direct_links!(cl::CodeLinks, src::CodeInfo)
# Utility for when a stmt itself contains a CodeInfo
function add_inner!(cl::CodeLinks, icl::CodeLinks, idx)
for (name, _) in icl.nameassigns
assigns = get(cl.nameassigns, name, nothing)
if assigns === nothing
cl.nameassigns[name] = assigns = Int[]
end
assigns = get!(Vector{Int}, cl.nameassigns, name)
push!(assigns, idx)
end
for (name, _) in icl.namesuccs
succs = get(cl.namesuccs, name, nothing)
if succs === nothing
cl.namesuccs[name] = succs = Links()
end
succs = get!(Links, cl.namesuccs, name)
push!(succs.ssas, idx)
end
end

P = Pair{Union{SSAValue,SlotNumber,GlobalRef},Links}

for (i, stmt) in enumerate(src.code)
if isexpr(stmt, :thunk) && isa(stmt.args[1], CodeInfo)
icl = CodeLinks(cl.thismod, stmt.args[1])
if isexpr(stmt, :thunk) && (arg1 = stmt.args[1]; arg1 isa CodeInfo)
icl = CodeLinks(cl.thismod, arg1)
add_inner!(cl, icl, i)
continue
elseif isa(stmt, Expr) && stmt.head ∈ trackedheads
if stmt.head === :method && length(stmt.args) === 3 && isa(stmt.args[3], CodeInfo)
icl = CodeLinks(cl.thismod, stmt.args[3])
elseif isexpr(stmt, :method)
if length(stmt.args) === 3 && (arg3 = stmt.args[3]; arg3 isa CodeInfo)
icl = CodeLinks(cl.thismod, arg3)
add_inner!(cl, icl, i)
end
name = stmt.args[1]
Expand All @@ -234,10 +249,7 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
cl.nameassigns[name] = assign = Int[]
end
push!(assign, i)
targetstore = get(cl.namepreds, name, nothing)
if targetstore === nothing
cl.namepreds[name] = targetstore = Links()
end
targetstore = get!(Links, cl.namepreds, name)
target = P(name, targetstore)
add_links!(target, stmt, cl)
elseif name in (nothing, false)
Expand All @@ -247,10 +259,10 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
end
rhs = stmt
target = P(SSAValue(i), cl.ssapreds[i])
elseif is_assignment_like(stmt)
elseif (lhs_rhs = get_lhs_rhs(stmt); lhs_rhs !== nothing)
# An assignment
stmt = stmt::Expr
lhs, rhs = stmt.args[1], stmt.args[2]
lhs, rhs = lhs_rhs
if @issslotnum(lhs)
lhs = lhs::AnySlotNumber
id = lhs.id
Expand All @@ -260,10 +272,7 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
if isa(lhs, Symbol)
lhs = GlobalRef(cl.thismod, lhs)
end
targetstore = get(cl.namepreds, lhs, nothing)
if targetstore === nothing
cl.namepreds[lhs] = targetstore = Links()
end
targetstore = get!(Links, cl.namepreds, lhs)
target = P(lhs, targetstore)
assign = get(cl.nameassigns, lhs, nothing)
if assign === nothing
Expand Down Expand Up @@ -299,23 +308,35 @@ function add_links!(target::Pair{Union{SSAValue,SlotNumber,GlobalRef},Links}, @n
stmt = GlobalRef(cl.thismod, stmt)
end
push!(targetstore, stmt)
namestore = get(cl.namesuccs, stmt, nothing)
if namestore === nothing
cl.namesuccs[stmt] = namestore = Links()
end
namestore = get!(Links, cl.namesuccs, stmt)
push!(namestore, targetid)
elseif isa(stmt, Expr) && stmt.head !== :copyast
stmt = stmt::Expr
arng = 1:length(stmt.args)
if stmt.head === :call
f = stmt.args[1]
if !@isssa(f) && !@issslotnum(f)
# Avoid putting named callees on the namestore
arng = 2:length(stmt.args)
elseif isa(stmt, Expr)
if stmt.head === :globaldecl
for i = 1:length(stmt.args)
a = stmt.args[i]
if a isa GlobalRef
namestore = get!(Links, cl.namepreds, a) # TODO should this information be tracked in the separate `cl.namedecls` store?
push!(namestore, targetid)
if targetid isa SSAValue
push!(namestore, SSAValue(targetid.id+1)) # +1 for :latestworld
end
else
add_links!(target, a, cl)
end
end
elseif stmt.head !== :copyast
stmt = stmt::Expr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line seems like it must date to an era when inference was not as capable, though obviously it does no harm.

arng = 1:length(stmt.args)
if stmt.head === :call
f = stmt.args[1]
if !@isssa(f) && !@issslotnum(f)
# Avoid putting named callees on the namestore
arng = 2:length(stmt.args)
end
end
for i in arng
add_links!(target, stmt.args[i], cl)
end
end
for i in arng
add_links!(target, stmt.args[i], cl)
end
elseif stmt isa Core.GotoIfNot
add_links!(target, stmt.cond, cl)
Expand Down Expand Up @@ -362,7 +383,6 @@ struct Variable
preds::Vector{Int}
succs::Vector{Int}
end
Variable() = Variable(Int[], Int[], Int[])

function Base.show(io::IO, v::Variable)
print(io, "assigned on ", showempty(v.assigned))
Expand Down Expand Up @@ -423,9 +443,9 @@ function CodeEdges(src::CodeInfo, cl::CodeLinks)
emptylist = Int[]
for (i, stmt) in enumerate(src.code)
# Identify line predecents for slots and named variables
if is_assignment_like(stmt)
if (lhs_rhs = get_lhs_rhs(stmt); lhs_rhs !== nothing)
stmt = stmt::Expr
lhs = stmt.args[1]
lhs, _ = lhs_rhs
# Mark predecessors and successors of this line by following ssas & named assignments
if @issslotnum(lhs)
lhs = lhs::AnySlotNumber
Expand Down Expand Up @@ -611,7 +631,7 @@ function exclude_named_typedefs(src::CodeInfo, edges::CodeEdges)
i = 1
nstmts = length(src.code)
while i <= nstmts
stmt = rhs(src.code[i])
stmt = getrhs(src.code[i])
if istypedef(stmt) && !isanonymous_typedef(stmt::Expr)
r = typedef_range(src, i)
pushall!(norequire, r)
Expand Down Expand Up @@ -715,10 +735,17 @@ function add_succs!(isrequired, idx, edges::CodeEdges, succs, norequire)
end
return chngd
end
function add_obj!(isrequired, objs, obj, edges::CodeEdges, norequire)
function add_obj!(isrequired, objs, obj::GlobalRef, edges::CodeEdges, norequire)
chngd = false
for p in edges.byname[obj].preds
p ∈ norequire && continue
isrequired[p] && continue
isrequired[p] = true
chngd = true
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh interesting, I assume this was a bugfix?

for d in edges.byname[obj].assigned
d ∈ norequire && continue
isrequired[d] && continue
isrequired[d] || add_preds!(isrequired, d, edges, norequire)
isrequired[d] = true
chngd = true
Expand Down Expand Up @@ -871,7 +898,7 @@ function find_typedefs(src::CodeInfo)
i = 1
nstmts = length(src.code)
while i <= nstmts
stmt = rhs(src.code[i])
stmt = getrhs(src.code[i])
if istypedef(stmt) && !isanonymous_typedef(stmt::Expr)
stmt = stmt::Expr
r = typedef_range(src, i)
Expand Down Expand Up @@ -972,8 +999,8 @@ function add_inplace!(isrequired, src, edges, norequire)
for k in edges.preds[j]
isrequired[k] || continue
predstmt = src.code[k]
if is_assignment_like(predstmt)
lhs = predstmt.args[1]
if (lhs_rhs = get_lhs_rhs(predstmt); lhs_rhs !== nothing)
lhs, _ = lhs_rhs
if @issslotnum(lhs) && lhs.id == id
changed |= mark_if_inplace(stmt, j)
break
Expand Down Expand Up @@ -1056,7 +1083,7 @@ function print_with_code(io::IO, src::CodeInfo, isrequired::AbstractVector{Bool}
preprint(::IO) = nothing
preprint(io::IO, idx::Int) = (c = isrequired[idx]; printstyled(io, lpad(idx, nd), ' ', c ? "t " : "f "; color = c ? :cyan : :plain))
postprint(::IO) = nothing
postprint(io::IO, idx::Int, bbchanged::Bool) = nothing
postprint(::IO, idx::Int, bbchanged::Bool) = nothing

print_with_code(preprint, postprint, io, src)
end
Expand Down
1 change: 0 additions & 1 deletion src/packagedef.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ using Base.Meta: isexpr

const SSAValues = Union{Core.Compiler.SSAValue, JuliaInterpreter.SSAValue}

const trackedheads = (:method,)
const structdecls = (:_structtype, :_abstracttype, :_primitivetype)

export signature, rename_framemethods!, methoddef!, methoddefs!, bodymethod
Expand Down
27 changes: 15 additions & 12 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ function callee_matches(f, mod, sym)
return false
end

function rhs(stmt)
is_assignment_like(stmt) && return (stmt::Expr).args[2]
return stmt
function getrhs(@nospecialize(stmt))
lhs_rhs = get_lhs_rhs(stmt)
return lhs_rhs === nothing ? stmt : lhs_rhs[2]
end

ismethod(frame::Frame) = ismethod(pc_expr(frame))
Expand Down Expand Up @@ -107,7 +107,7 @@ function ismethod_with_name(src, stmt, target::AbstractString; reentrant::Bool=f
end

# anonymous function types are defined in a :thunk expr with a characteristic CodeInfo
function isanonymous_typedef(stmt)
function isanonymous_typedef(@nospecialize stmt)
if isa(stmt, Expr)
stmt.head === :thunk || return false
stmt = stmt.args[1]
Expand All @@ -119,21 +119,22 @@ function isanonymous_typedef(stmt)
isexpr(stmt, :call) || return false
is_global_ref(stmt.args[1], Core, :_typebody!) || return false
stmt = isa(stmt.args[3], Core.SSAValue) ? src.code[end-3] : src.code[end-2]
is_assignment_like(stmt) || return false
name = stmt.args[1]
if isa(name, GlobalRef)
name = name.name
lhs_rhs = get_lhs_rhs(stmt)
lhs_rhs === nothing && return false
lhs, _ = lhs_rhs
if isa(lhs, GlobalRef)
lhs = lhs.name
else
isa(name, Symbol) || return false
isa(lhs, Symbol) || return false
end
return startswith(String(name), "#")
return startswith(String(lhs), "#")
end
return false
end

function istypedef(stmt)
isa(stmt, Expr) || return false
stmt = rhs(stmt)
stmt = getrhs(stmt)
isa(stmt, Expr) || return false
@static if all(s->isdefined(Core,s), structdecls)
if stmt.head === :call
Expand Down Expand Up @@ -162,6 +163,7 @@ function typedef_range(src::CodeInfo, idx)
istart = idx
while istart >= 1
isexpr(src.code[istart], :global) && break
isexpr(src.code[istart], :latestworld) && break
istart -= 1
end
istart >= 1 || error("no initial :global found")
Expand All @@ -171,6 +173,7 @@ function typedef_range(src::CodeInfo, idx)
stmt = src.code[iend]
if isa(stmt, Expr)
stmt.head === :global && break
stmt.head === :latestworld && break
if stmt.head === :call
if (is_global_ref(stmt.args[1], Core, :_typebody!) || is_quotenode_egal(stmt.args[1], Core._typebody!))
have_typebody = true
Expand All @@ -179,7 +182,7 @@ function typedef_range(src::CodeInfo, idx)
# Advance to the type-assignment
while iend <= n
stmt = src.code[iend]
is_assignment_like(stmt) && break
get_lhs_rhs(stmt) !== nothing && break
iend += 1
end
end
Expand Down
Loading
Loading