Skip to content

Commit c0009e2

Browse files
authored
Merge pull request #412 from JuliaDebug/teh/new_nodes
Support ReturnNode and GotoIfNot
2 parents 2167af6 + dd3cac5 commit c0009e2

File tree

7 files changed

+87
-19
lines changed

7 files changed

+87
-19
lines changed

src/commands.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ function next_line!(@nospecialize(recurse), frame::Frame, istoplevel::Bool=false
175175
return _next_line!(recurse, frame, istoplevel, initialline, initialfile) # avoid boxing
176176
end
177177
function _next_line!(@nospecialize(recurse), frame, istoplevel, initialline::Int, initialfile::String)
178-
predicate(frame) = isexpr(pc_expr(frame), :return) || (linenumber(frame) != initialline || getfile(frame) != initialfile)
178+
predicate(frame) = is_return(pc_expr(frame)) || (linenumber(frame) != initialline || getfile(frame) != initialfile)
179179

180180
pc = next_until!(predicate, recurse, frame, istoplevel)
181181
(pc === nothing || isa(pc, BreakpointRef)) && return pc
@@ -195,7 +195,7 @@ function until_line!(@nospecialize(recurse), frame::Frame, line::Union{Nothing,
195195
pc = frame.pc
196196
initialline, initialfile = linenumber(frame, pc), getfile(frame, pc)
197197
line === nothing && (line = initialline + 1)
198-
predicate(frame) = isexpr(pc_expr(frame), :return) || (linenumber(frame) >= line && getfile(frame) == initialfile)
198+
predicate(frame) = is_return(pc_expr(frame)) || (linenumber(frame) >= line && getfile(frame) == initialfile)
199199
pc = next_until!(predicate, frame, istoplevel)
200200
(pc === nothing || isa(pc, BreakpointRef)) && return pc
201201
maybe_step_through_kwprep!(recurse, frame, istoplevel)
@@ -443,7 +443,7 @@ function debug_command(@nospecialize(recurse), frame::Frame, cmd::Symbol, rootis
443443
is_si || maybe_step_through_kwprep!(recurse, frame, istoplevel)
444444
pc = frame.pc
445445
stmt0 = stmt = pc_expr(frame, pc)
446-
isexpr(stmt0, :return) && return maybe_reset_frame!(recurse, frame, nothing, rootistoplevel)
446+
is_return(stmt0) && return maybe_reset_frame!(recurse, frame, nothing, rootistoplevel)
447447
if isexpr(stmt, :(=))
448448
stmt = stmt.args[2]
449449
end

src/interpret.jl

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ function check_isdefined(frame, @nospecialize(node))
417417
error("unrecognized isdefined node ", node)
418418
end
419419

420-
# For "profiling" where JuliaIntepreter spends its time. See the commented-out block
420+
# For "profiling" where JuliaInterpreter spends its time. See the commented-out block
421421
# in `step_expr!`
422422
const _location = Dict{Tuple{Method,Int},Int}()
423423

@@ -449,7 +449,7 @@ function step_expr!(@nospecialize(recurse), frame, @nospecialize(node), istoplev
449449
elseif node.head === :gotoifnot
450450
arg = @lookup(frame, node.args[1])
451451
if !isa(arg, Bool)
452-
throw(TypeError(nameof(frame), "if", Bool, node.args[1]))
452+
throw(TypeError(nameof(frame), "if", Bool, arg))
453453
end
454454
if !arg
455455
return (frame.pc = node.args[2]::Int)
@@ -528,6 +528,17 @@ function step_expr!(@nospecialize(recurse), frame, @nospecialize(node), istoplev
528528
end
529529
elseif isa(node, GotoNode)
530530
return (frame.pc = node.label)
531+
elseif is_GotoIfNot(node)
532+
node = node::Core.GotoIfNot
533+
arg = @lookup(frame, node.cond)
534+
if !isa(arg, Bool)
535+
throw(TypeError(nameof(frame), "if", Bool, arg))
536+
end
537+
if !arg
538+
return (frame.pc = node.dest)
539+
end
540+
elseif is_ReturnNode(node)
541+
return nothing
531542
elseif isa(node, NewvarNode)
532543
# FIXME: undefine the slot?
533544
elseif istoplevel && isa(node, LineNumberNode)
@@ -606,6 +617,12 @@ function handle_err(@nospecialize(recurse), frame, err)
606617
return (frame.pc = data.exception_frames[end])
607618
end
608619

620+
if isdefined(Core, :ReturnNode)
621+
lookup_return(frame, node::Core.ReturnNode) = @lookup(frame, node.val)
622+
else
623+
lookup_return(frame, node::Expr) = @lookup(frame, node.args[1])
624+
end
625+
609626
"""
610627
ret = get_return(frame)
611628
@@ -615,7 +632,7 @@ e.g., [`JuliaInterpreter.finish!`](@ref)).
615632
"""
616633
function get_return(frame)
617634
node = pc_expr(frame)
618-
isexpr(node, :return) || error("expected return statement, got ", node)
619-
return @lookup(frame, (node::Expr).args[1])
635+
is_return(node) || error("expected return statement, got ", node)
636+
return lookup_return(frame, node)
620637
end
621638
get_return(t::Tuple{Module,Expr,Frame}) = get_return(t[end])

src/optimize.jl

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ function renumber_ssa!(stmts::Vector{Any}, ssalookup)
5252
if (stmt.head === :gotoifnot || stmt.head === :enter) && isa(stmt.args[end], Int)
5353
stmt.args[end] = ssalookup[stmt.args[end]]
5454
end
55+
elseif is_GotoIfNot(stmt)
56+
cond = stmt.cond
57+
if isa(cond, SSAValue)
58+
cond = SSAValue(ssalookup[cond.id])
59+
end
60+
stmts[i] = Core.GotoIfNot(cond, ssalookup[stmt.dest])
61+
elseif is_ReturnNode(stmt)
62+
val = (stmt::Core.ReturnNode).val
63+
if isa(val, SSAValue)
64+
stmts[i] = Core.ReturnNode(SSAValue(ssalookup[val.id]))
65+
end
5566
end
5667
end
5768
return stmts
@@ -372,11 +383,33 @@ function replace_coretypes!(src; rev::Bool=false)
372383
end
373384

374385
function replace_coretypes_list!(list::AbstractVector; rev::Bool)
386+
function rep(@nospecialize(x), rev::Bool)
387+
if isa(x, rev ? SSAValue : Core.SSAValue)
388+
return rev ? Core.SSAValue(x.id) : SSAValue(x.id)
389+
elseif isa(x, rev ? SlotNumber : Core.SlotNumber)
390+
return rev ? Core.SlotNumber(x.id) : SlotNumber(x.id)
391+
end
392+
return x
393+
end
394+
375395
for (i, stmt) in enumerate(list)
376-
if isa(stmt, rev ? SSAValue : Core.SSAValue)
377-
list[i] = rev ? Core.SSAValue(stmt.id) : SSAValue(stmt.id)
378-
elseif isa(stmt, rev ? SlotNumber : Core.SlotNumber)
379-
list[i] = rev ? Core.SlotNumber(stmt.id) : SlotNumber(stmt.id)
396+
rstmt = rep(stmt, rev)
397+
if rstmt !== stmt
398+
list[i] = rstmt
399+
elseif is_GotoIfNot(stmt)
400+
stmt = stmt::Core.GotoIfNot
401+
cond = stmt.cond
402+
rcond = rep(cond, rev)
403+
if rcond !== cond
404+
list[i] = Core.GotoIfNot(rcond, stmt.dest)
405+
end
406+
elseif is_ReturnNode(stmt)
407+
stmt = stmt::Core.ReturnNode
408+
val = stmt.val
409+
rval = rep(val, rev)
410+
if rval !== val
411+
list[i] = Core.ReturnNode(rval)
412+
end
380413
elseif isa(stmt, Expr)
381414
replace_coretypes!(stmt; rev=rev)
382415
end

src/utils.jl

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,23 @@ _scopename(parent, child, rest...) = Expr(:., parent, _scopename(child, rest...)
132132

133133
isidentical(x) = Base.Fix2(===, x) # recommended over isequal(::Symbol) since it cannot be invalidated
134134

135-
is_goto_node(@nospecialize(node)) = isa(node, GotoNode) || isexpr(node, :gotoifnot)
135+
# is_goto_node(@nospecialize(node)) = isa(node, GotoNode) || isexpr(node, :gotoifnot)
136+
137+
if isdefined(Core, :GotoIfNot)
138+
is_GotoIfNot(@nospecialize(node)) = isa(node, Core.GotoIfNot)
139+
is_gotoifnot(@nospecialize(node)) = is_GotoIfNot(node)
140+
else
141+
is_GotoIfNot(@nospecialize(node)) = false
142+
is_gotoifnot(@nospecialize(node)) = isexpr(node, :gotoifnot)
143+
end
144+
145+
if isdefined(Core, :ReturnNode)
146+
is_ReturnNode(@nospecialize(node)) = isa(node, Core.ReturnNode)
147+
is_return(@nospecialize(node)) = is_ReturnNode(node)
148+
else
149+
is_ReturnNode(@nospecialize(node)) = false
150+
is_return(@nospecialize(node)) = isexpr(node, :return)
151+
end
136152

137153
is_loc_meta(@nospecialize(expr), @nospecialize(kind)) = isexpr(expr, :meta) && length(expr.args) >= 1 && expr.args[1] === kind
138154

@@ -165,7 +181,7 @@ function is_call(@nospecialize(node))
165181
(isexpr(node, :(=)) && (isexpr(node.args[2], :call)))
166182
end
167183

168-
is_call_or_return(@nospecialize(node)) = is_call(node) || isexpr(node, :return)
184+
is_call_or_return(@nospecialize(node)) = is_call(node) || is_return(node)
169185

170186
is_dummy(bpref::BreakpointRef) = bpref.stmtidx == 0 && bpref.err === nothing
171187

@@ -253,7 +269,7 @@ function linetable(arg)
253269
end
254270
return (arg::CodeInfo).linetable::Vector{Any}
255271
end
256-
linetable(arg, i::Integer) = linetable(arg)[i]::LineTypes
272+
linetable(arg, i::Integer) = linetable(arg)[i]::Union{Expr,LineTypes}
257273

258274
function codelocs(arg)
259275
if isa(arg, Frame)

test/debug.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,17 +135,19 @@ end
135135
@test isa(pc, BreakpointRef)
136136
@test JuliaInterpreter.scopeof(f).name == :generatedfoo
137137
stmt = JuliaInterpreter.pc_expr(f)
138-
@test stmt.head == :return && stmt.args[1] === Int
138+
@test JuliaInterpreter.is_return(stmt) && JuliaInterpreter.lookup_return(frame, stmt) === Int
139139
@test debug_command(frame, :c) === nothing
140140
@test frame.callee === nothing
141141
@test get_return(frame) === Int
142142
# This time, step into the generated function itself
143143
frame = enter_call_expr(:($(callgenerated)()))
144144
f, pc = debug_command(frame, :sg)
145+
# Aside: generators can have `Expr(:line, ...)` in their line tables, test that this is OK
146+
@test isexpr(JuliaInterpreter.linetable(f, 2), :line)
145147
@test isa(pc, BreakpointRef)
146148
@test JuliaInterpreter.scopeof(f).name == :generatedfoo
147149
stmt = JuliaInterpreter.pc_expr(f)
148-
@test stmt.head == :return && @lookup(f, stmt.args[1]) === 1
150+
@test JuliaInterpreter.is_return(stmt) && JuliaInterpreter.lookup_return(f, stmt) === 1
149151
f2, pc = debug_command(f, :finish)
150152
@test JuliaInterpreter.scopeof(f2).name == :callgenerated
151153
# Now finish the regular function

test/limits.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ using Test
3232
@test Aborted(frame, i).at.line == 6
3333
# Check conditional
3434
frame = JuliaInterpreter.prepare_thunk(modexs[4])
35-
i = findfirst(stmt->isexpr(stmt, :gotoifnot), frame.framecode.src.code) + 1
35+
i = findfirst(stmt->JuliaInterpreter.is_gotoifnot(stmt), frame.framecode.src.code) + 1
3636
@test Aborted(frame, i).at.line == 9
3737
# Check macro
3838
frame = JuliaInterpreter.prepare_thunk(modexs[5])

test/utils.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ function evaluate_limited!(@nospecialize(recurse), frame::Frame, nstmts::Int, is
8484
nstmts = refnstmts[]
8585
elseif istoplevel && stmt.head == :thunk
8686
code = stmt.args[1]
87-
if length(code.code) == 1 && isexpr(code.code[end], :return) && isexpr(code.code[end].args[1], :method)
87+
if length(code.code) == 1 && JuliaInterpreter.is_return(code.code[end]) && isexpr(code.code[end].args[1], :method)
8888
# Julia 1.2+ puts a :thunk before the start of each method
8989
new_pc = pc + 1
9090
else
@@ -121,7 +121,7 @@ function evaluate_limited!(@nospecialize(recurse), frame::Frame, nstmts::Int, is
121121
end
122122
# Handle the return
123123
stmt = pc_expr(frame, pc)
124-
if nstmts == 0 && !isexpr(stmt, :return)
124+
if nstmts == 0 && !JuliaInterpreter.is_return(stmt)
125125
ret = Aborted(frame, pc)
126126
return ret, nstmts
127127
end

0 commit comments

Comments
 (0)