Skip to content

Commit 713c768

Browse files
authored
implement support for current_scope (#605)
* implement support for `current_scope` * add `current_scope` support for compiled frames
1 parent 580b95c commit 713c768

File tree

8 files changed

+113
-28
lines changed

8 files changed

+113
-28
lines changed

bin/generate_builtins.jl

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const RECENTLY_ADDED = Core.Builtin[
1111
Core.finalizer, Core._compute_sparams, Core._svec_ref,
1212
Core.compilerbarrier,
1313
Core.memoryref, Core.memoryref_isassigned, Core.memoryrefget, Core.memoryrefoffset, Core.memoryrefset!,
14+
#=Core.current_scope=#
1415
]
1516
# Builtins present in 1.6, not builtins (potentially still normal functions) anymore
1617
const RECENTLY_REMOVED = GlobalRef.(Ref(Core), [
@@ -187,13 +188,13 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
187188
print(io,
188189
"""
189190
$head f === $fstr
190-
if !expand
191-
argswrapped = getargs(args, frame)
192-
return Some{Any}($fstr(argswrapped...))
193-
end
194-
# This uses the original arguments to avoid looking them up twice
195-
# See #442
196-
return Expr(:call, invoke, args[2:end]...)
191+
if !expand
192+
argswrapped = getargs(args, frame)
193+
return Some{Any}($fstr(argswrapped...))
194+
end
195+
# This uses the original arguments to avoid looking them up twice
196+
# See #442
197+
return Expr(:call, invoke, args[2:end]...)
197198
""")
198199
continue
199200
elseif f === Core._call_latest
@@ -211,6 +212,22 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
211212
end
212213
return maybe_recurse_expanded_builtin(frame, new_expr)
213214
""")
215+
continue
216+
elseif f === Core.current_scope
217+
print(io,
218+
"""
219+
elseif @static isdefined(Core, :current_scope) && f === Core.current_scope
220+
if nargs == 0
221+
if isempty(frame.framedata.current_scopes)
222+
return Some{Any}(nothing)
223+
else
224+
return Some{Any}(frame.framedata.current_scopes[end])
225+
end
226+
else
227+
return Some{Any}(Core.current_scope(getargs(args, frame)...))
228+
end
229+
""")
230+
continue
214231
end
215232

216233
id = findfirst(isequal(f), Core.Compiler.T_FFUNC_KEY)

src/builtins.jl

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
9494
push!(new_expr.args, QuoteNode(x))
9595
end
9696
return maybe_recurse_expanded_builtin(frame, new_expr)
97-
elseif f === Core._call_latest
98-
return Some{Any}(Core._call_latest(getargs(args, frame)...))
9997
elseif @static isdefined(Core, :_compute_sparams) && f === Core._compute_sparams
10098
return Some{Any}(Core._compute_sparams(getargs(args, frame)...))
10199
elseif f === Core._equiv_typedef
@@ -126,6 +124,16 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
126124
else
127125
return Some{Any}(Core.compilerbarrier(getargs(args, frame)...))
128126
end
127+
elseif @static isdefined(Core, :current_scope) && f === Core.current_scope
128+
if nargs == 0
129+
if isempty(frame.framedata.current_scopes)
130+
return Some{Any}(nothing)
131+
else
132+
return Some{Any}(frame.framedata.current_scopes[end])
133+
end
134+
else
135+
return Some{Any}(Core.current_scope(getargs(args, frame)...))
136+
end
129137
elseif @static isdefined(Core, :donotdelete) && f === Core.donotdelete
130138
return Some{Any}(Core.donotdelete(getargs(args, frame)...))
131139
elseif @static isdefined(Core, :finalizer) && f === Core.finalizer
@@ -227,13 +235,13 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
227235
return Some{Any}(getglobal(getargs(args, frame)...))
228236
end
229237
elseif f === invoke
230-
if !expand
231-
argswrapped = getargs(args, frame)
232-
return Some{Any}(invoke(argswrapped...))
233-
end
234-
# This uses the original arguments to avoid looking them up twice
235-
# See #442
236-
return Expr(:call, invoke, args[2:end]...)
238+
if !expand
239+
argswrapped = getargs(args, frame)
240+
return Some{Any}(invoke(argswrapped...))
241+
end
242+
# This uses the original arguments to avoid looking them up twice
243+
# See #442
244+
return Expr(:call, invoke, args[2:end]...)
237245
elseif f === isa
238246
if nargs == 2
239247
return Some{Any}(isa(@lookup(frame, args[2]), @lookup(frame, args[3])))

src/construct.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ function prepare_framedata(framecode, argvals::Vector{Any}, lenv::SimpleVector=e
269269
if length(junk_framedata) > 0
270270
olddata = pop!(junk_framedata)
271271
locals, ssavalues, sparams = olddata.locals, olddata.ssavalues, olddata.sparams
272-
exception_frames, last_reference = olddata.exception_frames, olddata.last_reference
272+
exception_frames, current_scopes, last_reference = olddata.exception_frames, olddata.current_scopes, olddata.last_reference
273273
last_exception = olddata.last_exception
274274
callargs = olddata.callargs
275275
resize!(locals, ns)
@@ -279,13 +279,15 @@ function prepare_framedata(framecode, argvals::Vector{Any}, lenv::SimpleVector=e
279279
# for check_isdefined to work properly, we need sparams to start out unassigned
280280
resize!(sparams, 0)
281281
empty!(exception_frames)
282+
empty!(current_scopes)
282283
resize!(last_reference, ns)
283284
last_exception[] = _INACTIVE_EXCEPTION.instance
284285
else
285286
locals = Vector{Union{Nothing,Some{Any}}}(nothing, ns)
286287
ssavalues = Vector{Any}(undef, ng)
287288
sparams = Vector{Any}(undef, 0)
288289
exception_frames = Int[]
290+
current_scopes = Any[]
289291
last_reference = Vector{Int}(undef, ns)
290292
callargs = Any[]
291293
last_exception = Ref{Any}(_INACTIVE_EXCEPTION.instance)
@@ -314,7 +316,8 @@ function prepare_framedata(framecode, argvals::Vector{Any}, lenv::SimpleVector=e
314316
isa(T, TypeVar) && continue # only fill concrete types
315317
sparams[i] = T
316318
end
317-
FrameData(locals, ssavalues, sparams, exception_frames, last_exception, caller_will_catch_err, last_reference, callargs)
319+
return FrameData(locals, ssavalues, sparams, exception_frames, current_scopes,
320+
last_exception, caller_will_catch_err, last_reference, callargs)
318321
end
319322

320323
"""
@@ -331,6 +334,7 @@ end
331334
function prepare_frame_caller(caller::Frame, framecode::FrameCode, args::Vector{Any}, lenv::SimpleVector)
332335
caller_will_catch_err = !isempty(caller.framedata.exception_frames) || caller.framedata.caller_will_catch_err
333336
caller.callee = frame = prepare_frame(framecode, args, lenv, caller_will_catch_err)
337+
copy!(frame.framedata.current_scopes, caller.framedata.current_scopes)
334338
frame.caller = caller
335339
return frame
336340
end

src/interpret.jl

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,19 @@ function bypass_builtins(@nospecialize(recurse), frame, call_expr, pc)
222222
return nothing
223223
end
224224

225+
function native_call(fargs::Vector{Any}, frame::Frame)
226+
f = popfirst!(fargs) # now it's really just `args`
227+
if !isempty(frame.framedata.current_scopes)
228+
newscope = Core.current_scope()
229+
for scope in frame.framedata.current_scopes
230+
newscope = Scope(newscope, scope.values...)
231+
end
232+
ex = Expr(:tryfinally, :($f($fargs...)), nothing, newscope)
233+
return Core.eval(moduleof(frame), ex)
234+
end
235+
return Base.invokelatest(f, fargs...)
236+
end
237+
225238
function evaluate_call_compiled!(::Compiled, frame::Frame, call_expr::Expr; enter_generated::Bool=false)
226239
# @assert !enter_generated
227240
pc = frame.pc
@@ -230,9 +243,7 @@ function evaluate_call_compiled!(::Compiled, frame::Frame, call_expr::Expr; ente
230243
ret = maybe_evaluate_builtin(frame, call_expr, false)
231244
isa(ret, Some{Any}) && return ret.value
232245
fargs = collect_args(Compiled(), frame, call_expr)
233-
f = fargs[1]
234-
popfirst!(fargs) # now it's really just `args`
235-
return f(fargs...)
246+
return native_call(fargs, frame)
236247
end
237248

238249
function evaluate_call_recurse!(@nospecialize(recurse), frame::Frame, call_expr::Expr; enter_generated::Bool=false)
@@ -263,8 +274,7 @@ function evaluate_call_recurse!(@nospecialize(recurse), frame::Frame, call_expr:
263274
framecode, lenv = get_call_framecode(fargs, frame.framecode, frame.pc; enter_generated=enter_generated)
264275
if lenv === nothing
265276
if isa(framecode, Compiled)
266-
f = popfirst!(fargs) # now it's really just `args`
267-
return Base.invokelatest(f, fargs...)
277+
return native_call(fargs, frame)
268278
end
269279
return framecode # this was a Builtin
270280
end
@@ -415,9 +425,6 @@ function eval_rhs(@nospecialize(recurse), frame, node::Expr)
415425
elseif head === :copyast
416426
val = (node.args[1]::QuoteNode).value
417427
return isa(val, Expr) ? copy(val) : val
418-
elseif head === :enter
419-
# XXX This seems to be dead code
420-
return length(frame.framedata.exception_frames)
421428
elseif head === :boundscheck
422429
return true
423430
elseif head === :meta || head === :inbounds || head === :loopinfo ||
@@ -504,8 +511,12 @@ function step_expr!(@nospecialize(recurse), frame, @nospecialize(node), istoplev
504511
for i = 1:length(node.args)
505512
targ = node.args[i]
506513
targ === nothing && continue
507-
frame.framecode.src.code[(targ::SSAValue).id] === nothing && continue
514+
enterstmt = frame.framecode.src.code[(targ::SSAValue).id]
515+
enterstmt === nothing && continue
508516
pop!(data.exception_frames)
517+
if isdefined(enterstmt, :scope)
518+
pop!(data.current_scopes)
519+
end
509520
end
510521
end
511522
elseif node.head === :pop_exception
@@ -586,6 +597,9 @@ function step_expr!(@nospecialize(recurse), frame, @nospecialize(node), istoplev
586597
elseif @static (isdefined(Core.IR, :EnterNode) && true) && isa(node, Core.IR.EnterNode)
587598
rhs = node.catch_dest
588599
push!(data.exception_frames, rhs)
600+
if isdefined(node, :scope)
601+
push!(data.current_scopes, @lookup(frame, node.scope))
602+
end
589603
else
590604
rhs = @lookup(frame, node)
591605
end

src/optimize.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ function build_compiled_foreigncall!(stmt::Expr, code::CodeInfo, sparams::Vector
241241
return nothing
242242
end
243243

244-
function replace_coretypes!(src; rev::Bool=false)
244+
function replace_coretypes!(@nospecialize(src); rev::Bool=false)
245245
if isa(src, CodeInfo)
246246
replace_coretypes_list!(src.code; rev=rev)
247247
elseif isa(src, Expr)
@@ -286,6 +286,13 @@ function replace_coretypes_list!(list::AbstractVector; rev::Bool=false)
286286
if rval !== val
287287
list[i] = ReturnNode(rval)
288288
end
289+
elseif @static (isdefined(Core.IR, :EnterNode) && true) && isa(stmt, Core.IR.EnterNode)
290+
if isdefined(stmt, :scope)
291+
rscope = rep(stmt.scope, rev)
292+
if rscope !== stmt.scope
293+
list[i] = Core.IR.EnterNode(stmt.catch_dest, rscope)
294+
end
295+
end
289296
elseif isa(stmt, Expr)
290297
replace_coretypes!(stmt; rev=rev)
291298
end

src/packagedef.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ if !isdefined(Base, Symbol("@something"))
4343
end
4444
end
4545

46+
if isdefined(Base, :ScopedValues)
47+
using Base: ScopedValues.Scope
48+
else
49+
const Scope = Any
50+
end
51+
4652
include("types.jl")
4753
include("utils.jl")
4854
include("construct.jl")

src/types.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ struct FrameData
200200
ssavalues::Vector{Any}
201201
sparams::Vector{Any}
202202
exception_frames::Vector{Int}
203+
current_scopes::Vector{Scope}
203204
last_exception::Base.RefValue{Any}
204205
caller_will_catch_err::Bool
205206
last_reference::Vector{Int}

test/interpret.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,3 +997,31 @@ end
997997
# test for using generic functions that were previously builtin
998998
func_arrayref(a, i) = Core.arrayref(true, a, i)
999999
@test 2 == @interpret func_arrayref([1,2,3], 2)
1000+
1001+
@static if isdefined(Base, :ScopedValues)
1002+
const sval = ScopedValue(1)
1003+
@test 1 == @interpret getindex(sval)
1004+
1005+
# current_scope support for interpretation
1006+
sval_func1() = @with sval => 2 begin
1007+
return sval[]
1008+
end
1009+
@test 2 == @interpret sval_func1()
1010+
1011+
# current_scope support for compiled calls
1012+
_sval_func2() = sval[]
1013+
sval_func2() = @with sval => 2 begin
1014+
return _sval_func2()
1015+
end
1016+
let m = only(methods(_sval_func2))
1017+
push!(JuliaInterpreter.compiled_methods, m)
1018+
try
1019+
@test 2 == @interpret sval_func2()
1020+
finally
1021+
delete!(JuliaInterpreter.compiled_methods, m)
1022+
end
1023+
end
1024+
let frame = Frame(@__MODULE__, only(code_lowered(sval_func2)))
1025+
@test 2 == JuliaInterpreter.finish_and_return!(Compiled(), frame)
1026+
end
1027+
end

0 commit comments

Comments
 (0)