Skip to content

Commit c5c3ec8

Browse files
committed
Resolve varargs calls recursively
These were being "hidden" inside a Core._apply call. This revealed some other cases that require one runs in Compiled mode.
1 parent 7d8d038 commit c5c3ec8

File tree

5 files changed

+42
-6
lines changed

5 files changed

+42
-6
lines changed

src/JuliaInterpreter.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ function set_compiled_methods()
4949
push!(compiled_methods, which(flush, Tuple{IOStream}))
5050
push!(compiled_methods, which(disable_sigint, Tuple{Function}))
5151
push!(compiled_methods, which(reenable_sigint, Tuple{Function}))
52+
# Signal-handling in the `print` dispatch hierarchy
53+
push!(compiled_methods, which(Base.unsafe_write, Tuple{Base.LibuvStream, Ptr{UInt8}, UInt}))
5254
end
5355

5456
function __init__()

src/generate_builtins.jl

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,16 @@ function getargs(args, frame)
8282
end
8383
8484
\"\"\"
85-
ret = maybe_evaluate_builtin(frame, call_expr)
85+
ret = maybe_evaluate_builtin(frame, call_expr, expand::Bool)
8686
8787
If `call_expr` is to a builtin function, evaluate it, returning the result inside a `Some` wrapper.
8888
Otherwise, return `call_expr`.
89+
90+
If `expand` is true, `Core._apply` calls will be resolved as a call to the applied function.
8991
\"\"\"
90-
function maybe_evaluate_builtin(frame, call_expr)
92+
function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
9193
# By having each call appearing statically in the "switch" block below,
9294
# each gets call-site optimized.
93-
9495
args = call_expr.args
9596
nargs = length(args) - 1
9697
fex = args[1]
@@ -122,9 +123,24 @@ function maybe_evaluate_builtin(frame, call_expr)
122123
"""
123124
$head f === tuple
124125
return Some{Any}(ntuple(i->@lookup(frame, args[i+1]), length(args)-1))
126+
""")
127+
continue
128+
elseif f === Core._apply
129+
# Resolve varargs calls
130+
print(io,
131+
"""
132+
$head f === Core._apply
133+
argswrapped = getargs(args, frame)
134+
if !expand
135+
return Some{Any}(Core._apply(getargs(args, frame)...))
136+
end
137+
argsflat = Base.append_any((argswrapped[1],), argswrapped[2:end]...)
138+
new_expr = Expr(:call, map(x->isa(x, Symbol) || isa(x, Expr) || isa(x, QuoteNode) ? QuoteNode(x) : x, argsflat)...)
139+
return new_expr
125140
""")
126141
continue
127142
end
143+
128144
id = findfirst(isequal(f), Core.Compiler.T_FFUNC_KEY)
129145
fcall = generate_fcall(f, Core.Compiler.T_FFUNC_VAL, id)
130146
print(io,

src/interpret.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ function evaluate_call_compiled!(::Compiled, frame::Frame, call_expr::Expr; ente
178178
pc = frame.pc
179179
ret = bypass_builtins(frame, call_expr, pc)
180180
isa(ret, Some{Any}) && return ret.value
181-
ret = maybe_evaluate_builtin(frame, call_expr)
181+
ret = maybe_evaluate_builtin(frame, call_expr, false)
182182
isa(ret, Some{Any}) && return ret.value
183183
fargs = collect_args(frame, call_expr)
184184
f = fargs[1]
@@ -190,8 +190,9 @@ function evaluate_call_recurse!(@nospecialize(recurse), frame::Frame, call_expr:
190190
pc = frame.pc
191191
ret = bypass_builtins(frame, call_expr, pc)
192192
isa(ret, Some{Any}) && return ret.value
193-
ret = maybe_evaluate_builtin(frame, call_expr)
193+
ret = maybe_evaluate_builtin(frame, call_expr, true)
194194
isa(ret, Some{Any}) && return ret.value
195+
call_expr = ret
195196
fargs = collect_args(frame, call_expr)
196197
if (f = fargs[1]) === Core.eval
197198
return Core.eval(fargs[2], fargs[3]) # not a builtin, but worth treating specially

src/precompile.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
function _precompile_()
22
ccall(:jl_generating_output, Cint, ()) == 1 || return nothing
3-
precompile(Tuple{typeof(maybe_evaluate_builtin), Frame, Expr})
3+
precompile(Tuple{typeof(maybe_evaluate_builtin), Frame, Expr, Bool})
44
precompile(Tuple{typeof(getargs), Vector{Any}, Frame})
55
precompile(Tuple{typeof(get_call_framecode), Vector{Any}, FrameCode, Int})
66
for f in (evaluate_call!,

test/debug.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,23 @@ end
128128
@test get_return(frame) == 3
129129
end
130130

131+
@testset "Varargs" begin
132+
f_va_inner(x) = x + 1
133+
f_va_outer(args...) = f_va_inner(args...)
134+
frame = fr = JuliaInterpreter.enter_call(f_va_outer, 1)
135+
# depending on whether this is in or out of a @testset, the first statement may differ
136+
stmt1 = fr.framecode.src.code[1]
137+
if isexpr(stmt1, :call) && @lookup(frame, stmt1.args[1]) === getfield
138+
fr, pc = debug_command(fr, "se")
139+
end
140+
fr, pc = debug_command(fr, "s")
141+
fr, pc = debug_command(fr, "n")
142+
@test root(fr) !== fr
143+
fr, pc = debug_command(fr, "finish")
144+
@test debug_command(fr, "finish") === nothing
145+
@test get_return(frame) === 2
146+
end
147+
131148
@testset "Exceptions" begin
132149
# Don't break on caught exceptions
133150
err_caught = Any[nothing]

0 commit comments

Comments
 (0)