diff --git a/src/ir/ir.jl b/src/ir/ir.jl index 113df40..c89c1e3 100644 --- a/src/ir/ir.jl +++ b/src/ir/ir.jl @@ -1,10 +1,22 @@ -using Core.Compiler: 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 e9d7f24..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,6 +12,18 @@ import Core.Compiler: IRCode, CFG, GotoIfNot, ReturnNode, StmtRange import Core.Compiler: InstructionStream end +@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)) +else + addline!(lines, li) = push!(lines, li) +end + unvars(ex) = prewalk(x -> x isa Variable ? SSAValue(x.id) : x, ex) function IRCode(ir::IR) @@ -34,7 +46,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 +64,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 +73,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 +85,39 @@ 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 + 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) end @@ -118,7 +163,27 @@ 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 = 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) + linode = first(LI) # ::Base.IRShow.LineInfoNode + push!(linetable, LineInfoNode(linode.file, linode.line)) + codelocs[pc] = length(linetable) + end + end + + codelocs, linetable + else + ci.codelocs, map(li -> LineInfoNode(li.file, li.line), ci.linetable) + end + ir = IR(linetable, meta = meta) _rename = Dict() rename(ex) = prewalk(ex) do x haskey(_rename, x) && return _rename[x] @@ -151,7 +216,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)