Skip to content

Commit 2345411

Browse files
committed
refactor the optimization flags set and utilities
This commit serves as a preliminary PR for #52991. It involves adding `IR_FLAG_TERMINATES` to the optimization flags and carrying out various renaming refactors. As `IR_FLAG_TERMINATES` isn't utilized in this particular commit, it doesn't bring any changes to the compiler's functionality. The incoming commit will make use of it and fix #52991 along with other accompanying changes.
1 parent 188cc93 commit 2345411

File tree

8 files changed

+124
-126
lines changed

8 files changed

+124
-126
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2722,7 +2722,7 @@ end
27222722
function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState)
27232723
if !isa(e, Expr)
27242724
if isa(e, PhiNode)
2725-
add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW)
2725+
add_curr_ssaflag!(sv, IR_FLAGS_REMOVABLE)
27262726
return RTEffects(abstract_eval_phi(interp, e, vtypes, sv), Union{}, EFFECTS_TOTAL)
27272727
end
27282728
(; rt, exct, effects) = abstract_eval_special_value(interp, e, vtypes, sv)

base/compiler/optimize.jl

Lines changed: 74 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,61 @@ const IR_FLAG_INBOUNDS = one(UInt32) << 0
2323
const IR_FLAG_INLINE = one(UInt32) << 1
2424
# This statement is marked as @noinline by user
2525
const IR_FLAG_NOINLINE = one(UInt32) << 2
26+
# This statement is on a code path that eventually `throw`s.
2627
const IR_FLAG_THROW_BLOCK = one(UInt32) << 3
27-
# This statement was proven :effect_free
28-
const IR_FLAG_EFFECT_FREE = one(UInt32) << 4
29-
# This statement was proven not to throw
30-
const IR_FLAG_NOTHROW = one(UInt32) << 5
31-
# This is :consistent
32-
const IR_FLAG_CONSISTENT = one(UInt32) << 6
3328
# An optimization pass has updated this statement in a way that may
3429
# have exposed information that inference did not see. Re-running
3530
# inference on this statement may be profitable.
36-
const IR_FLAG_REFINED = one(UInt32) << 7
37-
# This is :noub == ALWAYS_TRUE
38-
const IR_FLAG_NOUB = one(UInt32) << 8
39-
31+
const IR_FLAG_REFINED = one(UInt32) << 4
32+
# This statement is proven :consistent
33+
const IR_FLAG_CONSISTENT = one(UInt32) << 5
34+
# This statement is proven :effect_free
35+
const IR_FLAG_EFFECT_FREE = one(UInt32) << 6
36+
# This statement is proven :nothrow
37+
const IR_FLAG_NOTHROW = one(UInt32) << 7
38+
# This statement is proven :terminates
39+
const IR_FLAG_TERMINATES = one(UInt32) << 8
40+
# This statement is proven :noub
41+
const IR_FLAG_NOUB = one(UInt32) << 9
4042
# TODO: Both of these should eventually go away once
41-
# This is :effect_free == EFFECT_FREE_IF_INACCESSIBLEMEMONLY
42-
const IR_FLAG_EFIIMO = one(UInt32) << 9
43-
# This is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY
44-
const IR_FLAG_INACCESSIBLE_OR_ARGMEM = one(UInt32) << 10
43+
# This statement is :effect_free == EFFECT_FREE_IF_INACCESSIBLEMEMONLY
44+
const IR_FLAG_EFIIMO = one(UInt32) << 10
45+
# This statement is :inaccessiblememonly == INACCESSIBLEMEM_OR_ARGMEMONLY
46+
const IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM = one(UInt32) << 11
47+
48+
const NUM_IR_FLAGS = 12 # sync with julia.h
4549

46-
const NUM_IR_FLAGS = 11 # sync with julia.h
50+
const IR_FLAGS_EFFECTS =
51+
IR_FLAG_CONSISTENT | IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_NOUB
4752

48-
const IR_FLAGS_EFFECTS = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW | IR_FLAG_CONSISTENT | IR_FLAG_NOUB
53+
const IR_FLAGS_REMOVABLE = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
54+
55+
const IR_FLAGS_NEEDS_EA = IR_FLAG_EFIIMO | IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM
4956

5057
has_flag(curr::UInt32, flag::UInt32) = (curr & flag) == flag
5158

59+
function flags_for_effects(effects::Effects)
60+
flags = zero(UInt32)
61+
if is_consistent(effects)
62+
flags |= IR_FLAG_CONSISTENT
63+
end
64+
if is_effect_free(effects)
65+
flags |= IR_FLAG_EFFECT_FREE
66+
elseif is_effect_free_if_inaccessiblememonly(effects)
67+
flags |= IR_FLAG_EFIIMO
68+
end
69+
if is_nothrow(effects)
70+
flags |= IR_FLAG_NOTHROW
71+
end
72+
if is_inaccessiblemem_or_argmemonly(effects)
73+
flags |= IR_FLAG_INACCESSIBLEMEM_OR_ARGMEM
74+
end
75+
if is_noub(effects)
76+
flags |= IR_FLAG_NOUB
77+
end
78+
return flags
79+
end
80+
5281
const TOP_TUPLE = GlobalRef(Core, :tuple)
5382

5483
# This corresponds to the type of `CodeInfo`'s `inlining_cost` field
@@ -263,9 +292,9 @@ end
263292

264293
"""
265294
stmt_effect_flags(stmt, rt, src::Union{IRCode,IncrementalCompact}) ->
266-
(consistent::Bool, effect_free_and_nothrow::Bool, nothrow::Bool)
295+
(consistent::Bool, removable::Bool, nothrow::Bool)
267296
268-
Returns a tuple of `(:consistent, :effect_free_and_nothrow, :nothrow)` flags for a given statement.
297+
Returns a tuple of `(:consistent, :removable, :nothrow)` flags for a given statement.
269298
"""
270299
function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospecialize(rt), src::Union{IRCode,IncrementalCompact})
271300
# TODO: We're duplicating analysis from inference here.
@@ -309,7 +338,8 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe
309338
consistent = is_consistent(effects)
310339
effect_free = is_effect_free(effects)
311340
nothrow = is_nothrow(effects)
312-
return (consistent, effect_free & nothrow, nothrow)
341+
removable = effect_free & nothrow
342+
return (consistent, removable, nothrow)
313343
elseif head === :new
314344
return new_expr_effect_flags(𝕃ₒ, args, src)
315345
elseif head === :foreigncall
@@ -319,7 +349,8 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe
319349
consistent = is_consistent(effects)
320350
effect_free = is_effect_free(effects)
321351
nothrow = is_nothrow(effects)
322-
return (consistent, effect_free & nothrow, nothrow)
352+
removable = effect_free & nothrow
353+
return (consistent, removable, nothrow)
323354
elseif head === :new_opaque_closure
324355
length(args) < 4 && return (false, false, false)
325356
typ = argextype(args[1], src)
@@ -346,6 +377,29 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe
346377
return (true, true, true)
347378
end
348379

380+
function recompute_effects_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospecialize(rt),
381+
src::Union{IRCode,IncrementalCompact})
382+
flag = IR_FLAG_NULL
383+
(consistent, removable, nothrow) = stmt_effect_flags(𝕃ₒ, stmt, rt, src)
384+
if consistent
385+
flag |= IR_FLAG_CONSISTENT
386+
end
387+
if removable
388+
flag |= IR_FLAGS_REMOVABLE
389+
elseif nothrow
390+
flag |= IR_FLAG_NOTHROW
391+
end
392+
if !(isexpr(stmt, :call) || isexpr(stmt, :invoke))
393+
# There is a bit of a subtle point here, which is that some non-call
394+
# statements (e.g. PiNode) can be UB:, however, we consider it
395+
# illegal to introduce such statements that actually cause UB (for any
396+
# input). Ideally that'd be handled at insertion time (TODO), but for
397+
# the time being just do that here.
398+
flag |= IR_FLAG_NOUB
399+
end
400+
return flag
401+
end
402+
349403
"""
350404
argextype(x, src::Union{IRCode,IncrementalCompact}) -> t
351405
argextype(x, src::CodeInfo, sptypes::Vector{VarState}) -> t
@@ -694,8 +748,6 @@ function is_conditional_noub(inst::Instruction, sv::PostOptAnalysisState)
694748
return true
695749
end
696750

697-
const IR_FLAGS_NEEDS_EA = IR_FLAG_EFIIMO | IR_FLAG_INACCESSIBLE_OR_ARGMEM
698-
699751
function scan_non_dataflow_flags!(inst::Instruction, sv::PostOptAnalysisState)
700752
flag = inst[:flag]
701753
# If we can prove that the argmem does not escape the current function, we can

base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ using ._TOP_MOD: # Base definitions
2525
unwrap_unionall, !, !=, !==, &, *, +, -, :, <, <<, =>, >, |, , , , , , , ,
2626
using Core.Compiler: # Core.Compiler specific definitions
2727
Bottom, IRCode, IR_FLAG_NOTHROW, InferenceResult, SimpleInferenceLattice,
28-
argextype, check_effect_free!, fieldcount_noerror, hasintersect, has_flag,
29-
intrinsic_nothrow, is_meta_expr_head, isbitstype, isexpr, println, setfield!_nothrow,
30-
singleton_type, try_compute_field, try_compute_fieldidx, widenconst, , AbstractLattice
28+
argextype, fieldcount_noerror, hasintersect, has_flag, intrinsic_nothrow,
29+
is_meta_expr_head, isbitstype, isexpr, println, setfield!_nothrow, singleton_type,
30+
try_compute_field, try_compute_fieldidx, widenconst, , AbstractLattice
3131

3232
include(x) = _TOP_MOD.include(@__MODULE__, x)
3333
if _TOP_MOD === Core.Compiler
@@ -597,12 +597,12 @@ struct LivenessChange <: Change
597597
end
598598
const Changes = Vector{Change}
599599

600-
struct AnalysisState{T, L <: AbstractLattice}
600+
struct AnalysisState{GetEscapeCache, Lattice<:AbstractLattice}
601601
ir::IRCode
602602
estate::EscapeState
603603
changes::Changes
604-
𝕃ₒ::L
605-
get_escape_cache::T
604+
𝕃ₒ::Lattice
605+
get_escape_cache::GetEscapeCache
606606
end
607607

608608
"""

base/compiler/ssair/inlining.jl

Lines changed: 19 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ function ir_prepare_inlining!(insert_node!::Inserter, inline_target::Union{IRCod
366366
if !validate_sparams(mi.sparam_vals)
367367
# N.B. This works on the caller-side argexprs, (i.e. before the va fixup below)
368368
spvals_ssa = insert_node!(
369-
effect_free_and_nothrow(NewInstruction(Expr(:call, Core._compute_sparams, def, argexprs...), SimpleVector, topline)))
369+
removable_if_unused(NewInstruction(Expr(:call, Core._compute_sparams, def, argexprs...), SimpleVector, topline)))
370370
end
371371
if def.isva
372372
nargs_def = Int(def.nargs::Int32)
@@ -425,7 +425,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector
425425
inline_compact.result[idx′][:type] =
426426
argextype(val, isa(val, Argument) || isa(val, Expr) ? compact : inline_compact)
427427
# Everything legal in value position is guaranteed to be effect free in stmt position
428-
inline_compact.result[idx′][:flag] = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
428+
inline_compact.result[idx′][:flag] = IR_FLAGS_REMOVABLE
429429
break
430430
elseif isexpr(stmt′, :boundscheck)
431431
adjust_boundscheck!(inline_compact, idx′, stmt′, boundscheck)
@@ -692,7 +692,7 @@ function batch_inline!(ir::IRCode, todo::Vector{Pair{Int,Any}}, propagate_inboun
692692
for aidx in 1:length(argexprs)
693693
aexpr = argexprs[aidx]
694694
if isa(aexpr, Expr) || isa(aexpr, GlobalRef)
695-
ninst = effect_free_and_nothrow(NewInstruction(aexpr, argextype(aexpr, compact), compact.result[idx][:line]))
695+
ninst = removable_if_unused(NewInstruction(aexpr, argextype(aexpr, compact), compact.result[idx][:line]))
696696
argexprs[aidx] = insert_node_here!(compact, ninst)
697697
end
698698
end
@@ -996,28 +996,6 @@ function retrieve_ir_for_inlining(::MethodInstance, ir::IRCode, preserve_local_s
996996
return ir
997997
end
998998

999-
function flags_for_effects(effects::Effects)
1000-
flags::UInt32 = 0
1001-
if is_consistent(effects)
1002-
flags |= IR_FLAG_CONSISTENT
1003-
end
1004-
if is_effect_free(effects)
1005-
flags |= IR_FLAG_EFFECT_FREE
1006-
elseif is_effect_free_if_inaccessiblememonly(effects)
1007-
flags |= IR_FLAG_EFIIMO
1008-
end
1009-
if is_inaccessiblemem_or_argmemonly(effects)
1010-
flags |= IR_FLAG_INACCESSIBLE_OR_ARGMEM
1011-
end
1012-
if is_nothrow(effects)
1013-
flags |= IR_FLAG_NOTHROW
1014-
end
1015-
if is_noub(effects)
1016-
flags |= IR_FLAG_NOUB
1017-
end
1018-
return flags
1019-
end
1020-
1021999
function handle_single_case!(todo::Vector{Pair{Int,Any}},
10221000
ir::IRCode, idx::Int, stmt::Expr, @nospecialize(case),
10231001
isinvoke::Bool = false)
@@ -1252,29 +1230,12 @@ end
12521230
# As a matter of convenience, this pass also computes effect-freenes.
12531231
# For primitives, we do that right here. For proper calls, we will
12541232
# discover this when we consult the caches.
1255-
function check_effect_free!(ir::IRCode, idx::Int, @nospecialize(stmt), @nospecialize(rt), state::InliningState)
1256-
return check_effect_free!(ir, idx, stmt, rt, optimizer_lattice(state.interp))
1257-
end
1258-
function check_effect_free!(ir::IRCode, idx::Int, @nospecialize(stmt), @nospecialize(rt), 𝕃ₒ::AbstractLattice)
1259-
(consistent, effect_free_and_nothrow, nothrow) = stmt_effect_flags(𝕃ₒ, stmt, rt, ir)
1260-
inst = ir.stmts[idx]
1261-
if consistent
1262-
add_flag!(inst, IR_FLAG_CONSISTENT)
1263-
end
1264-
if effect_free_and_nothrow
1265-
add_flag!(inst, IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW)
1266-
elseif nothrow
1267-
add_flag!(inst, IR_FLAG_NOTHROW)
1268-
end
1269-
if !(isexpr(stmt, :call) || isexpr(stmt, :invoke))
1270-
# There is a bit of a subtle point here, which is that some non-call
1271-
# statements (e.g. PiNode) can be UB:, however, we consider it
1272-
# illegal to introduce such statements that actually cause UB (for any
1273-
# input). Ideally that'd be handled at insertion time (TODO), but for
1274-
# the time being just do that here.
1275-
add_flag!(inst, IR_FLAG_NOUB)
1276-
end
1277-
return effect_free_and_nothrow
1233+
add_inst_flag!(inst::Instruction, ir::IRCode, state::InliningState) =
1234+
add_inst_flag!(inst, ir, optimizer_lattice(state.interp))
1235+
function add_inst_flag!(inst::Instruction, ir::IRCode, 𝕃ₒ::AbstractLattice)
1236+
flags = recompute_effects_flags(𝕃ₒ, inst[:stmt], inst[:type], ir)
1237+
add_flag!(inst, flags)
1238+
return !iszero(flags & IR_FLAGS_REMOVABLE)
12781239
end
12791240

12801241
# Handles all analysis and inlining of intrinsics and builtins. In particular,
@@ -1283,11 +1244,11 @@ end
12831244
function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, state::InliningState)
12841245
inst = ir[SSAValue(idx)]
12851246
stmt = inst[:stmt]
1286-
rt = inst[:type]
12871247
if !(stmt isa Expr)
1288-
check_effect_free!(ir, idx, stmt, rt, state)
1248+
add_inst_flag!(inst, ir, state)
12891249
return nothing
12901250
end
1251+
rt = inst[:type]
12911252
head = stmt.head
12921253
if head !== :call
12931254
if head === :splatnew
@@ -1299,7 +1260,7 @@ function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stat
12991260
sig === nothing && return nothing
13001261
return stmt, sig
13011262
end
1302-
check_effect_free!(ir, idx, stmt, rt, state)
1263+
add_inst_flag!(inst, ir, state)
13031264
return nothing
13041265
end
13051266

@@ -1317,7 +1278,7 @@ function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stat
13171278
return nothing
13181279
end
13191280

1320-
if check_effect_free!(ir, idx, stmt, rt, state)
1281+
if add_inst_flag!(inst, ir, state)
13211282
if sig.f === typeassert || (optimizer_lattice(state.interp), sig.ft, typeof(typeassert))
13221283
# typeassert is a no-op if effect free
13231284
inst[:stmt] = stmt.args[2]
@@ -1335,7 +1296,7 @@ function process_simple!(todo::Vector{Pair{Int,Any}}, ir::IRCode, idx::Int, stat
13351296
lateres = late_inline_special_case!(ir, idx, stmt, rt, sig, state)
13361297
if isa(lateres, SomeCase)
13371298
inst[:stmt] = lateres.val
1338-
check_effect_free!(ir, idx, lateres.val, rt, state)
1299+
add_inst_flag!(inst, ir, state)
13391300
return nothing
13401301
end
13411302

@@ -1683,7 +1644,7 @@ function inline_const_if_inlineable!(inst::Instruction)
16831644
inst[:stmt] = quoted(rt.val)
16841645
return true
16851646
end
1686-
add_flag!(inst, IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW)
1647+
add_flag!(inst, IR_FLAGS_REMOVABLE)
16871648
return false
16881649
end
16891650

@@ -1808,7 +1769,7 @@ function late_inline_special_case!(
18081769
return SomeCase(quoted(type.val))
18091770
end
18101771
cmp_call = Expr(:call, GlobalRef(Core, :(===)), stmt.args[2], stmt.args[3])
1811-
cmp_call_ssa = insert_node!(ir, idx, effect_free_and_nothrow(NewInstruction(cmp_call, Bool)))
1772+
cmp_call_ssa = insert_node!(ir, idx, removable_if_unused(NewInstruction(cmp_call, Bool)))
18121773
not_call = Expr(:call, GlobalRef(Core.Intrinsics, :not_int), cmp_call_ssa)
18131774
return SomeCase(not_call)
18141775
elseif length(argtypes) == 3 && istopfunction(f, :(>:))
@@ -1853,13 +1814,13 @@ end
18531814

18541815
function insert_spval!(insert_node!::Inserter, spvals_ssa::SSAValue, spidx::Int, do_isdefined::Bool)
18551816
ret = insert_node!(
1856-
effect_free_and_nothrow(NewInstruction(Expr(:call, Core._svec_ref, spvals_ssa, spidx), Any)))
1817+
removable_if_unused(NewInstruction(Expr(:call, Core._svec_ref, spvals_ssa, spidx), Any)))
18571818
tcheck_not = nothing
18581819
if do_isdefined
18591820
tcheck = insert_node!(
1860-
effect_free_and_nothrow(NewInstruction(Expr(:call, Core.isa, ret, Core.TypeVar), Bool)))
1821+
removable_if_unused(NewInstruction(Expr(:call, Core.isa, ret, Core.TypeVar), Bool)))
18611822
tcheck_not = insert_node!(
1862-
effect_free_and_nothrow(NewInstruction(Expr(:call, not_int, tcheck), Bool)))
1823+
removable_if_unused(NewInstruction(Expr(:call, not_int, tcheck), Bool)))
18631824
end
18641825
return (ret, tcheck_not)
18651826
end

0 commit comments

Comments
 (0)