Skip to content

Commit 014f8de

Browse files
authored
Align meaning for effects and IR flags (#50313)
This fixes a longstanding todo where the IR_FLAG_EFFECT_FREE flag actually required both :effect_free and :nothrow. After this PR, it is equivalent to :effect_free only. The mismatch in meaning here caused #50311. `Symbol(::String)` is :effect_free, but not :nothrow. As a result, setting IR_FLAG_EFFECT_FREE on it was not legal. Later, irinterp did discover that it was nothrow and set IR_FLAG_NOTHROW, but did not have sufficient information to know that it was also :effect_free, so it could not set that flag. With this PR, IR_FLAG_EFFECT_FREE is set early in inference, so once irinterp discovers IR_FLAG_NOTHROW, the call becomes DCE-eligible as desired. Fixes #50311.
1 parent 9dc2991 commit 014f8de

File tree

7 files changed

+50
-34
lines changed

7 files changed

+50
-34
lines changed

base/boot.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,11 @@ end)
510510

511511
function Symbol(s::String)
512512
@_foldable_meta
513+
@noinline
513514
return _Symbol(ccall(:jl_string_ptr, Ptr{UInt8}, (Any,), s), sizeof(s), s)
514515
end
515516
function Symbol(a::Array{UInt8,1})
517+
@noinline
516518
return _Symbol(ccall(:jl_array_ptr, Ptr{UInt8}, (Any,), a), Intrinsics.arraylen(a), a)
517519
end
518520
Symbol(s::Symbol) = s

base/compiler/abstractinterpretation.jl

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,17 +2280,33 @@ struct RTEffects
22802280
RTEffects(@nospecialize(rt), effects::Effects) = new(rt, effects)
22812281
end
22822282

2283+
function mark_curr_effect_flags!(sv::AbsIntState, effects::Effects)
2284+
if isa(sv, InferenceState)
2285+
if is_effect_free(effects)
2286+
add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE)
2287+
else
2288+
sub_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE)
2289+
end
2290+
if is_nothrow(effects)
2291+
add_curr_ssaflag!(sv, IR_FLAG_NOTHROW)
2292+
else
2293+
sub_curr_ssaflag!(sv, IR_FLAG_NOTHROW)
2294+
end
2295+
if is_consistent(effects)
2296+
add_curr_ssaflag!(sv, IR_FLAG_CONSISTENT)
2297+
else
2298+
sub_curr_ssaflag!(sv, IR_FLAG_CONSISTENT)
2299+
end
2300+
end
2301+
end
2302+
22832303
function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, sv::InferenceState)
22842304
si = StmtInfo(!call_result_unused(sv, sv.currpc))
22852305
(; rt, effects, info) = abstract_call(interp, arginfo, si, sv)
22862306
sv.stmt_info[sv.currpc] = info
22872307
# mark this call statement as DCE-elgible
22882308
# TODO better to do this in a single pass based on the `info` object at the end of abstractinterpret?
2289-
if is_removable_if_unused(effects)
2290-
add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE)
2291-
else
2292-
sub_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE)
2293-
end
2309+
mark_curr_effect_flags!(sv, effects)
22942310
return RTEffects(rt, effects)
22952311
end
22962312

@@ -2429,14 +2445,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
24292445
elseif ehead === :foreigncall
24302446
(; rt, effects) = abstract_eval_foreigncall(interp, e, vtypes, sv)
24312447
t = rt
2432-
if isa(sv, InferenceState)
2433-
# mark this call statement as DCE-elgible
2434-
if is_removable_if_unused(effects)
2435-
add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE)
2436-
else
2437-
sub_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE)
2438-
end
2439-
end
2448+
mark_curr_effect_flags!(sv, effects)
24402449
elseif ehead === :cfunction
24412450
effects = EFFECTS_UNKNOWN
24422451
t = e.args[1]
@@ -2558,7 +2567,7 @@ end
25582567
function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), vtypes::VarTable, sv::InferenceState)
25592568
if !isa(e, Expr)
25602569
if isa(e, PhiNode)
2561-
add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE)
2570+
add_curr_ssaflag!(sv, IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW)
25622571
return abstract_eval_phi(interp, e, vtypes, sv)
25632572
end
25642573
return abstract_eval_special_value(interp, e, vtypes, sv)

base/compiler/ssair/inlining.jl

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ function ir_prepare_inlining!(insert_node!::Inserter, inline_target::Union{IRCod
370370
if !validate_sparams(sparam_vals)
371371
# N.B. This works on the caller-side argexprs, (i.e. before the va fixup below)
372372
sp_ssa = insert_node!(
373-
effect_free(NewInstruction(Expr(:call, Core._compute_sparams, def, argexprs...), SimpleVector, topline)))
373+
effect_free_and_nothrow(NewInstruction(Expr(:call, Core._compute_sparams, def, argexprs...), SimpleVector, topline)))
374374
end
375375
if def.isva
376376
nargs_def = Int(def.nargs::Int32)
@@ -426,7 +426,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector
426426
inline_compact.result[idx′][:type] =
427427
argextype(val, isa(val, Argument) || isa(val, Expr) ? compact : inline_compact)
428428
# Everything legal in value position is guaranteed to be effect free in stmt position
429-
inline_compact.result[idx′][:flag] = IR_FLAG_EFFECT_FREE
429+
inline_compact.result[idx′][:flag] = IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
430430
break
431431
end
432432
inline_compact[idx′] = stmt′
@@ -702,7 +702,7 @@ function batch_inline!(ir::IRCode, todo::Vector{Pair{Int,Any}}, propagate_inboun
702702
for aidx in 1:length(argexprs)
703703
aexpr = argexprs[aidx]
704704
if isa(aexpr, Expr) || isa(aexpr, GlobalRef)
705-
ninst = effect_free(NewInstruction(aexpr, argextype(aexpr, compact), compact.result[idx][:line]))
705+
ninst = effect_free_and_nothrow(NewInstruction(aexpr, argextype(aexpr, compact), compact.result[idx][:line]))
706706
argexprs[aidx] = insert_node_here!(compact, ninst)
707707
end
708708
end
@@ -992,9 +992,10 @@ function flags_for_effects(effects::Effects)
992992
if is_consistent(effects)
993993
flags |= IR_FLAG_CONSISTENT
994994
end
995-
if is_removable_if_unused(effects)
996-
flags |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
997-
elseif is_nothrow(effects)
995+
if is_effect_free(effects)
996+
flags |= IR_FLAG_EFFECT_FREE
997+
end
998+
if is_nothrow(effects)
998999
flags |= IR_FLAG_NOTHROW
9991000
end
10001001
return flags
@@ -1650,7 +1651,7 @@ function inline_const_if_inlineable!(inst::Instruction)
16501651
inst[:inst] = quoted(rt.val)
16511652
return true
16521653
end
1653-
inst[:flag] |= IR_FLAG_EFFECT_FREE
1654+
inst[:flag] |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
16541655
return false
16551656
end
16561657

@@ -1773,7 +1774,7 @@ function late_inline_special_case!(
17731774
return SomeCase(quoted(type.val))
17741775
end
17751776
cmp_call = Expr(:call, GlobalRef(Core, :(===)), stmt.args[2], stmt.args[3])
1776-
cmp_call_ssa = insert_node!(ir, idx, effect_free(NewInstruction(cmp_call, Bool)))
1777+
cmp_call_ssa = insert_node!(ir, idx, effect_free_and_nothrow(NewInstruction(cmp_call, Bool)))
17771778
not_call = Expr(:call, GlobalRef(Core.Intrinsics, :not_int), cmp_call_ssa)
17781779
return SomeCase(not_call)
17791780
elseif length(argtypes) == 3 && istopfunction(f, :(>:))
@@ -1816,13 +1817,13 @@ end
18161817

18171818
function insert_spval!(insert_node!::Inserter, spvals_ssa::SSAValue, spidx::Int, do_isdefined::Bool)
18181819
ret = insert_node!(
1819-
effect_free(NewInstruction(Expr(:call, Core._svec_ref, false, spvals_ssa, spidx), Any)))
1820+
effect_free_and_nothrow(NewInstruction(Expr(:call, Core._svec_ref, false, spvals_ssa, spidx), Any)))
18201821
tcheck_not = nothing
18211822
if do_isdefined
18221823
tcheck = insert_node!(
1823-
effect_free(NewInstruction(Expr(:call, Core.isa, ret, Core.TypeVar), Bool)))
1824+
effect_free_and_nothrow(NewInstruction(Expr(:call, Core.isa, ret, Core.TypeVar), Bool)))
18241825
tcheck_not = insert_node!(
1825-
effect_free(NewInstruction(Expr(:call, not_int, tcheck), Bool)))
1826+
effect_free_and_nothrow(NewInstruction(Expr(:call, not_int, tcheck), Bool)))
18261827
end
18271828
return (ret, tcheck_not)
18281829
end
@@ -1849,7 +1850,7 @@ function ssa_substitute_op!(insert_node!::Inserter, subst_inst::Instruction,
18491850
(ret, tcheck_not) = insert_spval!(insert_node!, spvals_ssa::SSAValue, spidx, maybe_undef)
18501851
if maybe_undef
18511852
insert_node!(
1852-
non_effect_free(NewInstruction(Expr(:throw_undef_if_not, val.name, tcheck_not), Nothing)))
1853+
NewInstruction(Expr(:throw_undef_if_not, val.name, tcheck_not), Nothing))
18531854
end
18541855
return ret
18551856
end

base/compiler/ssair/ir.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,7 @@ function NewInstruction(inst::Instruction;
316316
return NewInstruction(stmt, type, info, line, flag)
317317
end
318318
@specialize
319-
effect_free(newinst::NewInstruction) = NewInstruction(newinst; flag=add_flag(newinst, IR_FLAG_EFFECT_FREE))
320-
non_effect_free(newinst::NewInstruction) = NewInstruction(newinst; flag=sub_flag(newinst, IR_FLAG_EFFECT_FREE))
319+
effect_free_and_nothrow(newinst::NewInstruction) = NewInstruction(newinst; flag=add_flag(newinst, IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW))
321320
with_flags(newinst::NewInstruction, flags::UInt8) = NewInstruction(newinst; flag=add_flag(newinst, flags))
322321
without_flags(newinst::NewInstruction, flags::UInt8) = NewInstruction(newinst; flag=sub_flag(newinst, flags))
323322
function add_flag(newinst::NewInstruction, newflag::UInt8)
@@ -1677,7 +1676,7 @@ function maybe_erase_unused!(callback::Function, compact::IncrementalCompact, id
16771676
stmt = inst[:inst]
16781677
stmt === nothing && return false
16791678
inst[:type] === Bottom && return false
1680-
effect_free = (inst[:flag] & IR_FLAG_EFFECT_FREE) 0
1679+
effect_free = (inst[:flag] & (IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW)) == IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
16811680
effect_free || return false
16821681
foreachssa(stmt) do val::SSAValue
16831682
if compact.used_ssas[val.id] == 1

base/compiler/ssair/irinterp.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ function reprocess_instruction!(interp::AbstractInterpreter, idx::Int, bb::Union
162162
if rt !== nothing
163163
if isa(rt, Const)
164164
ir.stmts[idx][:type] = rt
165-
if is_inlineable_constant(rt.val) && !isa(inst, PhiNode) && (ir.stmts[idx][:flag] & IR_FLAG_EFFECT_FREE) != 0
165+
if is_inlineable_constant(rt.val) && !isa(inst, PhiNode) && (ir.stmts[idx][:flag] & (IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW)) == IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
166166
ir.stmts[idx][:inst] = quoted(rt.val)
167167
end
168168
return true

base/compiler/ssair/passes.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ function lift_arg!(
472472
end
473473
end
474474
if isa(lifted, GlobalRef) || isa(lifted, Expr)
475-
lifted = insert_node!(compact, leaf, effect_free(NewInstruction(lifted, argextype(lifted, compact))))
475+
lifted = insert_node!(compact, leaf, effect_free_and_nothrow(NewInstruction(lifted, argextype(lifted, compact))))
476476
compact[leaf] = nothing
477477
stmt.args[argidx] = lifted
478478
compact[leaf] = stmt
@@ -718,7 +718,7 @@ function perform_lifting!(compact::IncrementalCompact,
718718
end
719719
if isa(old_node, PhiNode)
720720
new_node = PhiNode()
721-
ssa = insert_node!(compact, old_ssa, effect_free(NewInstruction(new_node, result_t)))
721+
ssa = insert_node!(compact, old_ssa, effect_free_and_nothrow(NewInstruction(new_node, result_t)))
722722
lifted_philikes[i] = LiftedPhilike(ssa, new_node, true)
723723
else
724724
@assert is_known_call(old_node, Core.ifelse, compact)
@@ -1110,8 +1110,8 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing)
11101110
def_val = perform_lifting!(compact,
11111111
visited_philikes, field, def_lifting_cache, Bool, lifted_leaves_def, val, lazydomtree).val
11121112
end
1113-
insert_node!(compact, SSAValue(idx), non_effect_free(NewInstruction(
1114-
Expr(:throw_undef_if_not, Symbol("##getfield##"), def_val), Nothing)))
1113+
insert_node!(compact, SSAValue(idx), NewInstruction(
1114+
Expr(:throw_undef_if_not, Symbol("##getfield##"), def_val), Nothing))
11151115

11161116
else
11171117
# val must be defined

test/compiler/effects.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,3 +993,8 @@ end
993993
hf50198(s) = hasfield(typeof((;x=1, y=2)), s)
994994
f50198() = (hf50198(Ref(:x)[]); nothing)
995995
@test fully_eliminated(f50198)
996+
997+
# Effects properly applied to flags by irinterp (#50311)
998+
f50311(x, s) = Symbol(s)
999+
g50311(x) = Val{f50311((1.0, x), "foo")}()
1000+
@test fully_eliminated(g50311, Tuple{Float64})

0 commit comments

Comments
 (0)