Skip to content

Commit 4ace972

Browse files
authored
Track GlobalRef consistently (#107)
Currently dependencies for `GlobalRef` and `Symbol` are tracked separately. Unfortunately, our lowering is not consistent about which one it uses (after lowering, all `Symbols` refer to globals in the current module), so the same variable can appear twice in the same expression, once as a `GlobalRef`, once as a symbol. Currently, in that situation, this package misses a dependency, because it considers them two separate objects. Fix that by always normalizing to `GlobalRef`. This does require the caller to pass through a Module, but the alternative, would be for the caller to keep track of these dependencies implicitly, which would be painful. I think consistently using `GlobalRef` here would be cleanest.
1 parent ac18aed commit 4ace972

File tree

4 files changed

+98
-76
lines changed

4 files changed

+98
-76
lines changed

src/codeedges.jl

+53-37
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const NamedVar = Union{Symbol,GlobalRef}
2-
31
## Phase 1: direct links
42

53
# There are 3 types of entities to track: ssavalues (line/statement numbers), slots, and named objects.
@@ -9,42 +7,44 @@ const NamedVar = Union{Symbol,GlobalRef}
97
struct Links
108
ssas::Vector{Int}
119
slots::Vector{Int}
12-
names::Vector{NamedVar}
10+
names::Vector{GlobalRef}
1311
end
14-
Links() = Links(Int[], Int[], NamedVar[])
12+
Links() = Links(Int[], Int[], GlobalRef[])
1513

1614
function Base.show(io::IO, l::Links)
1715
print(io, "ssas: ", showempty(l.ssas),
1816
", slots: ", showempty(l.slots),
1917
", names: ")
20-
print(IOContext(io, :typeinfo=>Vector{NamedVar}), showempty(l.names))
18+
print(IOContext(io, :typeinfo=>Vector{GlobalRef}), showempty(l.names))
2119
print(io, ';')
2220
end
2321

2422
struct CodeLinks
23+
thismod::Module
2524
ssapreds::Vector{Links}
2625
ssasuccs::Vector{Links}
2726
slotpreds::Vector{Links}
2827
slotsuccs::Vector{Links}
2928
slotassigns::Vector{Vector{Int}}
30-
namepreds::Dict{NamedVar,Links}
31-
namesuccs::Dict{NamedVar,Links}
32-
nameassigns::Dict{NamedVar,Vector{Int}}
29+
namepreds::Dict{GlobalRef,Links}
30+
namesuccs::Dict{GlobalRef,Links}
31+
nameassigns::Dict{GlobalRef,Vector{Int}}
3332
end
34-
function CodeLinks(nlines::Int, nslots::Int)
33+
function CodeLinks(thismod::Module, nlines::Int, nslots::Int)
3534
makelinks(n) = [Links() for _ = 1:n]
3635

37-
return CodeLinks(makelinks(nlines),
36+
return CodeLinks(thismod,
37+
makelinks(nlines),
3838
makelinks(nlines),
3939
makelinks(nslots),
4040
makelinks(nslots),
4141
[Int[] for _ = 1:nslots],
42-
Dict{NamedVar,Links}(),
43-
Dict{NamedVar,Links}(),
44-
Dict{NamedVar,Vector{Int}}())
42+
Dict{GlobalRef,Links}(),
43+
Dict{GlobalRef,Links}(),
44+
Dict{GlobalRef,Vector{Int}}())
4545
end
46-
function CodeLinks(src::CodeInfo)
47-
cl = CodeLinks(length(src.code), length(src.slotnames))
46+
function CodeLinks(thismod::Module, src::CodeInfo)
47+
cl = CodeLinks(thismod, length(src.code), length(src.slotnames))
4848
direct_links!(cl, src)
4949
end
5050

@@ -175,7 +175,7 @@ end
175175

176176

177177
function namedkeys(cl::CodeLinks)
178-
ukeys = Set{NamedVar}()
178+
ukeys = Set{GlobalRef}()
179179
for c in (cl.namepreds, cl.namesuccs, cl.nameassigns)
180180
for k in keys(c)
181181
push!(ukeys, k)
@@ -203,20 +203,23 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
203203
end
204204
end
205205

206-
P = Pair{Union{SSAValue,SlotNumber,NamedVar},Links}
206+
P = Pair{Union{SSAValue,SlotNumber,GlobalRef},Links}
207207

208208
for (i, stmt) in enumerate(src.code)
209209
if isexpr(stmt, :thunk) && isa(stmt.args[1], CodeInfo)
210-
icl = CodeLinks(stmt.args[1])
210+
icl = CodeLinks(cl.thismod, stmt.args[1])
211211
add_inner!(cl, icl, i)
212212
continue
213213
elseif isa(stmt, Expr) && stmt.head trackedheads
214214
if stmt.head === :method && length(stmt.args) === 3 && isa(stmt.args[3], CodeInfo)
215-
icl = CodeLinks(stmt.args[3])
215+
icl = CodeLinks(cl.thismod, stmt.args[3])
216216
add_inner!(cl, icl, i)
217217
end
218218
name = stmt.args[1]
219-
if isa(name, Symbol)
219+
if isa(name, GlobalRef) || isa(name, Symbol)
220+
if isa(name, Symbol)
221+
name = GlobalRef(cl.thismod, name)
222+
end
220223
assign = get(cl.nameassigns, name, nothing)
221224
if assign === nothing
222225
cl.nameassigns[name] = assign = Int[]
@@ -228,6 +231,10 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
228231
end
229232
target = P(name, targetstore)
230233
add_links!(target, stmt, cl)
234+
elseif name in (nothing, false)
235+
else
236+
@show stmt
237+
error("name ", typeof(name), " not recognized")
231238
end
232239
rhs = stmt
233240
target = P(SSAValue(i), cl.ssapreds[i])
@@ -240,7 +247,10 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
240247
id = lhs.id
241248
target = P(SlotNumber(id), cl.slotpreds[id])
242249
push!(cl.slotassigns[id], i)
243-
elseif isa(lhs, NamedVar)
250+
elseif isa(lhs, GlobalRef) || isa(lhs, Symbol)
251+
if isa(lhs, Symbol)
252+
lhs = GlobalRef(cl.thismod, lhs)
253+
end
244254
targetstore = get(cl.namepreds, lhs, nothing)
245255
if targetstore === nothing
246256
cl.namepreds[lhs] = targetstore = Links()
@@ -263,9 +273,9 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
263273
return cl
264274
end
265275

266-
function add_links!(target::Pair{Union{SSAValue,SlotNumber,NamedVar},Links}, @nospecialize(stmt), cl::CodeLinks)
276+
function add_links!(target::Pair{Union{SSAValue,SlotNumber,GlobalRef},Links}, @nospecialize(stmt), cl::CodeLinks)
267277
_targetid, targetstore = target
268-
targetid = _targetid::Union{SSAValue,SlotNumber,NamedVar}
278+
targetid = _targetid::Union{SSAValue,SlotNumber,GlobalRef}
269279
# Adds bidirectional edges
270280
if @isssa(stmt)
271281
stmt = stmt::AnySSAValue
@@ -275,7 +285,10 @@ function add_links!(target::Pair{Union{SSAValue,SlotNumber,NamedVar},Links}, @no
275285
stmt = stmt::AnySlotNumber
276286
push!(targetstore, SlotNumber(stmt.id))
277287
push!(cl.slotsuccs[stmt.id], targetid)
278-
elseif isa(stmt, Symbol) || isa(stmt, GlobalRef) # NamedVar
288+
elseif isa(stmt, GlobalRef) || isa(stmt, Symbol)
289+
if isa(stmt, Symbol)
290+
stmt = GlobalRef(cl.thismod, stmt)
291+
end
279292
push!(targetstore, stmt)
280293
namestore = get(cl.namesuccs, stmt, nothing)
281294
if namestore === nothing
@@ -311,7 +324,7 @@ function Base.push!(l::Links, id)
311324
k = id.id
312325
k l.slots && push!(l.slots, k)
313326
else
314-
id = id::NamedVar
327+
id = id::GlobalRef
315328
id l.names && push!(l.names, id)
316329
end
317330
return id
@@ -355,9 +368,9 @@ end
355368
struct CodeEdges
356369
preds::Vector{Vector{Int}}
357370
succs::Vector{Vector{Int}}
358-
byname::Dict{NamedVar,Variable}
371+
byname::Dict{GlobalRef,Variable}
359372
end
360-
CodeEdges(n::Integer) = CodeEdges([Int[] for i = 1:n], [Int[] for i = 1:n], Dict{Union{GlobalRef,Symbol},Variable}())
373+
CodeEdges(n::Integer) = CodeEdges([Int[] for i = 1:n], [Int[] for i = 1:n], Dict{GlobalRef,Variable}())
361374

362375
function Base.show(io::IO, edges::CodeEdges)
363376
println(io, "CodeEdges:")
@@ -383,10 +396,10 @@ Analyze `src` and determine the chain of dependencies.
383396
- `edges.preds[i]` lists the preceding statements that statement `i` depends on.
384397
- `edges.succs[i]` lists the succeeding statements that depend on statement `i`.
385398
- `edges.byname[v]` returns information about the predecessors, successors, and assignment statements
386-
for an object `v::$NamedVar`.
399+
for an object `v::GlobalRef`.
387400
"""
388-
function CodeEdges(src::CodeInfo)
389-
cl = CodeLinks(src)
401+
function CodeEdges(mod::Module, src::CodeInfo)
402+
cl = CodeLinks(mod, src)
390403
CodeEdges(src, cl)
391404
end
392405

@@ -412,7 +425,10 @@ function CodeEdges(src::CodeInfo, cl::CodeLinks)
412425
id = lhs.id
413426
linkpreds, linksuccs, listassigns = cl.slotpreds[id], cl.slotsuccs[id], cl.slotassigns[id]
414427
else
415-
lhs = lhs::NamedVar
428+
lhs = lhs::Union{GlobalRef,Symbol}
429+
if lhs isa Symbol
430+
lhs = GlobalRef(cl.thismod, lhs)
431+
end
416432
linkpreds = get(cl.namepreds, lhs, emptylink)
417433
linksuccs = get(cl.namesuccs, lhs, emptylink)
418434
listassigns = get(cl.nameassigns, lhs, emptylist)
@@ -546,7 +562,7 @@ function terminal_preds(i::Int, edges::CodeEdges)
546562
end
547563

548564
"""
549-
isrequired = lines_required(obj::$NamedVar, src::CodeInfo, edges::CodeEdges)
565+
isrequired = lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges)
550566
isrequired = lines_required(idx::Int, src::CodeInfo, edges::CodeEdges)
551567
552568
Determine which lines might need to be executed to evaluate `obj` or the statement indexed by `idx`.
@@ -556,16 +572,16 @@ will end up skipping a subset of such statements, perhaps while repeating others
556572
557573
See also [`lines_required!`](@ref) and [`selective_eval!`](@ref).
558574
"""
559-
function lines_required(obj::NamedVar, src::CodeInfo, edges::CodeEdges; kwargs...)
575+
function lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges; kwargs...)
560576
isrequired = falses(length(edges.preds))
561-
objs = Set{NamedVar}([obj])
577+
objs = Set{GlobalRef}([obj])
562578
return lines_required!(isrequired, objs, src, edges; kwargs...)
563579
end
564580

565581
function lines_required(idx::Int, src::CodeInfo, edges::CodeEdges; kwargs...)
566582
isrequired = falses(length(edges.preds))
567583
isrequired[idx] = true
568-
objs = Set{NamedVar}()
584+
objs = Set{GlobalRef}()
569585
return lines_required!(isrequired, objs, src, edges; kwargs...)
570586
end
571587

@@ -583,7 +599,7 @@ For example, use `norequire = LoweredCodeUtils.exclude_named_typedefs(src, edges
583599
extracting method signatures and not evaluating new definitions.
584600
"""
585601
function lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges; kwargs...)
586-
objs = Set{NamedVar}()
602+
objs = Set{GlobalRef}()
587603
return lines_required!(isrequired, objs, src, edges; kwargs...)
588604
end
589605

@@ -643,7 +659,7 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
643659
end
644660

645661
function add_requests!(isrequired, objs, edges::CodeEdges, norequire)
646-
objsnew = Set{NamedVar}()
662+
objsnew = Set{GlobalRef}()
647663
for obj in objs
648664
add_obj!(isrequired, objsnew, obj, edges, norequire)
649665
end

src/packagedef.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ if ccall(:jl_generating_output, Cint, ()) == 1
4242
end
4343
lwr = Meta.lower(@__MODULE__, ex)
4444
src = lwr.args[1]
45-
edges = CodeEdges(src)
46-
isrequired = lines_required(:s, src, edges)
47-
lines_required(:s, src, edges; norequire=())
48-
lines_required(:s, src, edges; norequire=exclude_named_typedefs(src, edges))
45+
edges = CodeEdges(@__MODULE__, src)
46+
isrequired = lines_required(GlobalRef(@__MODULE__, :s), src, edges)
47+
lines_required(GlobalRef(@__MODULE__, :s), src, edges; norequire=())
48+
lines_required(GlobalRef(@__MODULE__, :s), src, edges; norequire=exclude_named_typedefs(src, edges))
4949
for isreq in (isrequired, convert(Vector{Bool}, isrequired))
5050
lines_required!(isreq, src, edges; norequire=())
5151
lines_required!(isreq, src, edges; norequire=exclude_named_typedefs(src, edges))

src/utils.jl

+7-1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ function ismethod_with_name(src, stmt, target::AbstractString; reentrant::Bool=f
8585
name = src.code[name.id]
8686
elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.svec)
8787
name = name.args[2]
88+
elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.Typeof)
89+
name = name.args[2]
8890
elseif isexpr(name, :call) && is_quotenode_egal(name.args[1], Core.apply_type)
8991
for arg in name.args[2:end]
9092
ismethod_with_name(src, arg, target; reentrant=true) && return true
@@ -120,7 +122,11 @@ function isanonymous_typedef(stmt)
120122
stmt = isa(stmt.args[3], Core.SSAValue) ? src.code[end-3] : src.code[end-2]
121123
isexpr(stmt, :(=)) || return false
122124
name = stmt.args[1]
123-
isa(name, Symbol) || return false
125+
if isa(name, GlobalRef)
126+
name = name.name
127+
else
128+
isa(name, Symbol) || return false
129+
end
124130
else
125131
name = stmt.args[2]::Symbol
126132
end

0 commit comments

Comments
 (0)