Skip to content

Commit 16d12ce

Browse files
committed
Track GlobalRef consistently
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 usign `GlobalRef` here is cleanest.
1 parent 34f7f11 commit 16d12ce

File tree

3 files changed

+57
-36
lines changed

3 files changed

+57
-36
lines changed

src/codeedges.jl

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,44 @@ const NamedVar = Union{Symbol,GlobalRef}
99
struct Links
1010
ssas::Vector{Int}
1111
slots::Vector{Int}
12-
names::Vector{NamedVar}
12+
names::Vector{GlobalRef}
1313
end
14-
Links() = Links(Int[], Int[], NamedVar[])
14+
Links() = Links(Int[], Int[], GlobalRef[])
1515

1616
function Base.show(io::IO, l::Links)
1717
print(io, "ssas: ", showempty(l.ssas),
1818
", slots: ", showempty(l.slots),
1919
", names: ")
20-
print(IOContext(io, :typeinfo=>Vector{NamedVar}), showempty(l.names))
20+
print(IOContext(io, :typeinfo=>Vector{GlobalRef}), showempty(l.names))
2121
print(io, ';')
2222
end
2323

2424
struct CodeLinks
25+
thismod::Module
2526
ssapreds::Vector{Links}
2627
ssasuccs::Vector{Links}
2728
slotpreds::Vector{Links}
2829
slotsuccs::Vector{Links}
2930
slotassigns::Vector{Vector{Int}}
30-
namepreds::Dict{NamedVar,Links}
31-
namesuccs::Dict{NamedVar,Links}
32-
nameassigns::Dict{NamedVar,Vector{Int}}
31+
namepreds::Dict{GlobalRef,Links}
32+
namesuccs::Dict{GlobalRef,Links}
33+
nameassigns::Dict{GlobalRef,Vector{Int}}
3334
end
34-
function CodeLinks(nlines::Int, nslots::Int)
35+
function CodeLinks(thismod::Module, nlines::Int, nslots::Int)
3536
makelinks(n) = [Links() for _ = 1:n]
3637

37-
return CodeLinks(makelinks(nlines),
38+
return CodeLinks(thismod,
39+
makelinks(nlines),
3840
makelinks(nlines),
3941
makelinks(nslots),
4042
makelinks(nslots),
4143
[Int[] for _ = 1:nslots],
42-
Dict{NamedVar,Links}(),
43-
Dict{NamedVar,Links}(),
44-
Dict{NamedVar,Vector{Int}}())
44+
Dict{GlobalRef,Links}(),
45+
Dict{GlobalRef,Links}(),
46+
Dict{GlobalRef,Vector{Int}}())
4547
end
46-
function CodeLinks(src::CodeInfo)
47-
cl = CodeLinks(length(src.code), length(src.slotnames))
48+
function CodeLinks(thismod::Module, src::CodeInfo)
49+
cl = CodeLinks(thismod, length(src.code), length(src.slotnames))
4850
direct_links!(cl, src)
4951
end
5052

@@ -203,20 +205,23 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
203205
end
204206
end
205207

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

208210
for (i, stmt) in enumerate(src.code)
209211
if isexpr(stmt, :thunk) && isa(stmt.args[1], CodeInfo)
210-
icl = CodeLinks(stmt.args[1])
212+
icl = CodeLinks(cl.thismod, stmt.args[1])
211213
add_inner!(cl, icl, i)
212214
continue
213215
elseif isa(stmt, Expr) && stmt.head trackedheads
214216
if stmt.head === :method && length(stmt.args) === 3 && isa(stmt.args[3], CodeInfo)
215-
icl = CodeLinks(stmt.args[3])
217+
icl = CodeLinks(cl.thismod, stmt.args[3])
216218
add_inner!(cl, icl, i)
217219
end
218220
name = stmt.args[1]
219-
if isa(name, Symbol)
221+
if isa(name, NamedVar)
222+
if isa(name, Symbol)
223+
name = GlobalRef(cl.thismod, name)
224+
end
220225
assign = get(cl.nameassigns, name, nothing)
221226
if assign === nothing
222227
cl.nameassigns[name] = assign = Int[]
@@ -228,6 +233,10 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
228233
end
229234
target = P(name, targetstore)
230235
add_links!(target, stmt, cl)
236+
elseif name in (nothing, false)
237+
else
238+
@show stmt
239+
error("name ", typeof(name), " not recognized")
231240
end
232241
rhs = stmt
233242
target = P(SSAValue(i), cl.ssapreds[i])
@@ -263,9 +272,9 @@ function direct_links!(cl::CodeLinks, src::CodeInfo)
263272
return cl
264273
end
265274

266-
function add_links!(target::Pair{Union{SSAValue,SlotNumber,NamedVar},Links}, @nospecialize(stmt), cl::CodeLinks)
275+
function add_links!(target::Pair{Union{SSAValue,SlotNumber,GlobalRef},Links}, @nospecialize(stmt), cl::CodeLinks)
267276
_targetid, targetstore = target
268-
targetid = _targetid::Union{SSAValue,SlotNumber,NamedVar}
277+
targetid = _targetid::Union{SSAValue,SlotNumber,GlobalRef}
269278
# Adds bidirectional edges
270279
if @isssa(stmt)
271280
stmt = stmt::AnySSAValue
@@ -275,7 +284,10 @@ function add_links!(target::Pair{Union{SSAValue,SlotNumber,NamedVar},Links}, @no
275284
stmt = stmt::AnySlotNumber
276285
push!(targetstore, SlotNumber(stmt.id))
277286
push!(cl.slotsuccs[stmt.id], targetid)
278-
elseif isa(stmt, Symbol) || isa(stmt, GlobalRef) # NamedVar
287+
elseif isa(stmt, NamedVar) # NamedVar
288+
if isa(stmt, Symbol)
289+
stmt = GlobalRef(cl.thismod, stmt)
290+
end
279291
push!(targetstore, stmt)
280292
namestore = get(cl.namesuccs, stmt, nothing)
281293
if namestore === nothing
@@ -355,7 +367,7 @@ end
355367
struct CodeEdges
356368
preds::Vector{Vector{Int}}
357369
succs::Vector{Vector{Int}}
358-
byname::Dict{NamedVar,Variable}
370+
byname::Dict{GlobalRef,Variable}
359371
end
360372
CodeEdges(n::Integer) = CodeEdges([Int[] for i = 1:n], [Int[] for i = 1:n], Dict{Union{GlobalRef,Symbol},Variable}())
361373

@@ -385,8 +397,8 @@ Analyze `src` and determine the chain of dependencies.
385397
- `edges.byname[v]` returns information about the predecessors, successors, and assignment statements
386398
for an object `v::$NamedVar`.
387399
"""
388-
function CodeEdges(src::CodeInfo)
389-
cl = CodeLinks(src)
400+
function CodeEdges(mod::Module, src::CodeInfo)
401+
cl = CodeLinks(mod, src)
390402
CodeEdges(src, cl)
391403
end
392404

@@ -546,7 +558,7 @@ function terminal_preds(i::Int, edges::CodeEdges)
546558
end
547559

548560
"""
549-
isrequired = lines_required(obj::$NamedVar, src::CodeInfo, edges::CodeEdges)
561+
isrequired = lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges)
550562
isrequired = lines_required(idx::Int, src::CodeInfo, edges::CodeEdges)
551563
552564
Determine which lines might need to be executed to evaluate `obj` or the statement indexed by `idx`.
@@ -556,9 +568,9 @@ will end up skipping a subset of such statements, perhaps while repeating others
556568
557569
See also [`lines_required!`](@ref) and [`selective_eval!`](@ref).
558570
"""
559-
function lines_required(obj::NamedVar, src::CodeInfo, edges::CodeEdges; kwargs...)
571+
function lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges; kwargs...)
560572
isrequired = falses(length(edges.preds))
561-
objs = Set{NamedVar}([obj])
573+
objs = Set{GlobalRef}([obj])
562574
return lines_required!(isrequired, objs, src, edges; kwargs...)
563575
end
564576

@@ -583,7 +595,7 @@ For example, use `norequire = LoweredCodeUtils.exclude_named_typedefs(src, edges
583595
extracting method signatures and not evaluating new definitions.
584596
"""
585597
function lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges; kwargs...)
586-
objs = Set{NamedVar}()
598+
objs = Set{GlobalRef}()
587599
return lines_required!(isrequired, objs, src, edges; kwargs...)
588600
end
589601

@@ -643,7 +655,7 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
643655
end
644656

645657
function add_requests!(isrequired, objs, edges::CodeEdges, norequire)
646-
objsnew = Set{NamedVar}()
658+
objsnew = Set{GlobalRef}()
647659
for obj in objs
648660
add_obj!(isrequired, objsnew, obj, edges, norequire)
649661
end

src/packagedef.jl

Lines changed: 4 additions & 4 deletions
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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,20 @@ function isanonymous_typedef(stmt)
110110
if isa(stmt, CodeInfo)
111111
src = stmt # just for naming consistency
112112
length(src.code) >= 4 || return false
113+
local name
113114
@static if VERSION v"1.9.0-DEV.391"
114-
stmt = src.code[end-2]
115-
isexpr(stmt, :(=)) || return false
116-
name = stmt.args[1]
117-
isa(name, Symbol) || return false
115+
for i = length(src.code)-2:-1:1
116+
isexpr(stmt, :(=)) || continue
117+
stmt = src.code[i]
118+
name = stmt.args[1]
119+
if isa(name, GlobalRef)
120+
name = name.name
121+
else
122+
isa(name, Symbol) || return false
123+
end
124+
break
125+
end
126+
@isdefined(name) || return false
118127
else
119128
stmt = src.code[end-1]
120129
isexpr(stmt, :call) || return false

0 commit comments

Comments
 (0)