@@ -188,81 +188,90 @@ function stmt_affects_purity(@nospecialize(stmt), ir)
188
188
end
189
189
190
190
"""
191
- stmt_effect_free (stmt, rt, src::Union{IRCode,IncrementalCompact})
191
+ stmt_effect_flags (stmt, rt, src::Union{IRCode,IncrementalCompact})
192
192
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 .
194
194
"""
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
203
207
if isa (stmt, Expr)
204
208
(; head, args) = stmt
205
209
if head === :static_parameter
206
210
etyp = (isa (src, IRCode) ? src. sptypes : src. ir. sptypes)[args[1 ]:: Int ]
207
211
# 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)
209
214
end
210
215
if head === :call
211
216
f = argextype (args[1 ], src)
212
217
f = singleton_type (f)
213
- f === nothing && return false
218
+ f === nothing && return ( false , false )
214
219
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)
218
224
end
219
- contains_is (_PURE_BUILTINS, f) && return true
225
+ contains_is (_PURE_BUILTINS, f) && return ( true , true )
220
226
# `get_binding_type` sets the type to Any if the binding doesn't exist yet
221
227
if f === Core. get_binding_type
222
228
length (args) == 3 || return false
223
229
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)
225
232
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)
229
237
elseif head === :new
230
238
typ = argextype (args[1 ], src)
231
239
# `Expr(:new)` of unknown type could raise arbitrary TypeError.
232
240
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 )
235
243
typ = typ:: DataType
236
- fieldcount (typ) >= length (args) - 1 || return false
244
+ fieldcount (typ) >= length (args) - 1 || return ( false , false )
237
245
for fld_idx in 1 : (length (args) - 1 )
238
246
eT = argextype (args[fld_idx + 1 ], src)
239
247
fT = fieldtype (typ, fld_idx)
240
- eT ⊑ fT || return false
248
+ eT ⊑ fT || return ( false , false )
241
249
end
242
- return true
250
+ return ( true , true )
243
251
elseif head === :foreigncall
244
- return foreigncall_effect_free (stmt, src)
252
+ total = foreigncall_effect_free (stmt, src)
253
+ return (total, total)
245
254
elseif head === :new_opaque_closure
246
- length (args) < 4 && return false
255
+ length (args) < 4 && return ( false , false )
247
256
typ = argextype (args[1 ], src)
248
257
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 )
251
260
rt_lb = argextype (args[2 ], src)
252
261
rt_ub = argextype (args[3 ], src)
253
262
src = argextype (args[4 ], src)
254
263
if ! (rt_lb ⊑ Type && rt_ub ⊑ Type && src ⊑ Method)
255
- return false
264
+ return ( false , false )
256
265
end
257
- return true
266
+ return ( true , true )
258
267
elseif head === :isdefined || head === :the_exception || head === :copyast || head === :inbounds || head === :boundscheck
259
- return true
268
+ return ( true , true )
260
269
else
261
270
# e.g. :loopinfo
262
- return false
271
+ return ( false , false )
263
272
end
264
273
end
265
- return true
274
+ return ( true , true )
266
275
end
267
276
268
277
function foreigncall_effect_free (stmt:: Expr , src:: Union{IRCode,IncrementalCompact} )
@@ -421,7 +430,7 @@ function finish(interp::AbstractInterpreter, opt::OptimizationState,
421
430
for i in 1 : length (ir. stmts)
422
431
node = ir. stmts[i]
423
432
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 ]
425
434
proven_pure = false
426
435
break
427
436
end
0 commit comments