@@ -188,81 +188,90 @@ function stmt_affects_purity(@nospecialize(stmt), ir)
188188end
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 )
266275end
267276
268277function 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
0 commit comments