@@ -472,44 +472,34 @@ function gc_bytes()
472472 b[]
473473end
474474
475- function allocated (f, args:: Vararg{Any,N} ; kwargs... ) where {N}
476- b0 = Ref {Int64} (0 )
477- b1 = Ref {Int64} (0 )
478- Base. gc_bytes (b0)
479- f (args... ; kwargs... )
480- Base. gc_bytes (b1)
481- return b1[] - b0[]
482- end
483- only (methods (allocated)). called = 0xff
484-
485- function allocations (f, args:: Vararg{Any,N} ; kwargs... ) where {N}
486- stats = Base. gc_num ()
487- f (args... ; kwargs... )
488- diff = Base. GC_Diff (Base. gc_num (), stats)
489- return Base. gc_alloc_count (diff)
490- end
491- only (methods (allocations)). called = 0xff
492-
493- function is_simply_call (@nospecialize ex, captures:: Vector{Symbol} )
494- Meta. isexpr (ex, :call ) || return false
495- function is_simple_arg (@nospecialize a)
496- a isa Symbol && return true
497- a isa GlobalRef && return true
498- is_self_quoting (a) && return true
499- is_quoted (a) && return true
500- if a isa Expr
501- a. head === :(kw) && return is_simple_arg (a. args[2 ])
502- a. head === :(... ) && return is_simple_arg (a. args[1 ])
503- a. head === :parameters && return all (is_simple_arg, a. args)
504- end
475+ # get a list of all possible captures and globals from ex into vars
476+ # return false if that list would change the expression meaning too much
477+ function try_get_vars! (@nospecialize (ex), vars:: Vector{Symbol} )
478+ ex isa Symbol && (ex in vars || push! (vars, ex))
479+ ex isa Expr || return true
480+ is_quoted (ex) && return true
481+ if ex. head == :kw
482+ return try_get_vars! (ex. args[2 ], vars)
483+ end
484+ # An allow-list of all expression types that don't change meaning (much) if
485+ # we hoist their possible variable accesses into arguments, given that we
486+ # don't allow :(=) in this list. We do allow conditionals, even though that
487+ # means we could cause an UndefVar error to appear. We also hoist what may
488+ # be global accesses, even though that means we change the order of evaluation.
489+ if ex. head ∉ (:call , :kw , :parameters , :(... ), :., :var"'" , :tuple , :ref ,
490+ :vect , :hcat , :vcat , :ncat , :row , :nrow , :block , :if , :&& , :|| , :?,
491+ :let , :try , :catch , :finally , :global , :local )
505492 return false
506493 end
507494 for a in ex. args
508- is_simple_arg (a ) || return false
495+ try_get_vars! (a, vars ) || return false
509496 end
510497 return true
511498end
512499
500+ const b0 = gensym (" b0" )
501+ const b1 = gensym (" b1" )
502+
513503"""
514504 @allocated
515505
@@ -526,22 +516,23 @@ julia> @allocated rand(10^6)
526516"""
527517macro allocated (ex)
528518 vars = Symbol[]
529- try_get_captures! (ex, vars) || empty! (vars)
519+ try_get_vars! (ex, vars) || empty! (vars)
520+ # ex = esc(ex)
530521 body = quote
531- b0 = Ref {Int64} (0 )
532- b1 = Ref {Int64} (0 )
533- gc_bytes (b0)
534- $ ( esc (ex))
535- gc_bytes (b1)
536- return b1[] - b0[]
522+ local $ b0 = $ Ref {$ Int64} (0 )
523+ local $ b1 = $ Ref {$ Int64} (0 )
524+ $ gc_bytes ($ b0)
525+ $ ex
526+ $ gc_bytes ($ b1)
527+ return $ - ( $ getindex ( $ b1), $ getindex ( $ b0))
537528 end
538- pushfirst! (ex . args, GlobalRef (Base, :allocated ))
539- ws = map (i -> Symbol ( " _ $i " ), 1 : length (vars))
540- vars = map (esc, vars)
529+ # add "wheres" to disable de-specialization heuristics
530+ ws = map (i -> gensym ( ), 1 : length (vars)) # Symbol("_$i" )
531+ # vars = map(esc, vars)
541532 args = map ((v, w) -> :($ v:: $w ), vars, ws)
542533 sig = :( ($ (args... ),) where {$ (ws... )} )
543534 ex = remove_linenums! (:(($ sig -> $ body)($ (vars... ))))
544- return ex
535+ return esc (ex) # manual hygiene to work around macroexpand bugs
545536end
546537
547538"""
@@ -562,13 +553,21 @@ julia> @allocations rand(10^6)
562553 This macro was added in Julia 1.9.
563554"""
564555macro allocations (ex)
565- if ! is_simply_call (ex)
566- ex = :((() -> $ ex)())
567- else
568- length (ex. args) >= 2 && isexpr (ex. args[2 ], :parameters ) && ((ex. args[1 ], ex. args[2 ]) = (ex. args[2 ], ex. args[1 ]))
556+ vars = Symbol[]
557+ try_get_vars! (ex, vars) || empty! (vars)
558+ # ex = esc(ex)
559+ body = quote
560+ local $ b0 = $ gc_num ()
561+ $ ex
562+ return $ gc_alloc_count ($ GC_Diff ($ gc_num (), $ b0))
569563 end
570- pushfirst! (ex. args, GlobalRef (Base, :allocations ))
571- return esc (ex)
564+ # add "wheres" to disable de-specialization heuristics
565+ ws = map (i -> gensym (), 1 : length (vars)) # Symbol("_$i")
566+ # vars = map(esc, vars)
567+ args = map ((v, w) -> :($ v:: $w ), vars, ws)
568+ sig = :( ($ (args... ),) where {$ (ws... )} )
569+ ex = remove_linenums! (:(($ sig -> $ body)($ (vars... ))))
570+ return esc (ex) # manual hygiene to work around macroexpand bugs
572571end
573572
574573
0 commit comments