Skip to content

Sink CodeInfo transformation into transform_result_for_cache, continued #57375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
3d708e3
Sink CodeInfo transformation into transform_result_for_cache
serenity4 Feb 12, 2025
2c7642d
Merge branch 'master' of github.com:JuliaLang/julia into sinktransform
serenity4 Feb 13, 2025
cef3041
Use CodeInfo to determine inlining policy
serenity4 Feb 13, 2025
1dd0ad5
Convert IRCode to CodeInfo for IR retrieval for inlining
serenity4 Feb 14, 2025
c3f9168
Merge branch 'master' of github.com:JuliaLang/julia into sinktransform
serenity4 Feb 14, 2025
4b7f7dd
Run `cfg_simplify!` if source CodeInfo is inlineable
serenity4 Feb 17, 2025
10225d9
Merge branch 'master' of github.com:JuliaLang/julia into sinktransform
serenity4 Feb 17, 2025
79a8eed
Merge branch 'master' of github.com:JuliaLang/julia into sinktransform
serenity4 Feb 18, 2025
b6bf593
Fix phi node renaming for deleted forward edges
serenity4 Feb 19, 2025
f1d9392
Merge branch 'master' of github.com:JuliaLang/julia into sinktransform
serenity4 Feb 19, 2025
6c69af0
Fix doctest
serenity4 Feb 19, 2025
837a9dc
Consider previous instructions from the same block as processed
serenity4 Feb 28, 2025
3b7e491
Merge branch 'master' of github.com:JuliaLang/julia into sinktransform
serenity4 Feb 28, 2025
1cad9da
Defer CFG simplification to IR retrieval for inlining
serenity4 Feb 28, 2025
d1a80cd
Revert doctest change
serenity4 Mar 2, 2025
03ad9c0
Defer computation of new phi node values to when it is used
serenity4 Mar 3, 2025
e0da4b7
Merge branch 'master' of github.com:JuliaLang/julia into sinktransform
serenity4 Mar 3, 2025
ff40eb2
Fixes
serenity4 Mar 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Compiler/extras/CompilerDevTools/src/CompilerDevTools.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ end

function Compiler.optimize(interp::SplitCacheInterp, opt::Compiler.OptimizationState, caller::Compiler.InferenceResult)
@invoke Compiler.optimize(interp::Compiler.AbstractInterpreter, opt::Compiler.OptimizationState, caller::Compiler.InferenceResult)
ir = opt.ir::Compiler.IRCode
ir = opt.result.ir::Compiler.IRCode
override = GlobalRef(@__MODULE__(), :with_new_compiler)
for inst in ir.stmts
stmt = inst[:stmt]
Expand Down
26 changes: 20 additions & 6 deletions Compiler/src/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ is_declared_noinline(@nospecialize src::MaybeCompressed) =
# return whether this src should be inlined. If so, retrieve_ir_for_inlining must return an IRCode from it
function src_inlining_policy(interp::AbstractInterpreter,
@nospecialize(src), @nospecialize(info::CallInfo), stmt_flag::UInt32)
isa(src, OptimizationState) && (src = src.src)
if isa(src, MaybeCompressed)
src_inlineable = is_stmt_inline(stmt_flag) || is_inlineable(src)
return src_inlineable
Expand All @@ -154,10 +155,20 @@ end
# get `code_cache(::AbstractInterpreter)` from `state::InliningState`
code_cache(state::InliningState) = WorldView(code_cache(state.interp), state.world)

mutable struct OptimizationResult
ir::IRCode
simplified::Bool # indicates whether the IR was processed with `cfg_simplify!`
end

function simplify_ir!(result::OptimizationResult)
result.ir = cfg_simplify!(result.ir)
result.simplified = true
end

mutable struct OptimizationState{Interp<:AbstractInterpreter}
linfo::MethodInstance
src::CodeInfo
ir::Union{Nothing, IRCode}
result::Union{Nothing, OptimizationResult}
stmt_info::Vector{CallInfo}
mod::Module
sptypes::Vector{VarState}
Expand Down Expand Up @@ -226,10 +237,13 @@ include("ssair/passes.jl")
include("ssair/irinterp.jl")

function ir_to_codeinf!(opt::OptimizationState)
(; linfo, src) = opt
src = ir_to_codeinf!(src, opt.ir::IRCode)
src.edges = Core.svec(opt.inlining.edges...)
opt.ir = nothing
(; linfo, src, result) = opt
if result === nothing
return src
end
src = ir_to_codeinf!(src, result.ir)
opt.result = nothing
opt.src = src
maybe_validate_code(linfo, src, "optimized")
return src
end
Expand Down Expand Up @@ -489,7 +503,7 @@ function finish(interp::AbstractInterpreter, opt::OptimizationState,
@assert !(result isa LimitedAccuracy)
result = widenslotwrapper(result)

opt.ir = ir
opt.result = OptimizationResult(ir, false)

# determine and cache inlineability
if !force_noinline
Expand Down
8 changes: 8 additions & 0 deletions Compiler/src/ssair/inlining.jl
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,14 @@ function retrieve_ir_for_inlining(mi::MethodInstance, ir::IRCode, preserve_local
ir.debuginfo.def = mi
return ir, spec_info, DebugInfo(ir.debuginfo, length(ir.stmts))
end
function retrieve_ir_for_inlining(mi::MethodInstance, opt::OptimizationState, preserve_local_sources::Bool)
result = opt.result
if result !== nothing
!result.simplified && simplify_ir!(result)
return retrieve_ir_for_inlining(mi, result.ir, preserve_local_sources)
end
retrieve_ir_for_inlining(mi, opt.src, preserve_local_sources)
end
Comment on lines +978 to +985
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this new retrieve_ir_for_inlining (and the new OptimizationState handler of src_inlining_policy) tested somewhere? IIUC any test that would hit this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, because transform_result_for_cache in finish! is only applied to result that will be cached (due to isdefined(result, :ci)), inference local results and will keep the OptimizationState as is. So these new inlining code should be actually hit during the native compilation.


function handle_single_case!(todo::Vector{Pair{Int,Any}},
ir::IRCode, idx::Int, stmt::Expr, @nospecialize(case),
Expand Down
61 changes: 35 additions & 26 deletions Compiler/src/ssair/ir.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1265,39 +1265,48 @@ function process_phinode_values(old_values::Vector{Any}, late_fixup::Vector{Int}
values = Vector{Any}(undef, length(old_values))
for i = 1:length(old_values)
isassigned(old_values, i) || continue
val = old_values[i]
if isa(val, SSAValue)
if do_rename_ssa
if !already_inserted(i, OldSSAValue(val.id))
push!(late_fixup, result_idx)
val = OldSSAValue(val.id)
else
val = renumber_ssa2(val, ssa_rename, used_ssas, new_new_used_ssas, do_rename_ssa, mark_refined!)
end
else
used_ssas[val.id] += 1
end
elseif isa(val, OldSSAValue)
if !already_inserted(i, val)
values[i] = process_phinode_value(old_values, i, late_fixup, already_inserted, result_idx, ssa_rename, used_ssas, new_new_used_ssas, do_rename_ssa, mark_refined!)
end
return values
end

function process_phinode_value(old_values::Vector{Any}, i::Int, late_fixup::Vector{Int},
already_inserted, result_idx::Int,
ssa_rename::Vector{Any}, used_ssas::Vector{Int},
new_new_used_ssas::Vector{Int},
do_rename_ssa::Bool,
mark_refined!::Union{Refiner, Nothing})
val = old_values[i]
if isa(val, SSAValue)
if do_rename_ssa
if !already_inserted(i, OldSSAValue(val.id))
push!(late_fixup, result_idx)
val = OldSSAValue(val.id)
else
# Always renumber these. do_rename_ssa applies only to actual SSAValues
val = renumber_ssa2(SSAValue(val.id), ssa_rename, used_ssas, new_new_used_ssas, true, mark_refined!)
end
elseif isa(val, NewSSAValue)
if val.id < 0
new_new_used_ssas[-val.id] += 1
else
@assert do_rename_ssa
val = SSAValue(val.id)
val = renumber_ssa2(val, ssa_rename, used_ssas, new_new_used_ssas, do_rename_ssa, mark_refined!)
end
else
used_ssas[val.id] += 1
end
if isa(val, NewSSAValue)
elseif isa(val, OldSSAValue)
if !already_inserted(i, val)
push!(late_fixup, result_idx)
else
# Always renumber these. do_rename_ssa applies only to actual SSAValues
val = renumber_ssa2(SSAValue(val.id), ssa_rename, used_ssas, new_new_used_ssas, true, mark_refined!)
end
elseif isa(val, NewSSAValue)
if val.id < 0
new_new_used_ssas[-val.id] += 1
else
@assert do_rename_ssa
val = SSAValue(val.id)
end
values[i] = val
end
return values
if isa(val, NewSSAValue)
push!(late_fixup, result_idx)
end
return val
end

function renumber_ssa2(val::SSAValue, ssanums::Vector{Any}, used_ssas::Vector{Int},
Expand Down
39 changes: 19 additions & 20 deletions Compiler/src/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2574,51 +2574,50 @@ function cfg_simplify!(ir::IRCode)
values = phi.values
(; ssa_rename, late_fixup, used_ssas, new_new_used_ssas) = compact
ssa_rename[i] = SSAValue(compact.result_idx)
already_inserted = function (i::Int, val::OldSSAValue)
already_inserted = function (branch::Int, val::OldSSAValue)
if val.id in old_bb_stmts
return val.id <= i
end
return bb_rename_pred[phi.edges[i]] < idx
return 0 < bb_rename_pred[phi.edges[branch]] < idx
end
renamed_values = process_phinode_values(values, late_fixup, already_inserted, compact.result_idx, ssa_rename, used_ssas, new_new_used_ssas, true, nothing)
edges = Int32[]
values = Any[]
sizehint!(edges, length(phi.edges)); sizehint!(values, length(renamed_values))
sizehint!(edges, length(phi.edges)); sizehint!(values, length(phi.values))
for old_index in 1:length(phi.edges)
old_edge = phi.edges[old_index]
new_edge = bb_rename_pred[old_edge]
if new_edge > 0
push!(edges, new_edge)
if isassigned(renamed_values, old_index)
push!(values, renamed_values[old_index])
if isassigned(phi.values, old_index)
val = process_phinode_value(phi.values, old_index, late_fixup, already_inserted, compact.result_idx, ssa_rename, used_ssas, new_new_used_ssas, true, nothing)
push!(values, val)
else
resize!(values, length(values)+1)
end
elseif new_edge == -1
@assert length(phi.edges) == 1
if isassigned(renamed_values, old_index)
if isassigned(phi.values, old_index)
val = process_phinode_value(phi.values, old_index, late_fixup, already_inserted, compact.result_idx, ssa_rename, used_ssas, new_new_used_ssas, true, nothing)
push!(edges, -1)
push!(values, renamed_values[old_index])
push!(values, val)
end
elseif new_edge == -3
# Multiple predecessors, we need to expand out this phi
all_new_preds = Int32[]
add_preds!(all_new_preds, bbs, bb_rename_pred, old_edge)
append!(edges, all_new_preds)
if isassigned(renamed_values, old_index)
val = renamed_values[old_index]
for _ in 1:length(all_new_preds)
push!(values, val)
np = length(all_new_preds)
if np > 0
if isassigned(phi.values, old_index)
val = process_phinode_value(phi.values, old_index, late_fixup, already_inserted, compact.result_idx, ssa_rename, used_ssas, new_new_used_ssas, true, nothing)
for p in 1:np
push!(values, val)
p > 2 && count_added_node!(compact, val)
end
else
resize!(values, length(values)+np)
end
length(all_new_preds) == 0 && kill_current_use!(compact, val)
for _ in 2:length(all_new_preds)
count_added_node!(compact, val)
end
else
resize!(values, length(values)+length(all_new_preds))
end
else
isassigned(renamed_values, old_index) && kill_current_use!(compact, renamed_values[old_index])
end
end
if length(edges) == 0 || (length(edges) == 1 && !isassigned(values, 1))
Expand Down
31 changes: 22 additions & 9 deletions Compiler/src/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -93,19 +93,21 @@ If set to `true`, record per-method-instance timings within type inference in th
__set_measure_typeinf(onoff::Bool) = __measure_typeinf__[] = onoff
const __measure_typeinf__ = RefValue{Bool}(false)

function finish!(interp::AbstractInterpreter, caller::InferenceState, validation_world::UInt)
function result_edges(interp::AbstractInterpreter, caller::InferenceState)
result = caller.result
opt = result.src
if opt isa OptimizationState
src = ir_to_codeinf!(opt)
edges = src.edges::SimpleVector
caller.src = result.src = src
if isa(opt, OptimizationState)
return Core.svec(opt.inlining.edges...)
else
edges = Core.svec(caller.edges...)
caller.src.edges = edges
return Core.svec(caller.edges...)
end
end

function finish!(interp::AbstractInterpreter, caller::InferenceState, validation_world::UInt)
result = caller.result
#@assert last(result.valid_worlds) <= get_world_counter() || isempty(caller.edges)
if isdefined(result, :ci)
edges = result_edges(interp, caller)
ci = result.ci
# if we aren't cached, we don't need this edge
# but our caller might, so let's just make it anyways
Expand All @@ -119,9 +121,10 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState, validation
const_flag = is_result_constabi_eligible(result)
discard_src = caller.cache_mode === CACHE_MODE_NULL || const_flag
if !discard_src
inferred_result = transform_result_for_cache(interp, result)
inferred_result = transform_result_for_cache(interp, result, edges)
# TODO: do we want to augment edges here with any :invoke targets that we got from inlining (such that we didn't have a direct edge to it already)?
if inferred_result isa CodeInfo
result.src = inferred_result
if may_compress(interp)
nslots = length(inferred_result.slotflags)
resize!(inferred_result.slottypes::Vector{Any}, nslots)
Expand Down Expand Up @@ -278,7 +281,16 @@ function is_result_constabi_eligible(result::InferenceResult)
return isa(result_type, Const) && is_foldable_nothrow(result.ipo_effects) && is_inlineable_constant(result_type.val)
end

transform_result_for_cache(::AbstractInterpreter, result::InferenceResult) = result.src
function transform_result_for_cache(::AbstractInterpreter, result::InferenceResult, edges::SimpleVector)
src = result.src
if isa(src, OptimizationState)
src = ir_to_codeinf!(src)
end
if isa(src, CodeInfo)
src.edges = edges
end
return src
end

function maybe_compress_codeinfo(interp::AbstractInterpreter, mi::MethodInstance, ci::CodeInfo)
def = mi.def
Expand Down Expand Up @@ -1064,6 +1076,7 @@ function typeinf_frame(interp::AbstractInterpreter, mi::MethodInstance, run_opti
opt = OptimizationState(frame, interp)
optimize(interp, opt, frame.result)
src = ir_to_codeinf!(opt)
src.edges = Core.svec(opt.inlining.edges...)
end
result.src = frame.src = src
end
Expand Down
6 changes: 3 additions & 3 deletions Compiler/test/AbstractInterpreter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Compiler.may_optimize(::AbsIntOnlyInterp1) = false
# it should work even if the interpreter discards inferred source entirely
@newinterp AbsIntOnlyInterp2
Compiler.may_optimize(::AbsIntOnlyInterp2) = false
Compiler.transform_result_for_cache(::AbsIntOnlyInterp2, ::Compiler.InferenceResult) = nothing
Compiler.transform_result_for_cache(::AbsIntOnlyInterp2, ::Compiler.InferenceResult, edges::Core.SimpleVector) = nothing
@test Base.infer_return_type(Base.init_stdio, (Ptr{Cvoid},); interp=AbsIntOnlyInterp2()) >: IO

# OverlayMethodTable
Expand Down Expand Up @@ -493,9 +493,9 @@ struct CustomData
inferred
CustomData(@nospecialize inferred) = new(inferred)
end
function Compiler.transform_result_for_cache(interp::CustomDataInterp, result::Compiler.InferenceResult)
function Compiler.transform_result_for_cache(interp::CustomDataInterp, result::Compiler.InferenceResult, edges::Core.SimpleVector)
inferred_result = @invoke Compiler.transform_result_for_cache(
interp::Compiler.AbstractInterpreter, result::Compiler.InferenceResult)
interp::Compiler.AbstractInterpreter, result::Compiler.InferenceResult, edges::Core.SimpleVector)
return CustomData(inferred_result)
end
function Compiler.src_inlining_policy(interp::CustomDataInterp, @nospecialize(src),
Expand Down
Loading