Skip to content

Commit d4596e3

Browse files
timholyKristofferC
authored andcommitted
Replace the slow slotname Dict with a counter ("age")-based mechanism (#307)
* Replace the slow slotname Dict with a counter ("age")-based mechanism Co-authored-by: "Kristoffer Carlsson" <[email protected]> * make assignment_counter an Int64 to prevent overflow
1 parent 7d3b918 commit d4596e3

File tree

6 files changed

+115
-68
lines changed

6 files changed

+115
-68
lines changed

src/breakpoints.jl

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,27 @@ function prepare_slotfunction(framecode::FrameCode, body::Union{Symbol,Expr})
132132
for i = 1:length(slotnames)
133133
slotname = framecode.src.slotnames[i]
134134
qslotname = QuoteNode(slotname)
135-
getexpr = :(something($dataname.locals[$dataname.last_reference[$qslotname]]))
136-
push!(assignments, Expr(:(=), slotname, :(haskey($dataname.last_reference, $qslotname) ? $getexpr : $default)))
135+
list = framecode.slotnamelists[slotname]
136+
if length(list) == 1
137+
maxexpr = :($dataname.last_reference[$(list[1])] > 0 ? $(list[1]) : 0)
138+
else
139+
maxcounter, maxidx = gensym("maxcounter"), gensym("maxidx")
140+
maxexpr = quote
141+
begin
142+
$maxcounter, $maxidx = 0, 0
143+
for l in $list
144+
counter = $dataname.last_reference[l]
145+
if counter > $maxcounter
146+
$maxcounter, $maxidx = counter, l
147+
end
148+
end
149+
$maxidx
150+
end
151+
end
152+
end
153+
maxexsym = gensym("slotid")
154+
push!(assignments, :($maxexsym = $maxexpr))
155+
push!(assignments, :($slotname = $maxexsym > 0 ? something($dataname.locals[$maxexsym]) : $default))
137156
end
138157
if ismeth
139158
syms = sparam_syms(framecode.scope)

src/construct.jl

Lines changed: 38 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -265,56 +265,49 @@ function prepare_call(@nospecialize(f), allargs; enter_generated = false)
265265
end
266266

267267
function prepare_framedata(framecode, argvals::Vector{Any}, lenv::SimpleVector=empty_svec, caller_will_catch_err::Bool=false)
268+
src = framecode.src
269+
slotnames = src.slotnames::SlotNamesType
270+
ssavt = src.ssavaluetypes
271+
ng, ns = isa(ssavt, Int) ? ssavt : length(ssavt::Vector{Any}), length(src.slotflags)
272+
if length(junk) > 0
273+
olddata = pop!(junk)
274+
locals, ssavalues, sparams = olddata.locals, olddata.ssavalues, olddata.sparams
275+
exception_frames, last_reference = olddata.exception_frames, olddata.last_reference
276+
last_exception = olddata.last_exception
277+
callargs = olddata.callargs
278+
resize!(locals, ns)
279+
fill!(locals, nothing)
280+
resize!(ssavalues, ng)
281+
# for check_isdefined to work properly, we need sparams to start out unassigned
282+
resize!(sparams, 0)
283+
empty!(exception_frames)
284+
resize!(last_reference, ns)
285+
last_exception[] = nothing
286+
else
287+
locals = Vector{Union{Nothing,Some{Any}}}(nothing, ns)
288+
ssavalues = Vector{Any}(undef, ng)
289+
sparams = Vector{Any}(undef, 0)
290+
exception_frames = Int[]
291+
last_reference = Vector{Int}(undef, ns)
292+
callargs = Any[]
293+
last_exception = Ref{Any}(nothing)
294+
end
295+
fill!(last_reference, 0)
268296
if isa(framecode.scope, Method)
269-
meth, src = framecode.scope::Method, framecode.src
270-
slotnames = src.slotnames::SlotNamesType
271-
ssavt = src.ssavaluetypes
272-
ng = isa(ssavt, Int) ? ssavt : length(ssavt::Vector{Any})
297+
meth = framecode.scope::Method
273298
nargs, meth_nargs = length(argvals), Int(meth.nargs)
274-
if length(junk) > 0
275-
olddata = pop!(junk)
276-
locals, ssavalues, sparams = olddata.locals, olddata.ssavalues, olddata.sparams
277-
exception_frames, last_reference = olddata.exception_frames, olddata.last_reference
278-
last_exception = olddata.last_exception
279-
callargs = olddata.callargs
280-
resize!(locals, length(src.slotflags))
281-
resize!(ssavalues, ng)
282-
# for check_isdefined to work properly, we need sparams to start out unassigned
283-
resize!(sparams, 0)
284-
empty!(exception_frames)
285-
empty!(last_reference)
286-
last_exception[] = nothing
287-
else
288-
locals = Vector{Union{Nothing,Some{Any}}}(undef, length(src.slotflags))
289-
ssavalues = Vector{Any}(undef, ng)
290-
sparams = Vector{Any}(undef, 0)
291-
exception_frames = Int[]
292-
last_reference = Dict{Symbol,Int}()
293-
callargs = Any[]
294-
last_exception = Ref{Any}(nothing)
295-
end
296-
for i = 1:meth_nargs
297-
last_reference[slotnames[i]::Symbol] = i
298-
if meth.isva && i == meth_nargs
299-
locals[i] = nargs < i ? Some{Any}(()) : (let i=i; Some{Any}(ntuple(k->argvals[i+k-1], nargs-i+1)); end)
300-
break
299+
islastva = meth.isva && nargs >= meth_nargs
300+
for i = 1:meth_nargs-islastva
301+
if nargs >= i
302+
locals[i], last_reference[i] = Some{Any}(argvals[i]), 1
303+
else
304+
locals[i] = Some{Any}(())
301305
end
302-
locals[i] = nargs >= i ? Some{Any}(argvals[i]) : Some{Any}(())
303306
end
304-
# add local variables initially undefined
305-
for i = (meth_nargs+1):length(slotnames)
306-
locals[i] = nothing
307+
if islastva
308+
locals[meth_nargs] = (let i=meth_nargs; Some{Any}(ntuple(k->argvals[i+k-1], nargs-i+1)); end)
309+
last_reference[meth_nargs] = 1
307310
end
308-
else
309-
src = framecode.src
310-
locals = Vector{Union{Nothing,Some{Any}}}(undef, length(src.slotflags)) # src.slotflags is concretely typed, unlike slotnames
311-
fill!(locals, nothing)
312-
ssavalues = Vector{Any}(undef, length(src.code))
313-
sparams = Any[]
314-
exception_frames = Int[]
315-
last_reference = Dict{Symbol,Int}()
316-
callargs = Any[]
317-
last_exception = Ref{Any}(nothing)
318311
end
319312
resize!(sparams, length(lenv))
320313
# Add static parameters to environment

src/interpret.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,9 @@ function do_assignment!(frame, @nospecialize(lhs), @nospecialize(rhs))
332332
if isa(lhs, SSAValue)
333333
data.ssavalues[lhs.id] = rhs
334334
elseif isa(lhs, SlotNumber)
335+
counter = (frame.assignment_counter += 1)
335336
data.locals[lhs.id] = Some{Any}(rhs)
336-
slotnames = code.src.slotnames::SlotNamesType
337-
data.last_reference[slotnames[lhs.id]::Symbol] = lhs.id
337+
data.last_reference[lhs.id] = counter
338338
elseif isa(lhs, GlobalRef)
339339
Core.eval(lhs.mod, :($(lhs.name) = $(QuoteNode(rhs))))
340340
elseif isa(lhs, Symbol)

src/types.jl

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct FrameCode
7070
src::CodeInfo
7171
methodtables::Vector{Union{Compiled,TypeMapEntry}} # line-by-line method tables for generic-function :call Exprs
7272
breakpoints::Vector{BreakpointState}
73+
slotnamelists::Dict{Symbol,Vector{Int}}
7374
used::BitSet
7475
generator::Bool # true if this is for the expression-generator of a @generated function
7576
end
@@ -89,8 +90,13 @@ function FrameCode(scope, src::CodeInfo; generator=false, optimize=true)
8990
src.code[i] = nothing
9091
end
9192
end
93+
slotnamelists = Dict{Symbol,Vector{Int}}()
94+
for (i, sym) in enumerate(src.slotnames)
95+
list = get(slotnamelists, sym, Int[])
96+
slotnamelists[sym] = push!(list, i)
97+
end
9298
used = find_used(src)
93-
framecode = FrameCode(scope, src, methodtables, breakpoints, used, generator)
99+
framecode = FrameCode(scope, src, methodtables, breakpoints, slotnamelists, used, generator)
94100
if scope isa Method
95101
for bp in _breakpoints
96102
# Manual union splitting
@@ -151,9 +157,7 @@ struct FrameData
151157
exception_frames::Vector{Int}
152158
last_exception::Base.RefValue{Any}
153159
caller_will_catch_err::Bool
154-
# A vector from names to the slotnumber of that name
155-
# for which a reference was last encountered.
156-
last_reference::Dict{Symbol,Int}
160+
last_reference::Vector{Int}
157161
callargs::Vector{Any} # a temporary for processing arguments of :call exprs
158162
end
159163

@@ -176,10 +180,11 @@ mutable struct Frame
176180
framecode::FrameCode
177181
framedata::FrameData
178182
pc::Int
183+
assignment_counter::Int64
179184
caller::Union{Frame,Nothing}
180185
callee::Union{Frame,Nothing}
181186
end
182-
Frame(framecode, framedata, pc=1, caller=nothing) = Frame(framecode, framedata, pc, caller, nothing)
187+
Frame(framecode, framedata, pc=1, caller=nothing) = Frame(framecode, framedata, pc, 1, caller, nothing)
183188

184189
caller(frame) = frame.caller
185190
callee(frame) = frame.callee
@@ -331,7 +336,7 @@ struct BreakpointSignature <: AbstractBreakpoint
331336
enabled::Ref{Bool}
332337
instances::Vector{BreakpointRef}
333338
end
334-
same_location(bp2::BreakpointSignature, bp::BreakpointSignature) =
339+
same_location(bp2::BreakpointSignature, bp::BreakpointSignature) =
335340
bp2.f == bp.f && bp2.sig == bp.sig && bp2.line == bp.line
336341
function Base.show(io::IO, bp::BreakpointSignature)
337342
print(io, bp.f)
@@ -369,7 +374,7 @@ struct BreakpointFileLocation <: AbstractBreakpoint
369374
enabled::Ref{Bool}
370375
instances::Vector{BreakpointRef}
371376
end
372-
same_location(bp2::BreakpointFileLocation, bp::BreakpointFileLocation) =
377+
same_location(bp2::BreakpointFileLocation, bp::BreakpointFileLocation) =
373378
bp2.path == bp.path && bp2.abspath == bp.abspath && bp2.line == bp.line
374379
function Base.show(io::IO, bp::BreakpointFileLocation)
375380
print(io, bp.path, ':', bp.line)
@@ -378,4 +383,3 @@ function Base.show(io::IO, bp::BreakpointFileLocation)
378383
print(io, " [disabled]")
379384
end
380385
end
381-

src/utils.jl

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -335,16 +335,24 @@ end
335335
Return the local variables as a vector of `Variable`[@ref].
336336
"""
337337
function locals(frame::Frame)
338-
vars = Variable[]
338+
vars, var_counter = Variable[], Int[]
339+
varlookup = Dict{Symbol,Int}()
339340
data, code = frame.framedata, frame.framecode
340-
added = Set{Symbol}()
341341
slotnames = code.src.slotnames::SlotNamesType
342-
for sym in slotnames
343-
sym added && continue
344-
idx = get(data.last_reference, sym, 0)
345-
idx == 0 && continue
346-
push!(vars, Variable(something(data.locals[idx]), sym, false))
347-
push!(added, sym)
342+
for (sym, counter, val) in zip(slotnames, data.last_reference, data.locals)
343+
counter == 0 && continue
344+
var = Variable(something(val), sym, false)
345+
idx = get(varlookup, sym, 0)
346+
if idx > 0
347+
if counter > var_counter[idx]
348+
vars[idx] = var
349+
var_counter[idx] = counter
350+
end
351+
else
352+
varlookup[sym] = length(vars)+1
353+
push!(vars, var)
354+
push!(var_counter, counter)
355+
end
348356
end
349357
if code.scope isa Method
350358
syms = sparam_syms(code.scope)
@@ -424,7 +432,8 @@ function eval_code end
424432
eval_code(frame::Frame, command::AbstractString) = eval_code(frame, Base.parse_input_line(command))
425433
function eval_code(frame::Frame, expr)
426434
maybe_quote(x) = (isa(x, Expr) || isa(x, Symbol)) ? QuoteNode(x) : x
427-
435+
code = frame.framecode
436+
data = frame.framedata
428437
isexpr(expr, :toplevel) && (expr = expr.args[end])
429438

430439
if isexpr(expr, :toplevel)
@@ -443,10 +452,14 @@ function eval_code(frame::Frame, expr)
443452
j = 1
444453
for (i, v) in enumerate(vars)
445454
if v.isparam
446-
frame.framedata.sparams[j] = res[i]
455+
data.sparams[j] = res[i]
447456
j += 1
448457
else
449-
frame.framedata.locals[frame.framedata.last_reference[v.name]] = Some{Any}(v.value isa Core.Box ? Core.Box(res[i]) : res[i])
458+
slot_indices = code.slotnamelists[v.name]
459+
idx = argmax(data.last_reference[slot_indices])
460+
slot_idx = slot_indices[idx]
461+
data.last_reference[slot_idx] = (frame.assignment_counter += 1)
462+
data.locals[slot_idx] = Some{Any}(v.value isa Core.Box ? Core.Box(res[i]) : res[i])
450463
end
451464
end
452465
eval_res

test/breakpoints.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,24 @@ struct Squarer end
116116
@test !any(v->v.name == :b, var)
117117
@test filter(v->v.name == :a, var)[1].value == 2
118118

119+
# Method with local scope (two slots with same name)
120+
ln = @__LINE__
121+
function ftwoslots()
122+
y = 1
123+
z = let y = y
124+
y = y + 2
125+
rand()
126+
end
127+
y = y + 1
128+
return z
129+
end
130+
bp = breakpoint(@__FILE__, ln+5, :(y > 2))
131+
frame, bp2 = @interpret ftwoslots()
132+
var = JuliaInterpreter.locals(leaf(frame))
133+
@test filter(v->v.name == :y, var)[1].value == 3
134+
remove(bp)
135+
bp = breakpoint(@__FILE__, ln+8, :(y > 2))
136+
@test isa(@interpret(ftwoslots()), Float64)
119137

120138
# Direct return
121139
@breakpoint gcd(1,1) a==5

0 commit comments

Comments
 (0)