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

Commitda04e84

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 parent6f8860c commitda04e84

File tree

9 files changed

+382
-78
lines changed

9 files changed

+382
-78
lines changed

‎base/compiler/abstractinterpretation.jl

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

1592+
functionabstract_finalizer(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::InferenceState)
1593+
iflength(argtypes)==3
1594+
finalizer_argvec= Any[argtypes[2], argtypes[3]]
1595+
call=abstract_call(interp,ArgInfo(nothing, finalizer_argvec), sv,1)
1596+
returnCallMeta(Nothing,Effects(),FinalizerInfo(call.info, call.effects))
1597+
end
1598+
returnCallMeta(Nothing,Effects(),false)
1599+
end
1600+
15921601
# call where the function is known exactly
15931602
functionabstract_call_known(interp::AbstractInterpreter,@nospecialize(f),
15941603
arginfo::ArgInfo, sv::InferenceState,
@@ -1603,6 +1612,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
16031612
returnabstract_invoke(interp, arginfo, sv)
16041613
elseif f=== modifyfield!
16051614
returnabstract_modifyfield!(interp, argtypes, sv)
1615+
elseif f=== Core.finalizer
1616+
returnabstract_finalizer(interp, argtypes, sv)
16061617
end
16071618
rt=abstract_call_builtin(interp, f, arginfo, sv, max_methods)
16081619
returnCallMeta(rt,builtin_effects(f, argtypes, rt),false)

‎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

@@ -564,7 +567,7 @@ function run_passes(
564567
@pass"Inlining" ir=ssa_inlining_pass!(ir, ir.linetable, sv.inlining, ci.propagate_inbounds)
565568
# @timeit "verify 2" verify_ir(ir)
566569
@pass"compact 2" ir=compact!(ir)
567-
@pass"SROA" ir=sroa_pass!(ir)
570+
@pass"SROA" ir=sroa_pass!(ir, sv.inlining)
568571
@pass"ADCE" ir=adce_pass!(ir)
569572
@pass"type lift" ir=type_lift_pass!(ir)
570573
@pass"compact 3" ir=compact!(ir)

‎base/compiler/ssair/inlining.jl

Lines changed: 98 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -308,21 +308,17 @@ function finish_cfg_inline!(state::CFGInliningState)
308308
end
309309
end
310310

311-
functionir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any},
312-
linetable::Vector{LineInfoNode}, item::InliningTodo,
313-
boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}})
314-
# Ok, do the inlining here
315-
spec= item.spec::ResolvedInliningSpec
316-
sparam_vals= item.mi.sparam_vals
317-
def= item.mi.def::Method
311+
functionir_inline_linetable!(linetable::Vector{LineInfoNode}, inlinee_ir::IRCode,
312+
inlinee::Method,
313+
inlined_at::Int32)
314+
coverage=coverage_enabled(inlinee.module)
318315
linetable_offset::Int32=length(linetable)
319316
# Append the linetable of the inlined function to our line table
320-
inlined_at= compact.result[idx][:line]
321317
topline::Int32= linetable_offset+Int32(1)
322-
coverage=coverage_enabled(def.module)
323318
coverage_by_path=JLOptions().code_coverage==3
324-
push!(linetable,LineInfoNode(def.module, def.name, def.file, def.line, inlined_at))
325-
oldlinetable= spec.ir.linetable
319+
push!(linetable,LineInfoNode(inlinee.module, inlinee.name, inlinee.file, inlinee.line, inlined_at))
320+
oldlinetable= inlinee_ir.linetable
321+
extra_coverage_line=0
326322
for oldlinein1:length(oldlinetable)
327323
entry= oldlinetable[oldline]
328324
if!coverage&& coverage_by_path&&is_file_tracked(entry.file)
@@ -341,8 +337,25 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector
341337
end
342338
push!(linetable, newentry)
343339
end
344-
if coverage&& spec.ir.stmts[1][:line]+ linetable_offset!= topline
345-
insert_node_here!(compact,NewInstruction(Expr(:code_coverage_effect), Nothing, topline))
340+
if coverage&& inlinee_ir.stmts[1][:line]+ linetable_offset!= topline
341+
extra_coverage_line= topline
342+
end
343+
return linetable_offset, extra_coverage_line
344+
end
345+
346+
functionir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector{Any},
347+
linetable::Vector{LineInfoNode}, item::InliningTodo,
348+
boundscheck::Symbol, todo_bbs::Vector{Tuple{Int, Int}})
349+
# Ok, do the inlining here
350+
spec= item.spec::ResolvedInliningSpec
351+
sparam_vals= item.mi.sparam_vals
352+
def= item.mi.def::Method
353+
inlined_at= compact.result[idx][:line]
354+
linetable_offset::Int32=length(linetable)
355+
topline::Int32= linetable_offset+Int32(1)
356+
linetable_offset, extra_coverage_line=ir_inline_linetable!(linetable, item.spec.ir, def, inlined_at)
357+
if extra_coverage_line!=0
358+
insert_node_here!(compact,NewInstruction(Expr(:code_coverage_effect), Nothing, extra_coverage_line))
346359
end
347360
if def.isva
348361
nargs_def=Int(def.nargs::Int32)
@@ -839,7 +852,7 @@ function resolve_todo(todo::InliningTodo, state::InliningState, flag::UInt8)
839852
src===nothing&&returncompileable_specialization(et, match, effects)
840853

841854
et!==nothing&&push!(et, mi)
842-
returnInliningTodo(mi, src, effects)
855+
returnInliningTodo(mi,retrieve_ir_for_inlining(mi,src), effects)
843856
end
844857

845858
functionresolve_todo((; fully_covered, atype, cases,#=bbs=#)::UnionSplit, state::InliningState, flag::UInt8)
@@ -861,7 +874,8 @@ function validate_sparams(sparams::SimpleVector)
861874
end
862875

863876
functionanalyze_method!(match::MethodMatch, argtypes::Vector{Any},
864-
flag::UInt8, state::InliningState)
877+
flag::UInt8, state::InliningState,
878+
do_resolve::Bool=true)
865879
method= match.method
866880
spec_types= match.spec_types
867881

@@ -895,7 +909,7 @@ function analyze_method!(match::MethodMatch, argtypes::Vector{Any},
895909
todo=InliningTodo(mi, match, argtypes)
896910
# If we don't have caches here, delay resolving this MethodInstance
897911
# until the batch inlining step (or an external post-processing pass)
898-
state.mi_cache===nothing&&return todo
912+
do_resolve&&state.mi_cache===nothing&&return todo
899913
returnresolve_todo(todo, state, flag)
900914
end
901915

@@ -904,17 +918,12 @@ function InliningTodo(mi::MethodInstance, ir::IRCode, effects::Effects)
904918
returnInliningTodo(mi,ResolvedInliningSpec(ir, effects))
905919
end
906920

907-
functionInliningTodo(mi::MethodInstance, src::Union{CodeInfo, Vector{UInt8}}, effects::Effects)
908-
if!isa(src, CodeInfo)
909-
src=ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def,C_NULL, src::Vector{UInt8})::CodeInfo
910-
else
911-
src=copy(src)
912-
end
913-
@timeit"inline IR inflation"begin
914-
ir=inflate_ir!(src, mi)::IRCode
915-
returnInliningTodo(mi,ResolvedInliningSpec(ir, effects))
916-
end
921+
functionretrieve_ir_for_inlining(mi::MethodInstance, src::Array{UInt8, 1})
922+
src=ccall(:jl_uncompress_ir, Any, (Any, Ptr{Cvoid}, Any), mi.def,C_NULL, src::Vector{UInt8})::CodeInfo
923+
returninflate_ir!(src, mi)
917924
end
925+
retrieve_ir_for_inlining(mi::MethodInstance, src::CodeInfo)=inflate_ir(src, mi)::IRCode
926+
retrieve_ir_for_inlining(mi::MethodInstance, ir::IRCode)=copy(ir)
918927

919928
functionhandle_single_case!(
920929
ir::IRCode, idx::Int, stmt::Expr,
@@ -1196,7 +1205,7 @@ function process_simple!(ir::IRCode, idx::Int, state::InliningState, todo::Vecto
11961205
end
11971206
end
11981207

1199-
if sig.f!== Core.invoke&&is_builtin(sig)
1208+
if sig.f!== Core.invoke&&sig.f!== Core.finalizer&&is_builtin(sig)
12001209
# No inlining for builtins (other invoke/apply/typeassert)
12011210
returnnothing
12021211
end
@@ -1213,9 +1222,10 @@ function process_simple!(ir::IRCode, idx::Int, state::InliningState, todo::Vecto
12131222
end
12141223

12151224
#TODO inline non-`isdispatchtuple`, union-split callsites?
1216-
functionanalyze_single_call!(
1217-
ir::IRCode, idx::Int, stmt::Expr, infos::Vector{MethodMatchInfo}, flag::UInt8,
1218-
sig::Signature, state::InliningState, todo::Vector{Pair{Int, Any}})
1225+
functioncompute_inlining_cases(
1226+
infos::Vector{MethodMatchInfo}, flag::UInt8,
1227+
sig::Signature, state::InliningState,
1228+
do_resolve::Bool=true)
12191229
argtypes= sig.argtypes
12201230
cases= InliningCase[]
12211231
local any_fully_covered=false
@@ -1232,7 +1242,7 @@ function analyze_single_call!(
12321242
continue
12331243
end
12341244
for matchin meth
1235-
handled_all_cases&=handle_match!(match, argtypes, flag, state, cases,true)
1245+
handled_all_cases&=handle_match!(match, argtypes, flag, state, cases,true, do_resolve)
12361246
any_fully_covered|= match.fully_covers
12371247
end
12381248
end
@@ -1242,8 +1252,18 @@ function analyze_single_call!(
12421252
filter!(case::InliningCase->isdispatchtuple(case.sig), cases)
12431253
end
12441254

1245-
handle_cases!(ir, idx, stmt,argtypes_to_type(argtypes), cases,
1246-
handled_all_cases& any_fully_covered, todo, state.params)
1255+
return cases, handled_all_cases& any_fully_covered
1256+
end
1257+
1258+
functionanalyze_single_call!(
1259+
ir::IRCode, idx::Int, stmt::Expr, infos::Vector{MethodMatchInfo}, flag::UInt8,
1260+
sig::Signature, state::InliningState, todo::Vector{Pair{Int, Any}})
1261+
1262+
r=compute_inlining_cases(infos, flag, sig, state)
1263+
r===nothing&&returnnothing
1264+
cases, all_covered= r
1265+
handle_cases!(ir, idx, stmt,argtypes_to_type(sig.argtypes), cases,
1266+
all_covered, todo, state.params)
12471267
end
12481268

12491269
# similar to `analyze_single_call!`, but with constant results
@@ -1299,14 +1319,15 @@ end
12991319

13001320
functionhandle_match!(
13011321
match::MethodMatch, argtypes::Vector{Any}, flag::UInt8, state::InliningState,
1302-
cases::Vector{InliningCase}, allow_abstract::Bool=false)
1322+
cases::Vector{InliningCase}, allow_abstract::Bool=false,
1323+
do_resolve::Bool=true)
13031324
spec_types= match.spec_types
13041325
allow_abstract||isdispatchtuple(spec_types)||returnfalse
13051326
# we may see duplicated dispatch signatures here when a signature gets widened
13061327
# during abstract interpretation: for the purpose of inlining, we can just skip
13071328
# processing this dispatch candidate
13081329
_any(case->case.sig=== spec_types, cases)&&returntrue
1309-
item=analyze_method!(match, argtypes, flag, state)
1330+
item=analyze_method!(match, argtypes, flag, state, do_resolve)
13101331
item===nothing&&returnfalse
13111332
push!(cases,InliningCase(spec_types, item))
13121333
returntrue
@@ -1419,6 +1440,48 @@ function assemble_inline_todo!(ir::IRCode, state::InliningState)
14191440
continue
14201441
end
14211442

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

‎base/compiler/ssair/ir.jl

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

164164

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

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

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp