Skip to content

Commit a2cc0f4

Browse files
authored
Hook up IR_FLAG_NOTHROW (#45752)
I added the flag in #45272, but didn't hook it up to the nothrow model. In the fullness of time, these should forwarded from inference and only recomputed if necessary, but for the moment, just make it work.
1 parent 4237352 commit a2cc0f4

File tree

5 files changed

+73
-48
lines changed

5 files changed

+73
-48
lines changed

base/compiler/optimize.jl

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -188,81 +188,90 @@ function stmt_affects_purity(@nospecialize(stmt), ir)
188188
end
189189

190190
"""
191-
stmt_effect_free(stmt, rt, src::Union{IRCode,IncrementalCompact})
191+
stmt_effect_flags(stmt, rt, src::Union{IRCode,IncrementalCompact})
192192
193-
Determine whether a `stmt` is "side-effect-free", i.e. may be removed if it has no uses.
193+
Returns a tuple of (effect_free_and_nothrow, nothrow) for a given statement.
194194
"""
195-
function stmt_effect_free(@nospecialize(stmt), @nospecialize(rt), src::Union{IRCode,IncrementalCompact})
196-
isa(stmt, PiNode) && return true
197-
isa(stmt, PhiNode) && return true
198-
isa(stmt, ReturnNode) && return false
199-
isa(stmt, GotoNode) && return false
200-
isa(stmt, GotoIfNot) && return false
201-
isa(stmt, Slot) && return false # Slots shouldn't occur in the IR at this point, but let's be defensive here
202-
isa(stmt, GlobalRef) && return isdefined(stmt.mod, stmt.name)
195+
function stmt_effect_flags(@nospecialize(stmt), @nospecialize(rt), src::Union{IRCode,IncrementalCompact})
196+
# TODO: We're duplicating analysis from inference here.
197+
isa(stmt, PiNode) && return (true, true)
198+
isa(stmt, PhiNode) && return (true, true)
199+
isa(stmt, ReturnNode) && return (false, true)
200+
isa(stmt, GotoNode) && return (false, true)
201+
isa(stmt, GotoIfNot) && return (false, argextype(stmt.cond, src) Bool)
202+
isa(stmt, Slot) && return (false, false) # Slots shouldn't occur in the IR at this point, but let's be defensive here
203+
if isa(stmt, GlobalRef)
204+
nothrow = isdefined(stmt.mod, stmt.name)
205+
return (nothrow, nothrow)
206+
end
203207
if isa(stmt, Expr)
204208
(; head, args) = stmt
205209
if head === :static_parameter
206210
etyp = (isa(src, IRCode) ? src.sptypes : src.ir.sptypes)[args[1]::Int]
207211
# if we aren't certain enough about the type, it might be an UndefVarError at runtime
208-
return isa(etyp, Const)
212+
nothrow = isa(etyp, Const)
213+
return (nothrow, nothrow)
209214
end
210215
if head === :call
211216
f = argextype(args[1], src)
212217
f = singleton_type(f)
213-
f === nothing && return false
218+
f === nothing && return (false, false)
214219
if isa(f, IntrinsicFunction)
215-
intrinsic_effect_free_if_nothrow(f) || return false
216-
return intrinsic_nothrow(f,
217-
Any[argextype(args[i], src) for i = 2:length(args)])
220+
nothrow = intrinsic_nothrow(f,
221+
Any[argextype(args[i], src) for i = 2:length(args)])
222+
nothrow || return (false, false)
223+
return (intrinsic_effect_free_if_nothrow(f), nothrow)
218224
end
219-
contains_is(_PURE_BUILTINS, f) && return true
225+
contains_is(_PURE_BUILTINS, f) && return (true, true)
220226
# `get_binding_type` sets the type to Any if the binding doesn't exist yet
221227
if f === Core.get_binding_type
222228
length(args) == 3 || return false
223229
M, s = argextype(args[2], src), argextype(args[3], src)
224-
return get_binding_type_effect_free(M, s)
230+
total = get_binding_type_effect_free(M, s)
231+
return (total, total)
225232
end
226-
contains_is(_EFFECT_FREE_BUILTINS, f) || return false
227-
rt === Bottom && return false
228-
return _builtin_nothrow(f, Any[argextype(args[i], src) for i = 2:length(args)], rt)
233+
rt === Bottom && return (false, false)
234+
nothrow = _builtin_nothrow(f, Any[argextype(args[i], src) for i = 2:length(args)], rt)
235+
nothrow || return (false, false)
236+
return (contains_is(_EFFECT_FREE_BUILTINS, f), nothrow)
229237
elseif head === :new
230238
typ = argextype(args[1], src)
231239
# `Expr(:new)` of unknown type could raise arbitrary TypeError.
232240
typ, isexact = instanceof_tfunc(typ)
233-
isexact || return false
234-
isconcretedispatch(typ) || return false
241+
isexact || return (false, false)
242+
isconcretedispatch(typ) || return (false, false)
235243
typ = typ::DataType
236-
fieldcount(typ) >= length(args) - 1 || return false
244+
fieldcount(typ) >= length(args) - 1 || return (false, false)
237245
for fld_idx in 1:(length(args) - 1)
238246
eT = argextype(args[fld_idx + 1], src)
239247
fT = fieldtype(typ, fld_idx)
240-
eT fT || return false
248+
eT fT || return (false, false)
241249
end
242-
return true
250+
return (true, true)
243251
elseif head === :foreigncall
244-
return foreigncall_effect_free(stmt, src)
252+
total = foreigncall_effect_free(stmt, src)
253+
return (total, total)
245254
elseif head === :new_opaque_closure
246-
length(args) < 4 && return false
255+
length(args) < 4 && return (false, false)
247256
typ = argextype(args[1], src)
248257
typ, isexact = instanceof_tfunc(typ)
249-
isexact || return false
250-
typ Tuple || return false
258+
isexact || return (false, false)
259+
typ Tuple || return (false, false)
251260
rt_lb = argextype(args[2], src)
252261
rt_ub = argextype(args[3], src)
253262
src = argextype(args[4], src)
254263
if !(rt_lb Type && rt_ub Type && src Method)
255-
return false
264+
return (false, false)
256265
end
257-
return true
266+
return (true, true)
258267
elseif head === :isdefined || head === :the_exception || head === :copyast || head === :inbounds || head === :boundscheck
259-
return true
268+
return (true, true)
260269
else
261270
# e.g. :loopinfo
262-
return false
271+
return (false, false)
263272
end
264273
end
265-
return true
274+
return (true, true)
266275
end
267276

268277
function foreigncall_effect_free(stmt::Expr, src::Union{IRCode,IncrementalCompact})
@@ -421,7 +430,7 @@ function finish(interp::AbstractInterpreter, opt::OptimizationState,
421430
for i in 1:length(ir.stmts)
422431
node = ir.stmts[i]
423432
stmt = node[:inst]
424-
if stmt_affects_purity(stmt, ir) && !stmt_effect_free(stmt, node[:type], ir)
433+
if stmt_affects_purity(stmt, ir) && !stmt_effect_flags(stmt, node[:type], ir)[1]
425434
proven_pure = false
426435
break
427436
end

base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import Core.Compiler: # Core.Compiler specific definitions
3131
isbitstype, isexpr, is_meta_expr_head, println, widenconst, argextype, singleton_type,
3232
fieldcount_noerror, try_compute_field, try_compute_fieldidx, hasintersect, ,
3333
intrinsic_nothrow, array_builtin_common_typecheck, arrayset_typecheck,
34-
setfield!_nothrow, alloc_array_ndims, stmt_effect_free, check_effect_free!
34+
setfield!_nothrow, alloc_array_ndims, check_effect_free!
3535

3636
include(x) = _TOP_MOD.include(@__MODULE__, x)
3737
if _TOP_MOD === Core.Compiler
@@ -1333,7 +1333,7 @@ function escape_call!(astate::AnalysisState, pc::Int, args::Vector{Any})
13331333
return # ThrownEscape is already checked
13341334
else
13351335
# we escape statements with the `ThrownEscape` property using the effect-freeness
1336-
# computed by `stmt_effect_free` invoked within inlining
1336+
# computed by `stmt_effect_flags` invoked within inlining
13371337
# TODO throwness ≠ "effect-free-ness"
13381338
if is_effect_free(astate.ir, pc)
13391339
add_liveness_changes!(astate, pc, args, 2)

base/compiler/ssair/inlining.jl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,9 @@ function handle_single_case!(
935935
stmt.head = :invoke
936936
pushfirst!(stmt.args, case.invoke)
937937
if is_removable_if_unused(case.effects)
938-
ir[SSAValue(idx)][:flag] |= IR_FLAG_EFFECT_FREE
938+
ir[SSAValue(idx)][:flag] |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
939+
elseif is_nothrow(case.effects)
940+
ir[SSAValue(idx)][:flag] |= IR_FLAG_NOTHROW
939941
end
940942
elseif case === nothing
941943
# Do, well, nothing
@@ -1138,11 +1140,13 @@ end
11381140
# For primitives, we do that right here. For proper calls, we will
11391141
# discover this when we consult the caches.
11401142
function check_effect_free!(ir::IRCode, idx::Int, @nospecialize(stmt), @nospecialize(rt))
1141-
if stmt_effect_free(stmt, rt, ir)
1142-
ir.stmts[idx][:flag] |= IR_FLAG_EFFECT_FREE
1143-
return true
1143+
(total, nothrow) = stmt_effect_flags(stmt, rt, ir)
1144+
if total
1145+
ir.stmts[idx][:flag] |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
1146+
elseif nothrow
1147+
ir.stmts[idx][:flag] |= IR_FLAG_NOTHROW
11441148
end
1145-
return false
1149+
return total
11461150
end
11471151

11481152
# Handles all analysis and inlining of intrinsics and builtins. In particular,

base/compiler/ssair/ir.jl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ function setindex!(is::InstructionStream, newval::Instruction, idx::Int)
250250
is.flag[idx] = newval[:flag]
251251
return is
252252
end
253-
function setindex!(is::InstructionStream, newval::AnySSAValue, idx::Int)
253+
function setindex!(is::InstructionStream, newval::Union{AnySSAValue, Nothing}, idx::Int)
254254
is.inst[idx] = newval
255255
return is
256256
end
@@ -343,7 +343,7 @@ function getindex(x::IRCode, s::SSAValue)
343343
end
344344
end
345345

346-
function setindex!(x::IRCode, repl::Union{Instruction, AnySSAValue}, s::SSAValue)
346+
function setindex!(x::IRCode, repl::Union{Instruction, Nothing, AnySSAValue}, s::SSAValue)
347347
if s.id <= length(x.stmts)
348348
x.stmts[s.id] = repl
349349
else
@@ -509,8 +509,11 @@ function insert_node!(ir::IRCode, pos::Int, inst::NewInstruction, attach_after::
509509
node[:line] = something(inst.line, ir.stmts[pos][:line])
510510
flag = inst.flag
511511
if !inst.effect_free_computed
512-
if stmt_effect_free(inst.stmt, inst.type, ir)
513-
flag |= IR_FLAG_EFFECT_FREE
512+
(effect_free_and_nothrow, nothrow) = stmt_effect_flags(inst.stmt, inst.type, ir)
513+
if effect_free_and_nothrow
514+
flag |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
515+
elseif nothrow
516+
flag |= IR_FLAG_NOTHROW
514517
end
515518
end
516519
node[:inst], node[:type], node[:flag] = inst.stmt, inst.type, flag
@@ -830,8 +833,13 @@ function insert_node_here!(compact::IncrementalCompact, inst::NewInstruction, re
830833
resize!(compact, result_idx)
831834
end
832835
flag = inst.flag
833-
if !inst.effect_free_computed && stmt_effect_free(inst.stmt, inst.type, compact)
834-
flag |= IR_FLAG_EFFECT_FREE
836+
if !inst.effect_free_computed
837+
(effect_free_and_nothrow, nothrow) = stmt_effect_flags(inst.stmt, inst.type, compact)
838+
if effect_free_and_nothrow
839+
flag |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW
840+
elseif nothrow
841+
flag |= IR_FLAG_NOTHROW
842+
end
835843
end
836844
node = compact.result[result_idx]
837845
node[:inst], node[:type], node[:line], node[:flag] = inst.stmt, inst.type, inst.line, flag

base/compiler/tfuncs.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,10 @@ function _builtin_nothrow(@nospecialize(f), argtypes::Array{Any,1}, @nospecializ
17581758
return argtypes[1] Module && argtypes[2] Symbol
17591759
elseif f === donotdelete
17601760
return true
1761+
elseif f === Core.finalizer
1762+
2 <= length(argtypes) <= 4 || return false
1763+
# Core.finalizer does no error checking - that's done in Base.finalizer
1764+
return true
17611765
end
17621766
return false
17631767
end

0 commit comments

Comments
 (0)