Skip to content

Commit a265c14

Browse files
authored
[incomplete] changes needed to adapt to compressed line table format in Julia (#606)
This commit is incomplete, but since I don't hold back other packages in the ecosystem, I'm going to release a version that allows for precompilation of this package in v1.12. I've made the issues for specific problems found during the PR and we can fix them one by one later on.
1 parent 31253a0 commit a265c14

File tree

5 files changed

+101
-32
lines changed

5 files changed

+101
-32
lines changed

src/construct.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ function prepare_framecode(method::Method, @nospecialize(argtypes); enter_genera
172172
if (!isempty(lenv) && (hasarg(isidentical(:llvmcall), code.code) ||
173173
hasarg(isidentical(Base.llvmcall), code.code) ||
174174
hasarg(a->is_global_ref(a, Base, :llvmcall), code.code))) ||
175-
hasarg(isidentical(:iolock_begin), code.code)
175+
hasarg(isidentical(:iolock_begin), code.code)
176176
return Compiled()
177177
end
178178
framecode = FrameCode(method, code; generator=generator)

src/interpret.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,14 +460,25 @@ function coverage_visit_line!(frame::Frame)
460460
pc, code = frame.pc, frame.framecode
461461
code.report_coverage || return
462462
src = code.src
463+
@static if VERSION v"1.12.0-DEV.173"
464+
lineinfo = linetable(src, pc)
465+
file, line = lineinfo.file, lineinfo.line
466+
if line != frame.last_codeloc
467+
file isa Symbol || (file = Symbol(file)::Symbol)
468+
@ccall jl_coverage_visit_line(file::Cstring, sizeof(file)::Csize_t, line::Cint)::Cvoid
469+
frame.last_codeloc = line
470+
end
471+
else # VERSION < v"1.12.0-DEV.173"
463472
codeloc = src.codelocs[pc]
464473
if codeloc != frame.last_codeloc && codeloc != 0
465474
linetable = src.linetable::Vector{Any}
466475
lineinfo = linetable[codeloc]::Core.LineInfoNode
467-
file, line = String(lineinfo.file), lineinfo.line
468-
ccall(:jl_coverage_visit_line, Cvoid, (Cstring, Csize_t, Cint), file, sizeof(file), line)
476+
file, line = lineinfo.file, lineinfo.line
477+
file isa Symbol || (file = Symbol(file)::Symbol)
478+
@ccall jl_coverage_visit_line(file::Cstring, sizeof(file)::Csize_t, line::Cint)::Cvoid
469479
frame.last_codeloc = codeloc
470480
end
481+
end # @static if
471482
end
472483

473484
# For "profiling" where JuliaInterpreter spends its time. See the commented-out block

src/types.jl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,24 @@ function FrameCode(scope, src::CodeInfo; generator=false, optimize=true)
147147

148148
lt = linetable(src)
149149
unique_files = Set{Symbol}()
150+
@static if VERSION v"1.12.0-DEV.173"
151+
function pushuniquefiles!(unique_files::Set{Symbol}, lt)
152+
for edge in lt.edges
153+
pushuniquefiles!(unique_files, edge)
154+
end
155+
linetable = lt.linetable
156+
if linetable === nothing
157+
push!(unique_files, Base.IRShow.debuginfo_file1(lt))
158+
else
159+
pushuniquefiles!(unique_files, linetable)
160+
end
161+
end
162+
pushuniquefiles!(unique_files, lt)
163+
else # VERSION < v"1.12.0-DEV.173"
150164
for entry in lt
151165
push!(unique_files, entry.file)
152166
end
167+
end # @static if
153168

154169
framecode = FrameCode(scope, src, methodtables, breakpoints, slotnamelists, used, generator, report_coverage, unique_files)
155170
if scope isa Method
@@ -237,7 +252,7 @@ mutable struct Frame
237252
assignment_counter::Int64
238253
caller::Union{Frame,Nothing}
239254
callee::Union{Frame,Nothing}
240-
last_codeloc::Int32
255+
last_codeloc::Int
241256
end
242257
function Frame(framecode::FrameCode, framedata::FrameData, pc=1, caller=nothing)
243258
if length(junk_frames) > 0

src/utils.jl

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -258,38 +258,88 @@ end
258258

259259
# These getters improve inference since fieldtype(CodeInfo, :linetable)
260260
# and fieldtype(CodeInfo, :codelocs) are both Any
261-
const LineTypes = Union{LineNumberNode,Core.LineInfoNode}
261+
@static if VERSION v"1.12.0-DEV.173"
262+
const LineTypes = Union{LineNumberNode,Base.IRShow.LineInfoNode}
263+
else
264+
const LineTypes = Union{LineNumberNode,Core.LineInfoNode}
265+
end
262266
function linetable(arg)
263267
if isa(arg, Frame)
264268
arg = arg.framecode
265269
end
266270
if isa(arg, FrameCode)
267271
arg = arg.src
268272
end
269-
return (arg::CodeInfo).linetable::Union{Vector{Core.LineInfoNode},Vector{Any}} # issue #264
273+
ci = arg::CodeInfo
274+
@static if VERSION v"1.12.0-DEV.173"
275+
return ci.debuginfo
276+
else # VERSION < v"1.12.0-DEV.173"
277+
return ci.linetable::Union{Vector{Core.LineInfoNode},Vector{Any}} # issue #264
278+
end # @static if
270279
end
271-
_linetable(list::Vector, i::Integer) = list[i]::Union{Expr,LineTypes}
272280
function linetable(arg, i::Integer; macro_caller::Bool=false)::Union{Expr,LineTypes}
273281
lt = linetable(arg)
274-
lineinfo = _linetable(lt, i)
282+
@static if VERSION v"1.12.0-DEV.173"
283+
# TODO: decode the linetable at this frame efficiently by reimplementing this here
284+
# TODO: get the contextual name from the parent, rather than returning "n/a" (which breaks Cthulhu)
285+
return Base.IRShow.buildLineInfoNode(lt, :var"n/a", i)[1] # ignore all inlining / macro expansion / etc :(
286+
else # VERSION < v"1.12.0-DEV.173"
287+
lin = lt[i]::Union{Expr,LineTypes}
275288
if macro_caller
276-
while lineinfo isa Core.LineInfoNode && lineinfo.method === Symbol("macro expansion") && lineinfo.inlined_at != 0
277-
lineinfo = _linetable(lt, lineinfo.inlined_at)
289+
while lin isa Core.LineInfoNode && lin.method === Symbol("macro expansion") && lin.inlined_at != 0
290+
lin = lt[lin.inlined_at]::Union{Expr,LineTypes}
278291
end
279292
end
280-
return lineinfo
293+
return lin
294+
end # @static if
295+
end
296+
297+
@static if VERSION v"1.12.0-DEV.173"
298+
299+
function getlastline(arg)
300+
debuginfo = linetable(arg)
301+
while true
302+
ltnext = debuginfo.linetable
303+
ltnext === nothing && break
304+
debuginfo = ltnext
305+
end
306+
lastline = 0
307+
for k = 0:typemax(Int)
308+
codeloc = Core.Compiler.getdebugidx(debuginfo, k)
309+
line::Int = codeloc[1]
310+
line < 0 && break
311+
lastline = max(lastline, line)
312+
end
313+
return lastline
314+
end
315+
function codelocs(arg, i::Integer)
316+
debuginfo = linetable(arg)
317+
codeloc = Core.Compiler.getdebugidx(debuginfo, i)
318+
line::Int = codeloc[1]
319+
line < 0 && return 0# broken or disabled debug info?
320+
if line == 0 && codeloc[2] == 0
321+
return 0 # no line number update
322+
end
323+
return i
281324
end
282325

326+
else # VERSION < v"1.12.0-DEV.173"
327+
328+
getfirstline(arg) = getline(linetable(arg)[begin])
329+
getlastline(arg) = getline(linetable(arg)[end])
283330
function codelocs(arg)
284331
if isa(arg, Frame)
285332
arg = arg.framecode
286333
end
287334
if isa(arg, FrameCode)
288335
arg = arg.src
289336
end
290-
return (arg::CodeInfo).codelocs::Vector{Int32}
337+
ci = arg::CodeInfo
338+
return ci.codelocs
291339
end
292-
codelocs(arg, i::Integer) = codelocs(arg)[i] # for consistency with linetable (but no extra benefit here)
340+
codelocs(arg, i::Integer) = codelocs(arg)[i]
341+
342+
end # @static if
293343

294344
function lineoffset(framecode::FrameCode)
295345
offset = 0
@@ -302,9 +352,9 @@ function lineoffset(framecode::FrameCode)
302352
end
303353

304354
function getline(ln::Union{LineTypes,Expr})
305-
_getline(ln::LineTypes) = ln.line
306-
_getline(ln::Expr) = ln.args[1] # assuming ln.head === :line
307-
return Int(_getline(ln))::Int
355+
_getline(ln::LineTypes) = Int(ln.line)
356+
_getline(ln::Expr) = ln.args[1]::Int # assuming ln.head === :line
357+
return _getline(ln)
308358
end
309359
function getfile(ln::Union{LineTypes,Expr})
310360
_getfile(ln::LineTypes) = ln.file::Symbol
@@ -367,7 +417,7 @@ function codelocation(code::CodeInfo, idx::Int)
367417
idx′ = idx
368418
# look ahead if we are on a meta line
369419
while idx′ < length(code.code)
370-
codeloc = codelocs(code)[idx′]
420+
codeloc = codelocs(code, idx′)
371421
codeloc == 0 || return codeloc
372422
ex = code.code[idx′]
373423
ex === nothing || isexpr(ex, :meta) || break
@@ -377,7 +427,7 @@ function codelocation(code::CodeInfo, idx::Int)
377427
# if zero, look behind until we find where we last might have had a line
378428
while idx′ > 0
379429
ex = code.code[idx′]
380-
codeloc = codelocs(code)[idx′]
430+
codeloc = codelocs(code, idx′)
381431
codeloc == 0 || return codeloc
382432
idx′ -= 1
383433
end
@@ -390,13 +440,11 @@ function compute_corrected_linerange(method::Method)
390440
offset = line1 - method.line
391441
@assert !is_generated(method)
392442
src = JuliaInterpreter.get_source(method)
393-
lastline = linetable(src)[end]::LineTypes
394-
return line1:getline(lastline) + offset
443+
lastline = getlastline(src)
444+
return line1:lastline + offset
395445
end
396446

397-
function compute_linerange(framecode)
398-
getline(linetable(framecode, 1)):getline(last(linetable(framecode)))
399-
end
447+
compute_linerange(framecode) = getfirstline(framecode):getlastline(framecode)
400448

401449
function statementnumbers(framecode::FrameCode, line::Integer, file::Symbol)
402450
# Check to see if this framecode really contains that line. Methods that fill in a default positional argument,
@@ -434,7 +482,6 @@ function statementnumbers(framecode::FrameCode, line::Integer, file::Symbol)
434482
return stmtidxs
435483
end
436484

437-
438485
# If the exact line number does not exist in the line table, take the one that is closest after that line
439486
# restricted to the line range of the current scope.
440487
scope = framecode.scope
@@ -506,9 +553,7 @@ breakpointchar(framecode, stmtidx) =
506553
function print_framecode(io::IO, framecode::FrameCode; pc=0, range=1:nstatements(framecode), kwargs...)
507554
iscolor = get(io, :color, false)
508555
ndstmt = ndigits(nstatements(framecode))
509-
lt = linetable(framecode)
510-
offset = lineoffset(framecode)
511-
ndline = isempty(lt) ? 0 : ndigits(getline(lt[end]) + offset)
556+
ndline = ndigits(getlastline(framecode) + lineoffset(framecode))
512557
nullline = " "^ndline
513558
src = copy(framecode.src)
514559
replace_coretypes!(src; rev=true)
@@ -759,12 +804,11 @@ function Base.StackTraces.StackFrame(frame::Frame)
759804
Base.StackFrame(
760805
fname,
761806
Symbol(getfile(frame)),
762-
@something(linenumber(frame), getline(linetable(frame, 1))),
807+
@something(linenumber(frame), getfirstline(frame)),
763808
mi,
764809
false,
765810
false,
766-
C_NULL
767-
)
811+
C_NULL)
768812
end
769813

770814
function Base.show_backtrace(io::IO, frame::Frame)

test/utils.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@ struct Aborted # for signaling that some statement or test blocks were interr
3636
end
3737

3838
function Aborted(frame::Frame, pc)
39-
src = frame.framecode.src
40-
lineidx = src.codelocs[pc]
39+
lineidx = JuliaInterpreter.codelocs(frame, pc)
4140
lineinfo = JuliaInterpreter.linetable(frame, lineidx; macro_caller=true)
4241
return Aborted(lineinfo)
4342
end

0 commit comments

Comments
 (0)