Skip to content

Commit a2943da

Browse files
committed
Look through SSAValue to find include and similar
There's no guarantee that all include calls will be literal GlobalRefs and in fact JuliaLang/julia#56746 will likely make them never GlobalRef.
1 parent 71eb16a commit a2943da

File tree

3 files changed

+40
-17
lines changed

3 files changed

+40
-17
lines changed

src/lowered.jl

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_dependencies!(methodinfo::MethodInfo, be::CodeEdges, src, isrequired) = meth
2424
add_includes!(methodinfo::MethodInfo, mod::Module, filename) = methodinfo
2525

2626
function is_some_include(@nospecialize(f))
27+
@assert !isa(f, Core.SSAValue) && !isa(f, JuliaInterpreter.SSAValue)
2728
if isa(f, GlobalRef)
2829
return f.name === :include
2930
elseif isa(f, Symbol)
@@ -44,17 +45,21 @@ function is_some_include(@nospecialize(f))
4445
end
4546

4647
# This is not generally used, see `is_method_or_eval` instead
47-
function hastrackedexpr(stmt; heads=LoweredCodeUtils.trackedheads)
48+
function hastrackedexpr(stmt, code; heads=LoweredCodeUtils.trackedheads)
4849
haseval = false
4950
if isa(stmt, Expr)
5051
haseval = matches_eval(stmt)
5152
if stmt.head === :call
5253
f = stmt.args[1]
54+
while isa(f, Core.SSAValue) || isa(f, JuliaInterpreter.SSAValue)
55+
f = code[f.id]
56+
end
5357
callee_matches(f, Core, :_typebody!) && return true, haseval
5458
callee_matches(f, Core, :_setsuper!) && return true, haseval
5559
is_some_include(f) && return true, haseval
5660
elseif stmt.head === :thunk
57-
any(s->any(hastrackedexpr(s; heads=heads)), (stmt.args[1]::Core.CodeInfo).code) && return true, haseval
61+
newcode = (stmt.args[1]::Core.CodeInfo).code
62+
any(s->any(hastrackedexpr(s, newcode; heads=heads)), newcode) && return true, haseval
5863
elseif stmt.head heads
5964
return true, haseval
6065
end
@@ -70,14 +75,21 @@ function matches_eval(stmt::Expr)
7075
(isa(f, GlobalRef) && f.name === :eval) || is_quotenode_egal(f, Core.eval)
7176
end
7277

73-
function categorize_stmt(@nospecialize(stmt))
78+
function categorize_stmt(@nospecialize(stmt), code::Vector{Any})
7479
ismeth, haseval, isinclude, isnamespace, istoplevel = false, false, false, false, false
7580
if isa(stmt, Expr)
7681
haseval = matches_eval(stmt)
7782
ismeth = stmt.head === :method || (stmt.head === :thunk && defines_function(only(stmt.args)))
7883
istoplevel = stmt.head === :toplevel
7984
isnamespace = stmt.head === :export || stmt.head === :import || stmt.head === :using
80-
isinclude = stmt.head === :call && is_some_include(stmt.args[1])
85+
isinclude = false
86+
if stmt.head === :call && length(stmt.args) >= 1
87+
callee = stmt.args[1]
88+
while isa(callee, Core.SSAValue) || isa(callee, JuliaInterpreter.SSAValue)
89+
callee = code[callee.id]
90+
end
91+
isinclude = is_some_include(callee)
92+
end
8193
end
8294
return ismeth, haseval, isinclude, isnamespace, istoplevel
8395
end
@@ -112,7 +124,7 @@ function minimal_evaluation!(@nospecialize(predicate), methodinfo, mod::Module,
112124
evalassign = false
113125
for (i, stmt) in enumerate(src.code)
114126
if !isrequired[i]
115-
isrequired[i], haseval = predicate(stmt)::Tuple{Bool,Bool}
127+
isrequired[i], haseval = predicate(stmt, src.code)::Tuple{Bool,Bool}
116128
if haseval # line `i` may be the equivalent of `f = Core.eval`, so...
117129
isrequired[edges.succs[i]] .= true # ...require each stmt that calls `eval` via `f(expr)`
118130
isrequired[i] = true
@@ -169,8 +181,8 @@ end
169181
minimal_evaluation!(predicate, methodinfo, moduleof(frame), frame.framecode.src, mode)
170182

171183
function minimal_evaluation!(methodinfo, frame::JuliaInterpreter.Frame, mode::Symbol)
172-
minimal_evaluation!(methodinfo, frame, mode) do @nospecialize(stmt)
173-
ismeth, haseval, isinclude, isnamespace, istoplevel = categorize_stmt(stmt)
184+
minimal_evaluation!(methodinfo, frame, mode) do @nospecialize(stmt), code
185+
ismeth, haseval, isinclude, isnamespace, istoplevel = categorize_stmt(stmt, code)
174186
isreq = ismeth | isinclude | istoplevel
175187
return mode === :sigs ? (isreq, haseval) : (isreq | isnamespace, haseval)
176188
end

src/packagedef.jl

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -447,15 +447,23 @@ push_expr!(methodinfo::CodeTrackingMethodInfo, mod::Module, ex::Expr) = (push!(m
447447
pop_expr!(methodinfo::CodeTrackingMethodInfo) = (pop!(methodinfo.exprstack); methodinfo)
448448
function add_dependencies!(methodinfo::CodeTrackingMethodInfo, edges::CodeEdges, src, musteval)
449449
isempty(src.code) && return methodinfo
450-
stmt1 = first(src.code)
451-
if isa(stmt1, Core.GotoIfNot) && (dep = stmt1.cond; isa(dep, Union{GlobalRef,Symbol}))
452-
# This is basically a hack to look for symbols that control definition of methods via a conditional.
453-
# It is aimed at solving #249, but this will have to be generalized for anything real.
454-
for (stmt, me) in zip(src.code, musteval)
455-
me || continue
456-
if hastrackedexpr(stmt)[1]
457-
push!(methodinfo.deps, dep)
458-
break
450+
for i = 1:length(src.code)
451+
stmt = src.code[i]
452+
if isa(stmt, Core.GotoIfNot)
453+
dep = stmt.cond
454+
while (isa(dep, Core.SSAValue) || isa(dep, JuliaInterpreter.SSAValue))
455+
dep = src.code[dep.id]
456+
end
457+
if isa(dep, Union{GlobalRef,Symbol})
458+
# This is basically a hack to look for symbols that control definition of methods via a conditional.
459+
# It is aimed at solving #249, but this will have to be generalized for anything real.
460+
for (stmt, me) in zip(src.code, musteval)
461+
me || continue
462+
if hastrackedexpr(stmt, src.code)[1]
463+
push!(methodinfo.deps, dep)
464+
break
465+
end
466+
end
459467
end
460468
end
461469
end

test/backedges.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ do_test("Backedges") && @testset "Backedges" begin
1717
src2 = src.code[idtype].args[1]
1818
methodinfo = Revise.MethodInfo()
1919
isrequired = Revise.minimal_evaluation!(methodinfo, frame, :sigs)[1]
20-
@test sum(isrequired) == length(src.code)-count(e->isexpr(e, :latestworld), src.code)-1 # skips the `return` at the end
20+
laststmt = src.code[end]
21+
@assert isa(laststmt, Core.ReturnNode)
22+
to_skip = isa(laststmt.val, Revise.JuliaInterpreter.SSAValue) ? 2 : 1
23+
@test sum(isrequired) == length(src.code)-count(e->isexpr(e, :latestworld), src.code)-to_skip # skips the `return` at the end (and its argument)
2124

2225
src = """
2326
# issue #249

0 commit comments

Comments
 (0)