Skip to content

Commit d21c32f

Browse files
authoredDec 5, 2024··
Look through SSAValue to find include and similar (#869)
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 d21c32f

File tree

3 files changed

+40
-17
lines changed

3 files changed

+40
-17
lines changed
 

‎src/lowered.jl

+19-7
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

+17-9
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

+4-1
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)
Please sign in to comment.