Skip to content

Commit 97c3717

Browse files
committed
allow multiple inlining again
Before #52415 we could do multiple rounds of inlining on IR that had already been inlined, but this was no longer possible. By removing the assertion that was introduced in #52415, this commit makes it possible to do multi-inlining once more. Note that to fully solve this, though, we need to enhance `ir_inline_linetable!` so it can add new linetables to an inner linetable that's already been inlined. This commit notes that enhancement as something we need to do later, leaving it with a TODO comment.
1 parent e9d25ca commit 97c3717

File tree

3 files changed

+52
-22
lines changed

3 files changed

+52
-22
lines changed

base/compiler/ssair/inlining.jl

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,8 @@ function finish_cfg_inline!(state::CFGInliningState)
300300
end
301301
end
302302

303-
function ir_inline_linetable!(debuginfo::DebugInfoStream, inlinee_debuginfo::DebugInfo, inlinee::MethodInstance)
303+
# TODO append `inlinee_debuginfo` to inner linetable when `inlined_at[2] ≠ 0`
304+
function ir_inline_linetable!(debuginfo::DebugInfoStream, inlinee_debuginfo::DebugInfo, inlined_at::NTuple{3,Int32})
304305
# Append the linetable of the inlined function to our edges table
305306
linetable_offset = 1
306307
while true
@@ -312,16 +313,14 @@ function ir_inline_linetable!(debuginfo::DebugInfoStream, inlinee_debuginfo::Deb
312313
end
313314
linetable_offset += 1
314315
end
315-
return Int32(linetable_offset)
316+
return (inlined_at[1], Int32(linetable_offset), Int32(0))
316317
end
317318

318319
function ir_prepare_inlining!(insert_node!::Inserter, inline_target::Union{IRCode, IncrementalCompact},
319-
ir::IRCode, di::DebugInfo, mi::MethodInstance, inlined_at::Int32, argexprs::Vector{Any})
320+
ir::IRCode, di::DebugInfo, mi::MethodInstance, inlined_at::NTuple{3,Int32}, argexprs::Vector{Any})
320321
def = mi.def::Method
321322
debuginfo = inline_target isa IRCode ? inline_target.debuginfo : inline_target.ir.debuginfo
322-
323-
linetable_offset = ir_inline_linetable!(debuginfo, di, mi)
324-
topline = (inlined_at, linetable_offset, Int32(0))
323+
topline = new_inlined_at = ir_inline_linetable!(debuginfo, di, inlined_at)
325324
if should_insert_coverage(def.module, di)
326325
insert_node!(NewInstruction(Expr(:code_coverage_effect), Nothing, topline))
327326
end
@@ -343,7 +342,7 @@ function ir_prepare_inlining!(insert_node!::Inserter, inline_target::Union{IRCod
343342
NewInstruction(Expr(:call, GlobalRef(Core, :getfield), argexprs[1], QuoteNode(:captures)),
344343
ir.argtypes[1], topline))
345344
end
346-
return SSASubstitute(mi, argexprs, spvals_ssa, (inlined_at, linetable_offset))
345+
return SSASubstitute(mi, argexprs, spvals_ssa, new_inlined_at)
347346
end
348347

349348
function adjust_boundscheck!(inline_compact::IncrementalCompact, idx′::Int, stmt::Expr, boundscheck::Symbol)
@@ -359,8 +358,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector
359358
item::InliningTodo, boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}})
360359
# Ok, do the inlining here
361360
inlined_at = compact.result[idx][:line]
362-
@assert inlined_at[2] == 0 "already inlined this instruction"
363-
ssa_substitute = ir_prepare_inlining!(InsertHere(compact), compact, item.ir, item.di, item.mi, inlined_at[1], argexprs)
361+
ssa_substitute = ir_prepare_inlining!(InsertHere(compact), compact, item.ir, item.di, item.mi, inlined_at, argexprs)
364362
boundscheck = has_flag(compact.result[idx], IR_FLAG_INBOUNDS) ? :off : boundscheck
365363

366364
# If the iterator already moved on to the next basic block,
@@ -1771,7 +1769,7 @@ struct SSASubstitute
17711769
mi::MethodInstance
17721770
arg_replacements::Vector{Any}
17731771
spvals_ssa::Union{Nothing,SSAValue}
1774-
inlined_at::NTuple{2,Int32} # TODO: add a map also, so that ssaidx doesn't need to equal inlined_idx?
1772+
inlined_at::NTuple{3,Int32} # TODO: add a map also, so that ssaidx doesn't need to equal inlined_idx?
17751773
end
17761774

17771775
function insert_spval!(insert_node!::Inserter, spvals_ssa::SSAValue, spidx::Int, do_isdefined::Bool)

base/compiler/ssair/passes.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1516,7 +1516,7 @@ function try_inline_finalizer!(ir::IRCode, argexprs::Vector{Any}, idx::Int,
15161516
add_inlining_backedge!(et, mi)
15171517

15181518
# TODO: Should there be a special line number node for inlined finalizers?
1519-
inline_at = ir[SSAValue(idx)][:line][1]
1519+
inline_at = ir[SSAValue(idx)][:line]
15201520
ssa_substitute = ir_prepare_inlining!(InsertBefore(ir, SSAValue(idx)), ir, src, di, mi, inline_at, argexprs)
15211521

15221522
# TODO: Use the actual inliner here rather than open coding this special purpose inliner.

test/compiler/inline.jl

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,23 +1818,55 @@ let src = code_typed1((AtomicMemoryRef{Int},)) do a
18181818
@test count(isinvokemodify(:+), src.code) == 1
18191819
end
18201820

1821-
1822-
18231821
# apply `ssa_inlining_pass` multiple times
1824-
let interp = Core.Compiler.NativeInterpreter()
1822+
func_mul_int(a::Int, b::Int) = Core.Intrinsics.mul_int(a, b)
1823+
multi_inlining1(a::Int, b::Int) = @noinline func_mul_int(a, b)
1824+
let i::Int, continue_::Bool
1825+
interp = Core.Compiler.NativeInterpreter()
18251826
# check if callsite `@noinline` annotation works
1826-
ir, = Base.code_ircode((Int,Int); optimize_until="inlining", interp) do a, b
1827-
@noinline a*b
1828-
end |> only
1829-
i = findfirst(isinvoke(:*), ir.stmts.stmt)
1827+
ir, = only(Base.code_ircode(multi_inlining1, (Int,Int); optimize_until="inlining", interp))
1828+
i = findfirst(isinvoke(:func_mul_int), ir.stmts.stmt)
18301829
@test i !== nothing
1831-
1832-
# ok, now delete the callsite flag, and see the second inlining pass can inline the call
1830+
# now delete the callsite flag, and see the second inlining pass can inline the call
18331831
@eval Core.Compiler $ir.stmts[$i][:flag] &= ~IR_FLAG_NOINLINE
18341832
inlining = Core.Compiler.InliningState(interp)
18351833
ir = Core.Compiler.ssa_inlining_pass!(ir, inlining, false)
1836-
@test count(isinvoke(:*), ir.stmts.stmt) == 0
1837-
@test count(iscall((ir, Core.Intrinsics.mul_int)), ir.stmts.stmt) == 1
1834+
@test findfirst(isinvoke(:func_mul_int), ir.stmts.stmt) === nothing
1835+
@test (i = findfirst(iscall((ir, Core.Intrinsics.mul_int)), ir.stmts.stmt)) !== nothing
1836+
lins = Base.IRShow.buildLineInfoNode(ir.debuginfo, nothing, i)
1837+
@test (continue_ = length(lins) == 2) # :multi_inlining1 -> :func_mul_int
1838+
if continue_
1839+
def1 = lins[1].method
1840+
@test def1 isa Core.MethodInstance && def1.def.name === :multi_inlining1
1841+
def2 = lins[2].method
1842+
@test def2 isa Core.MethodInstance && def2.def.name === :func_mul_int
1843+
end
1844+
end
1845+
1846+
call_func_mul_int(a::Int, b::Int) = @noinline func_mul_int(a, b)
1847+
multi_inlining2(a::Int, b::Int) = call_func_mul_int(a, b)
1848+
let i::Int, continue_::Bool
1849+
interp = Core.Compiler.NativeInterpreter()
1850+
# check if callsite `@noinline` annotation works
1851+
ir, = only(Base.code_ircode(multi_inlining2, (Int,Int); optimize_until="inlining", interp))
1852+
i = findfirst(isinvoke(:func_mul_int), ir.stmts.stmt)
1853+
@test i !== nothing
1854+
# now delete the callsite flag, and see the second inlining pass can inline the call
1855+
@eval Core.Compiler $ir.stmts[$i][:flag] &= ~IR_FLAG_NOINLINE
1856+
inlining = Core.Compiler.InliningState(interp)
1857+
ir = Core.Compiler.ssa_inlining_pass!(ir, inlining, false)
1858+
@test findfirst(isinvoke(:func_mul_int), ir.stmts.stmt) === nothing
1859+
@test (i = findfirst(iscall((ir, Core.Intrinsics.mul_int)), ir.stmts.stmt)) !== nothing
1860+
lins = Base.IRShow.buildLineInfoNode(ir.debuginfo, nothing, i)
1861+
@test_broken (continue_ = length(lins) == 3) # see TODO in `ir_inline_linetable!`
1862+
if continue_
1863+
def1 = lins[1].method
1864+
@test def1 isa Core.MethodInstance && def1.def.name === :multi_inlining2
1865+
def2 = lins[2].method
1866+
@test def2 isa Core.MethodInstance && def2.def.name === :call_func_mul_int
1867+
def3 = lins[3].method
1868+
@test def3 isa Core.MethodInstance && def3.def.name === :call_func_mul_int
1869+
end
18381870
end
18391871

18401872
# Test special purpose inliner for Core.ifelse

0 commit comments

Comments
 (0)