Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit67ec007

Browse files
committed
Eager finalizer insertion
This is a variant of the eager-finalization idea(e.g. as seen in#44056), but with a focus on the mechanismof finalizer insertion, since I need a similar pass downstream.Integration of EscapeAnalysis is left to#44056.My motivation for this change is somewhat different. In particular,I want to be able to insert finalize call such that I cansubsequently SROA the mutable object. This requires a coupledesign points that are more stringent than the pass from#44056,so I decided to prototype them as an independent PR. The primarythings I need here that are not seen in#44056 are:- The ability to forgo finalizer registration with the runtime entirely (requires additional legality analyis)- The ability to inline the registered finalizer at the deallocation point (to enable subsequent SROA)To this end, adding a finalizer is promoted to a builtinthat is recognized by inference and inlining (such that inferencecan produce an inferred version of the finalizer for inlining).The current status is that this fixes the minimal example I wantedto have work, but does not yet extend to the motivating case I had.Nevertheless, I felt that this was a good checkpoint to synchronizewith other efforts along these lines.Currently working demo:```julia> const total_deallocations = Ref{Int}(0)Base.RefValue{Int64}(0)julia> mutable struct DoAlloc function DoAlloc() this = new() Core._add_finalizer(this, function(this) global total_deallocations[] += 1 end) return this end endjulia> function foo() for i = 1:1000 DoAlloc() end endfoo (generic function with 1 method)julia> @code_llvm foo(); @ REPL[3]:1 within `foo`define void @julia_foo_111() #0 {top: %.promoted = load i64, i64* inttoptr (i64 140370001753968 to i64*), align 16; @ REPL[3]:2 within `foo` %0 = add i64 %.promoted, 1000; @ REPL[3] within `foo` store i64 %0, i64* inttoptr (i64 140370001753968 to i64*), align 16; @ REPL[3]:4 within `foo` ret void}```
1 parent37dd084 commit67ec007

File tree

17 files changed

+447
-99
lines changed

17 files changed

+447
-99
lines changed

‎base/compiler/abstractinterpretation.jl

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,6 +1589,16 @@ function invoke_rewrite(xs::Vector{Any})
15891589
return newxs
15901590
end
15911591

1592+
functionabstract_add_finalizer(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::InferenceState)
1593+
iflength(argtypes)==3
1594+
tt= argtypes[3]
1595+
finalizer_argvec= Any[argtypes[3], argtypes[2]]
1596+
call=abstract_call(interp,ArgInfo(nothing, finalizer_argvec), sv,1)
1597+
returnCallMeta(Nothing,Effects(),FinalizerInfo(call.info, call.effects))
1598+
end
1599+
returnCallMeta(Nothing,Effects(),false)
1600+
end
1601+
15921602
# call where the function is known exactly
15931603
functionabstract_call_known(interp::AbstractInterpreter,@nospecialize(f),
15941604
arginfo::ArgInfo, sv::InferenceState,
@@ -1603,6 +1613,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
16031613
returnabstract_invoke(interp, arginfo, sv)
16041614
elseif f=== modifyfield!
16051615
returnabstract_modifyfield!(interp, argtypes, sv)
1616+
elseif f=== Core._add_finalizer
1617+
returnabstract_add_finalizer(interp, argtypes, sv)
16061618
end
16071619
rt=abstract_call_builtin(interp, f, arginfo, sv, max_methods)
16081620
returnCallMeta(rt,builtin_effects(f, argtypes, rt),false)
@@ -1998,7 +2010,8 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
19982010
effects.effect_free? ALWAYS_TRUE: TRISTATE_UNKNOWN,
19992011
effects.nothrow? ALWAYS_TRUE: TRISTATE_UNKNOWN,
20002012
effects.terminates_globally? ALWAYS_TRUE: TRISTATE_UNKNOWN,
2001-
#=nonoverlayed=#true
2013+
#=nonoverlayed=#true,
2014+
TRISTATE_UNKNOWN
20022015
))
20032016
else
20042017
tristate_merge!(sv, EFFECTS_UNKNOWN)
@@ -2089,6 +2102,19 @@ function abstract_eval_global(M::Module, s::Symbol, frame::InferenceState)
20892102
return ty
20902103
end
20912104

2105+
functionabstract_eval_global_assignment(interp::AbstractInterpreter, frame::InferenceState, lhs::GlobalRef,@nospecialize(rhs))
2106+
M= lhs.mod
2107+
s= lhs.name
2108+
nothrow=false
2109+
ifisdefined(M, s)&&!isconst(M, s)
2110+
ty=ccall(:jl_binding_type, Any, (Any, Any), M, s)
2111+
nothrow= ty===nothing|| rhs ty
2112+
end
2113+
tristate_merge!(frame,Effects(EFFECTS_TOTAL,
2114+
effect_free=TRISTATE_UNKNOWN,
2115+
nothrow=nothrow? ALWAYS_TRUE: TRISTATE_UNKNOWN))
2116+
end
2117+
20922118
abstract_eval_ssavalue(s::SSAValue, sv::InferenceState)=abstract_eval_ssavalue(s, sv.src)
20932119
functionabstract_eval_ssavalue(s::SSAValue, src::CodeInfo)
20942120
typ= (src.ssavaluetypes::Vector{Any})[s.id]
@@ -2321,9 +2347,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
23212347
ifisa(lhs, SlotNumber)
23222348
changes=StateUpdate(lhs,VarState(t,false), changes,false)
23232349
elseifisa(lhs, GlobalRef)
2324-
tristate_merge!(frame,Effects(EFFECTS_TOTAL,
2325-
effect_free=TRISTATE_UNKNOWN,
2326-
nothrow=TRISTATE_UNKNOWN))
2350+
abstract_eval_global_assignment(interp, frame, lhs, t)
23272351
elseif!isa(lhs, SSAValue)
23282352
tristate_merge!(frame, EFFECTS_UNKNOWN)
23292353
end

‎base/compiler/optimize.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ const IR_FLAG_THROW_BLOCK = 0x01 << 3
2727
# This statement may be removed if its result is unused. In particular it must
2828
# thus be both pure and effect free.
2929
const IR_FLAG_EFFECT_FREE=0x01<<4
30+
# This statement was proven not to throw
31+
const IR_FLAG_NOTHROW=0x01<<5
32+
3033

3134
const TOP_TUPLE=GlobalRef(Core,:tuple)
3235

@@ -542,7 +545,7 @@ function run_passes(ci::CodeInfo, sv::OptimizationState, caller::InferenceResult
542545
@timeit"Inlining" ir=ssa_inlining_pass!(ir, ir.linetable, sv.inlining, ci.propagate_inbounds)
543546
# @timeit "verify 2" verify_ir(ir)
544547
@timeit"compact 2" ir=compact!(ir)
545-
@timeit"SROA" ir=sroa_pass!(ir)
548+
@timeit"SROA" ir=sroa_pass!(ir, sv.inlining)
546549
@timeit"ADCE" ir=adce_pass!(ir)
547550
@timeit"type lift" ir=type_lift_pass!(ir)
548551
@timeit"compact 3" ir=compact!(ir)

‎base/compiler/ssair/inlining.jl

Lines changed: 100 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -306,21 +306,17 @@ function finish_cfg_inline!(state::CFGInliningState)
306306
end
307307
end
308308

309-
functionir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any},
310-
linetable::Vector{LineInfoNode}, item::InliningTodo,
311-
boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}})
312-
# Ok, do the inlining here
313-
spec= item.spec::ResolvedInliningSpec
314-
sparam_vals= item.mi.sparam_vals
315-
def= item.mi.def::Method
309+
functionir_inline_linetable!(linetable::Vector{LineInfoNode}, inlinee_ir::IRCode,
310+
inlinee::Method,
311+
inlined_at::Int32)
312+
coverage=coverage_enabled(inlinee.module)
316313
linetable_offset::Int32=length(linetable)
317314
# Append the linetable of the inlined function to our line table
318-
inlined_at= compact.result[idx][:line]
319315
topline::Int32= linetable_offset+Int32(1)
320-
coverage=coverage_enabled(def.module)
321316
coverage_by_path=JLOptions().code_coverage==3
322-
push!(linetable,LineInfoNode(def.module, def.name, def.file, def.line, inlined_at))
323-
oldlinetable= spec.ir.linetable
317+
push!(linetable,LineInfoNode(inlinee.module, inlinee.name, inlinee.file, inlinee.line, inlined_at))
318+
oldlinetable= inlinee_ir.linetable
319+
extra_coverage_line=0
324320
for oldlinein1:length(oldlinetable)
325321
entry= oldlinetable[oldline]
326322
if!coverage&& coverage_by_path&&is_file_tracked(entry.file)
@@ -339,8 +335,25 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector
339335
end
340336
push!(linetable, newentry)
341337
end
342-
if coverage&& spec.ir.stmts[1][:line]+ linetable_offset!= topline
343-
insert_node_here!(compact,NewInstruction(Expr(:code_coverage_effect), Nothing, topline))
338+
if coverage&& inlinee_ir.stmts[1][:line]+ linetable_offset!= topline
339+
extra_coverage_line= topline
340+
end
341+
return linetable_offset, extra_coverage_line
342+
end
343+
344+
functionir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any},
345+
linetable::Vector{LineInfoNode}, item::InliningTodo,
346+
boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}})
347+
# Ok, do the inlining here
348+
spec= item.spec::ResolvedInliningSpec
349+
sparam_vals= item.mi.sparam_vals
350+
def= item.mi.def::Method
351+
inlined_at= compact.result[idx][:line]
352+
linetable_offset::Int32=length(linetable)
353+
topline::Int32= linetable_offset+Int32(1)
354+
linetable_offset, extra_coverage_line=ir_inline_linetable!(linetable, item.spec.ir, def, inlined_at)
355+
if extra_coverage_line!=0
356+
insert_node_here!(compact,NewInstruction(Expr(:code_coverage_effect), Nothing, extra_coverage_line))
344357
end
345358
if def.isva
346359
nargs_def=Int(def.nargs::Int32)
@@ -847,12 +860,8 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
847860
returncompileable_specialization(et, match, effects)
848861
end
849862

850-
ifisa(src, IRCode)
851-
src=copy(src)
852-
end
853-
854863
et!==nothing&&push!(et, mi)
855-
returnInliningTodo(mi, src, effects)
864+
returnInliningTodo(mi,retrieve_ir_for_inlining(mi,src), effects)
856865
end
857866

858867
functionresolve_todo((; fully_covered, atype, cases,#=bbs=#)::UnionSplit, state::InliningState, flag::UInt8)
@@ -874,7 +883,8 @@ function validate_sparams(sparams::SimpleVector)
874883
end
875884

876885
functionanalyze_method!(match::MethodMatch, argtypes::Vector{Any},
877-
flag::UInt8, state::InliningState)
886+
flag::UInt8, state::InliningState,
887+
do_resolve::Bool=true)
878888
method= match.method
879889
spec_types= match.spec_types
880890

@@ -908,23 +918,23 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any},
908918
todo=InliningTodo(mi, match, argtypes)
909919
# If we don't have caches here, delay resolving this MethodInstance
910920
# until the batch inlining step (or an external post-processing pass)
911-
state.mi_cache===nothing&&return todo
921+
do_resolve&&state.mi_cache===nothing&&return todo
912922
returnresolve_todo(todo, state, flag)
913923
end
914924

915925
functionInliningTodo(mi::MethodInstance, ir::IRCode, effects::Effects)
916926
returnInliningTodo(mi,ResolvedInliningSpec(ir,linear_inline_eligible(ir), effects))
917927
end
918928

919-
functionInliningTodo(mi::MethodInstance, src::Union{CodeInfo,Array{UInt8, 1}}, effects::Effects)
920-
if!isa(src, CodeInfo)
921-
src=ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def,C_NULL,src::Vector{UInt8})::CodeInfo
922-
end
929+
functionretrieve_ir_for_inlining(mi::MethodInstance, src::Array{UInt8, 1})
930+
src=ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def,C_NULL, src::Vector{UInt8})::CodeInfo
931+
returnretrieve_ir_for_inlining(mi,src)
932+
end
923933

924-
@timeit"inline IR inflation"begin;
925-
returnInliningTodo(mi,inflate_ir(src, mi)::IRCode, effects)
926-
end
934+
retrieve_ir_for_inlining(mi::MethodInstance, src::CodeInfo)=@timeit"inline IR inflation"begin;
935+
inflate_ir(src, mi)::IRCode
927936
end
937+
retrieve_ir_for_inlining(mi::MethodInstance, ir::IRCode)=copy(ir)
928938

929939
functionhandle_single_case!(
930940
ir::IRCode, idx::Int, stmt::Expr,
@@ -1206,7 +1216,7 @@ function process_simple!(ir::IRCode, idx::Int, state::InliningState, todo::Vecto
12061216
end
12071217
end
12081218

1209-
if sig.f!== Core.invoke&&is_builtin(sig)
1219+
if sig.f!== Core.invoke&&sig.f!== Core._add_finalizer&&is_builtin(sig)
12101220
# No inlining for builtins (other invoke/apply/typeassert)
12111221
returnnothing
12121222
end
@@ -1223,9 +1233,10 @@ function process_simple!(ir::IRCode, idx::Int, state::InliningState, todo::Vecto
12231233
end
12241234

12251235
#TODO inline non-`isdispatchtuple`, union-split callsites?
1226-
functionanalyze_single_call!(
1227-
ir::IRCode, idx::Int, stmt::Expr, infos::Vector{MethodMatchInfo}, flag::UInt8,
1228-
sig::Signature, state::InliningState, todo::Vector{Pair{Int, Any}})
1236+
functioncompute_inlining_cases(
1237+
infos::Vector{MethodMatchInfo}, flag::UInt8,
1238+
sig::Signature, state::InliningState,
1239+
do_resolve::Bool=true)
12291240
argtypes= sig.argtypes
12301241
cases= InliningCase[]
12311242
local any_fully_covered=false
@@ -1242,7 +1253,7 @@ function analyze_single_call!(
12421253
continue
12431254
end
12441255
for matchin meth
1245-
handled_all_cases&=handle_match!(match, argtypes, flag, state, cases,true)
1256+
handled_all_cases&=handle_match!(match, argtypes, flag, state, cases,true, do_resolve)
12461257
any_fully_covered|= match.fully_covers
12471258
end
12481259
end
@@ -1252,8 +1263,18 @@ function analyze_single_call!(
12521263
filter!(case::InliningCase->isdispatchtuple(case.sig), cases)
12531264
end
12541265

1255-
handle_cases!(ir, idx, stmt,argtypes_to_type(argtypes), cases,
1256-
handled_all_cases& any_fully_covered, todo, state.params)
1266+
return cases, handled_all_cases& any_fully_covered
1267+
end
1268+
1269+
functionanalyze_single_call!(
1270+
ir::IRCode, idx::Int, stmt::Expr, infos::Vector{MethodMatchInfo}, flag::UInt8,
1271+
sig::Signature, state::InliningState, todo::Vector{Pair{Int, Any}})
1272+
1273+
r=compute_inlining_cases(infos, flag, sig, state)
1274+
r===nothing&&returnnothing
1275+
cases, all_covered= r
1276+
handle_cases!(ir, idx, stmt,argtypes_to_type(sig.argtypes), cases,
1277+
all_covered, todo, state.params)
12571278
end
12581279

12591280
# similar to `analyze_single_call!`, but with constant results
@@ -1305,14 +1326,15 @@ end
13051326

13061327
functionhandle_match!(
13071328
match::MethodMatch, argtypes::Vector{Any}, flag::UInt8, state::InliningState,
1308-
cases::Vector{InliningCase}, allow_abstract::Bool=false)
1329+
cases::Vector{InliningCase}, allow_abstract::Bool=false,
1330+
do_resolve::Bool=true)
13091331
spec_types= match.spec_types
13101332
allow_abstract||isdispatchtuple(spec_types)||returnfalse
13111333
# we may see duplicated dispatch signatures here when a signature gets widened
13121334
# during abstract interpretation: for the purpose of inlining, we can just skip
13131335
# processing this dispatch candidate
13141336
_any(case->case.sig=== spec_types, cases)&&returntrue
1315-
item=analyze_method!(match, argtypes, flag, state)
1337+
item=analyze_method!(match, argtypes, flag, state, do_resolve)
13161338
item===nothing&&returnfalse
13171339
push!(cases,InliningCase(spec_types, item))
13181340
returntrue
@@ -1427,6 +1449,48 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
14271449
continue
14281450
end
14291451

1452+
# Handle finalizer
1453+
if sig.f=== Core._add_finalizer
1454+
ifisa(info, FinalizerInfo)
1455+
# Only inline finalizers that are known nothrow and notls.
1456+
# This avoids having to set up state for finalizer isolation
1457+
(is_nothrow(info.effects)&&is_notls(info.effects))||continue
1458+
1459+
info= info.info
1460+
ifisa(info, MethodMatchInfo)
1461+
infos= MethodMatchInfo[info]
1462+
elseifisa(info, UnionSplitInfo)
1463+
infos= info.matches
1464+
else
1465+
continue
1466+
end
1467+
1468+
ft=argextype(stmt.args[3], ir)
1469+
has_free_typevars(ft)&&returnnothing
1470+
f=singleton_type(ft)
1471+
argtypes=Vector{Any}(undef,2)
1472+
argtypes[1]= ft
1473+
argtypes[2]=argextype(stmt.args[2], ir)
1474+
sig=Signature(f, ft, argtypes)
1475+
1476+
cases, all_covered=compute_inlining_cases(infos,UInt8(0), sig, state,false)
1477+
length(cases)==0&&continue
1478+
if all_covered&&length(cases)==1
1479+
ifisa(cases[1], InliningCase)
1480+
case1= cases[1].item
1481+
ifisa(case1, InliningTodo)
1482+
push!(stmt.args,true)
1483+
push!(stmt.args, case1.mi)
1484+
elseifisa(case1, InvokeCase)
1485+
push!(stmt.args,false)
1486+
push!(stmt.args, case1.invoke)
1487+
end
1488+
end
1489+
end
1490+
continue
1491+
end
1492+
end
1493+
14301494
# if inference arrived here with constant-prop'ed result(s),
14311495
# we can perform a specialized analysis for just this case
14321496
ifisa(info, ConstCallInfo)

‎base/compiler/ssair/ir.jl

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -166,36 +166,6 @@ const AnySSAValue = Union{SSAValue, OldSSAValue, NewSSAValue}
166166

167167

168168
# SSA-indexed nodes
169-
170-
struct NewInstruction
171-
stmt::Any
172-
type::Any
173-
info::Any
174-
# If nothing, copy the line from previous statement
175-
# in the insertion location
176-
line::Union{Int32, Nothing}
177-
flag::UInt8
178-
179-
## Insertion options
180-
181-
# The IR_FLAG_EFFECT_FREE flag has already been computed (or forced).
182-
# Don't bother redoing so on insertion.
183-
effect_free_computed::Bool
184-
NewInstruction(@nospecialize(stmt),@nospecialize(type),@nospecialize(info),
185-
line::Union{Int32, Nothing}, flag::UInt8, effect_free_computed::Bool)=
186-
new(stmt, type, info, line, flag, effect_free_computed)
187-
end
188-
NewInstruction(@nospecialize(stmt),@nospecialize(type))=
189-
NewInstruction(stmt, type,nothing)
190-
NewInstruction(@nospecialize(stmt),@nospecialize(type), line::Union{Nothing, Int32})=
191-
NewInstruction(stmt, type,nothing, line, IR_FLAG_NULL,false)
192-
193-
effect_free(inst::NewInstruction)=
194-
NewInstruction(inst.stmt, inst.type, inst.info, inst.line, inst.flag| IR_FLAG_EFFECT_FREE,true)
195-
non_effect_free(inst::NewInstruction)=
196-
NewInstruction(inst.stmt, inst.type, inst.info, inst.line, inst.flag&~IR_FLAG_EFFECT_FREE,true)
197-
198-
199169
struct InstructionStream
200170
inst::Vector{Any}
201171
type::Vector{Any}
@@ -295,6 +265,36 @@ function add!(new::NewNodeStream, pos::Int, attach_after::Bool)
295265
end
296266
copy(nns::NewNodeStream)=NewNodeStream(copy(nns.stmts),copy(nns.info))
297267

268+
struct NewInstruction
269+
stmt::Any
270+
type::Any
271+
info::Any
272+
# If nothing, copy the line from previous statement
273+
# in the insertion location
274+
line::Union{Int32, Nothing}
275+
flag::UInt8
276+
277+
## Insertion options
278+
279+
# The IR_FLAG_EFFECT_FREE flag has already been computed (or forced).
280+
# Don't bother redoing so on insertion.
281+
effect_free_computed::Bool
282+
NewInstruction(@nospecialize(stmt),@nospecialize(type),@nospecialize(info),
283+
line::Union{Int32, Nothing}, flag::UInt8, effect_free_computed::Bool)=
284+
new(stmt, type, info, line, flag, effect_free_computed)
285+
end
286+
NewInstruction(@nospecialize(stmt),@nospecialize(type))=
287+
NewInstruction(stmt, type,nothing)
288+
NewInstruction(@nospecialize(stmt),@nospecialize(type), line::Union{Nothing, Int32})=
289+
NewInstruction(stmt, type,nothing, line, IR_FLAG_NULL,false)
290+
NewInstruction(@nospecialize(stmt), meta::Instruction; line::Union{Int32, Nothing}=nothing)=
291+
NewInstruction(stmt, meta[:type], meta[:info], line===nothing? meta[:line]: line, meta[:flag],true)
292+
293+
effect_free(inst::NewInstruction)=
294+
NewInstruction(inst.stmt, inst.type, inst.info, inst.line, inst.flag| IR_FLAG_EFFECT_FREE,true)
295+
non_effect_free(inst::NewInstruction)=
296+
NewInstruction(inst.stmt, inst.type, inst.info, inst.line, inst.flag&~IR_FLAG_EFFECT_FREE,true)
297+
298298
struct IRCode
299299
stmts::InstructionStream
300300
argtypes::Vector{Any}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp