From 5b3617f04454ce4a26916dc5549e350bdbe7d118 Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Thu, 28 Mar 2024 11:04:22 +0100 Subject: [PATCH] adapt to upstream linetable change `Core.Compiler.LineInfoNode` was removed in https://github.com/JuliaLang/julia/pull/52415 but kept in `Core` for serializer compatibility reasons (see https://github.com/JuliaLang/julia/blob/e07c0f1ddbfc89ad1ac4dda7246d8ed5d0d57c19/base/boot.jl#L491). Linetable representation in Julia IR has been changed to be more compact. This commit updates IRTools to decode and generate valid debug information according to these changes. Adaptation for IRTools is relatively straightforward in the sense that IRTools operates on untyped IR which does not contains inline statements. However, IRTools contains an inlining pass but it did not account for linetable information previously. In this change, I have made the choice to use the same line for all inlined statements. This can be further improved in the future. --- src/ir/ir.jl | 2 +- src/ir/wrap.jl | 68 +++++++++++++++++++++++++++++++++++++---- src/passes/inline.jl | 9 ++++-- src/reflection/utils.jl | 4 ++- 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/ir/ir.jl b/src/ir/ir.jl index 113df40..4dd5edf 100644 --- a/src/ir/ir.jl +++ b/src/ir/ir.jl @@ -1,4 +1,4 @@ -using Core.Compiler: LineInfoNode +using Base.IRShow: LineInfoNode import Base: push!, insert!, getindex, setindex!, iterate, length # We have our own versions of these in order to diff --git a/src/ir/wrap.jl b/src/ir/wrap.jl index e9d7f24..656e473 100644 --- a/src/ir/wrap.jl +++ b/src/ir/wrap.jl @@ -14,6 +14,13 @@ end unvars(ex) = prewalk(x -> x isa Variable ? SSAValue(x.id) : x, ex) + +@static if VERSION ≥ v"1.12.0-DEV.173" + addline!(lines, li) = push!(lines, li, Int32(0), Int32(0)) +else + addline!(lines, li) = push!(lines, li) +end + function IRCode(ir::IR) defs = Dict() stmts, types, lines = [], [], Int32[] @@ -34,7 +41,7 @@ function IRCode(ir::IR) end push!(stmts, ex) push!(types, st.type) - push!(lines, st.line) + addline!(lines, st.line) end for br in BasicBlock(b).branches if IRTools.isreturn(br) @@ -52,7 +59,7 @@ function IRCode(ir::IR) cond = get(defs, br.condition, br.condition) |> unvars push!(stmts, GotoIfNot(cond, br.block)) end - push!(types, Any); push!(lines, 0) + push!(types, Any); addline!(lines, Int32(0)) end push!(index, length(stmts)+1) end @@ -61,7 +68,8 @@ function IRCode(ir::IR) preds = map.(x -> x.id, IRTools.predecessors.(IRTools.blocks(ir))) bs = Core.Compiler.BasicBlock.(ranges, preds, succs) cfg = CFG(bs, index) - flags = [0x00 for _ in stmts] + + flags = UInt32[0x00 for _ in stmts] sps = VERSION > v"1.2-" ? (VERSION >= v"1.10.0-DEV.552" ? Core.Compiler.VarState[] : []) : Core.svec() @static if VERSION > v"1.6-" @@ -72,7 +80,36 @@ function IRCode(ir::IR) end stmts = InstructionStream(stmts, types, stmtinfo, lines, flags) meta = @static VERSION < v"1.9.0-DEV.472" ? [] : Expr[] - IRCode(stmts, cfg, ir.lines, ir.blocks[1].argtypes, meta, sps) + @static if VERSION ≥ v"1.12.0-DEV.173" + nlocs = length(types) + codelocs = fill(Int32(0), 3nlocs) + + if !isempty(ir.lines) + LI = first(ir.lines) + topfile, topline = LI.file, LI.line + + for i in 1:nlocs + lineidx = lines[3i - 2] + if lineidx == 0 + continue + end + # TODO: support inlining, see passes/inline.jl + @assert LI.file === topfile && LI.inlined_at == 0 + LI = ir.lines[lineidx] + codelocs[3i - 2] = LI.line + end + else + topline = Int32(1) + end + codelocs = @ccall jl_compress_codelocs(topline::Int32, codelocs::Any, nlocs::Csize_t)::Any + + debuginfo = Core.Compiler.DebugInfoStream(lines) + debuginfo.def = ir.meta isa Meta ? ir.meta.instance : :var"n/a" + debuginfo.linetable = Core.DebugInfo(debuginfo.def, nothing, Core.svec(), codelocs) + IRCode(stmts, cfg, debuginfo, ir.blocks[1].argtypes, meta, sps) + else + IRCode(stmts, cfg, ir.lines, ir.blocks[1].argtypes, meta, sps) + end else IRCode(stmts, types, lines, flags, cfg, ir.lines, ir.blocks[1].argtypes, [], sps) end @@ -118,7 +155,26 @@ slotname(ci, s) = Symbol(:_, s.id) function IR(ci::CodeInfo, nargs::Integer; meta = nothing) bs = blockstarts(ci) - ir = IR(Core.LineInfoNode[ci.linetable...], meta = meta) + codelocs, linetable = @static if VERSION ≥ v"1.12.0-DEV.173" + def = isnothing(meta) ? :var"n/a" : meta.instance + N = length(ci.code) + codelocs = fill(0, N) + linetable = Base.IRShow.LineInfoNode[] + + # NOTE: we could be faster about decoding here and support inlining? + for pc in 1:N + LI = Base.IRShow.buildLineInfoNode(ci.debuginfo, def, pc) + if !isempty(LI) + push!(linetable, first(LI)) + codelocs[pc] = length(linetable) + end + end + + codelocs, linetable + else + ci.codelocs, Core.LineInfoNode[ci.linetable...] + end + ir = IR(linetable, meta = meta) _rename = Dict() rename(ex) = prewalk(ex) do x haskey(_rename, x) && return _rename[x] @@ -151,7 +207,7 @@ function IR(ci::CodeInfo, nargs::Integer; meta = nothing) elseif isreturn(ex) return!(ir, rename(retval(ex))) else - _rename[Core.SSAValue(i)] = push!(ir, IRTools.stmt(rename(ex), line = ci.codelocs[i])) + _rename[Core.SSAValue(i)] = push!(ir, IRTools.stmt(rename(ex), line = codelocs[i])) end end return ir diff --git a/src/passes/inline.jl b/src/passes/inline.jl index d85ea64..bd8b038 100644 --- a/src/passes/inline.jl +++ b/src/passes/inline.jl @@ -8,14 +8,14 @@ function fixup_blocks!(ir, n) end end -function inlinehere!(ir, source, args...) +function inlinehere!(ir, line, source, args...) source = merge_returns!(copy(source)) # TODO preserve type info offset = length(blocks(ir.to))-1 env = Dict() retvalue = nothing rename(x::Variable) = env[x] rename(x::Expr) = Expr(x.head, rename.(x.args)...) - rename(x::Statement) = stmt(x, expr = rename(x.expr)) + rename(x::Statement) = stmt(x; expr=rename(x.expr), line=line) rename(x) = x for (name, arg) in zip(arguments(source), args) env[name] = arg @@ -78,9 +78,12 @@ function inline(ir::IR, loc::Variable, source::IR) if v === loc startblock = length(blocks(pr.to)) fixup_blocks!(pr.to, length(blocks(source))) + # TODO: when inlining, we set all statements from source + # .... at the line from loc. Ideally, we use the `inlined_at` field. + line = ir[loc].line ex = ir[loc].expr delete!(pr, v) - v′ = inlinehere!(pr, source, ex.args...) + v′ = inlinehere!(pr, line, source, ex.args...) substitute!(pr, v, substitute(pr, v′)) end end diff --git a/src/reflection/utils.jl b/src/reflection/utils.jl index 5fb4367..8240f79 100644 --- a/src/reflection/utils.jl +++ b/src/reflection/utils.jl @@ -156,7 +156,9 @@ end function update!(ci::CodeInfo, ir::Core.Compiler.IRCode) replace_code_newstyle!(ci, ir, length(ir.argtypes)) - ci.inferred = false + @static if VERSION < v"1.12.0-DEV.73" + ci.inferred = false + end ci.ssavaluetypes = length(ci.code) slots!(ci) fill!(ci.slotflags, 0)