From dd1aa0d7cf86333d3242903915d9dd59e4e7f1bb Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Thu, 28 Mar 2024 11:04:22 +0100 Subject: [PATCH 1/2] 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..53d8035 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.15" + ci.inferred = false + end ci.ssavaluetypes = length(ci.code) slots!(ci) fill!(ci.slotflags, 0) From 1f0968cfecc509c1551ee10e824577d44cd0ead7 Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Wed, 3 Apr 2024 14:29:53 +0200 Subject: [PATCH 2/2] move LineInfoNode definition in IRTools --- src/ir/ir.jl | 14 +++++++++++++- src/ir/wrap.jl | 23 ++++++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/ir/ir.jl b/src/ir/ir.jl index 4dd5edf..c89c1e3 100644 --- a/src/ir/ir.jl +++ b/src/ir/ir.jl @@ -1,10 +1,22 @@ -using Base.IRShow: LineInfoNode import Base: push!, insert!, getindex, setindex!, iterate, length # We have our own versions of these in order to # (1) be more robust to Base IR changes, and # (2) make sure that mistakes/bugs do not cause bad LLVM IR. +""" + LineInfoNode(file::Symbol, line::Int32, [inlined_at::Int32]) + +Represents line information about a statement; Inlined statements has a non-zero +`inlined_at` which represents the "parent" location in the linetable array. +""" +struct LineInfoNode + file::Symbol + line::Int32 + inlined_at::Int32 +end +LineInfoNode(file, line) = LineInfoNode(file, line, Int32(0)) + struct Undefined end const undef = Undefined() diff --git a/src/ir/wrap.jl b/src/ir/wrap.jl index 656e473..03553dd 100644 --- a/src/ir/wrap.jl +++ b/src/ir/wrap.jl @@ -4,7 +4,7 @@ using MacroTools: isexpr, prewalk using ..Inner, ..IRTools import ..Inner: IR, Variable, Statement, Branch, BasicBlock, Meta, block!, - unreachable, varmap, argument!, branch!, return! + unreachable, varmap, argument!, branch!, return!, LineInfoNode import Core: CodeInfo, GotoNode, SSAValue import Core.Compiler: IRCode, CFG, GotoIfNot, ReturnNode, StmtRange @@ -12,8 +12,11 @@ import Core.Compiler: IRCode, CFG, GotoIfNot, ReturnNode, StmtRange import Core.Compiler: InstructionStream end -unvars(ex) = prewalk(x -> x isa Variable ? SSAValue(x.id) : x, ex) - +@static if VERSION ≥ v"1.9.0-DEV.502" + const LineType = Int32 +else + const LineType = Int +end @static if VERSION ≥ v"1.12.0-DEV.173" addline!(lines, li) = push!(lines, li, Int32(0), Int32(0)) @@ -21,6 +24,8 @@ else addline!(lines, li) = push!(lines, li) end +unvars(ex) = prewalk(x -> x isa Variable ? SSAValue(x.id) : x, ex) + function IRCode(ir::IR) defs = Dict() stmts, types, lines = [], [], Int32[] @@ -108,7 +113,10 @@ function IRCode(ir::IR) 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) + mod, meth = ir.meta isa Meta ? (ir.meta.method.module, ir.meta.method) : (Main, nothing) + linetable = map(li -> Core.LineInfoNode(mod, meth, li.file, LineType(li.line), LineType(li.inlined_at)), + ir.lines) + IRCode(stmts, cfg, linetable, ir.blocks[1].argtypes, meta, sps) end else IRCode(stmts, types, lines, flags, cfg, ir.lines, ir.blocks[1].argtypes, [], sps) @@ -159,20 +167,21 @@ function IR(ci::CodeInfo, nargs::Integer; meta = nothing) def = isnothing(meta) ? :var"n/a" : meta.instance N = length(ci.code) codelocs = fill(0, N) - linetable = Base.IRShow.LineInfoNode[] + linetable = 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)) + linode = first(LI) # ::Base.IRShow.LineInfoNode + push!(linetable, LineInfoNode(linode.file, linode.line)) codelocs[pc] = length(linetable) end end codelocs, linetable else - ci.codelocs, Core.LineInfoNode[ci.linetable...] + ci.codelocs, map(li -> LineInfoNode(li.file, li.line), ci.linetable) end ir = IR(linetable, meta = meta) _rename = Dict()