@@ -394,7 +394,7 @@ Analyze `src` and determine the chain of dependencies.
394394- `edges.preds[i]` lists the preceding statements that statement `i` depends on.
395395- `edges.succs[i]` lists the succeeding statements that depend on statement `i`.
396396- `edges.byname[v]` returns information about the predecessors, successors, and assignment statements
397- for an object `v::Union{Symbol,GlobalRef} `.
397+ for an object `v::$NamedVar `.
398398"""
399399function CodeEdges (src:: CodeInfo )
400400 src. inferred && error (" supply lowered but not inferred code" )
@@ -557,7 +557,7 @@ function terminal_preds(i::Int, edges::CodeEdges)
557557end
558558
559559"""
560- isrequired = lines_required(obj::Union{Symbol,GlobalRef} , src::CodeInfo, edges::CodeEdges)
560+ isrequired = lines_required(obj::$NamedVar , src::CodeInfo, edges::CodeEdges)
561561 isrequired = lines_required(idx::Int, src::CodeInfo, edges::CodeEdges)
562562
563563Determine which lines might need to be executed to evaluate `obj` or the statement indexed by `idx`.
@@ -567,16 +567,16 @@ will end up skipping a subset of such statements, perhaps while repeating others
567567
568568See also [`lines_required!`](@ref) and [`selective_eval!`](@ref).
569569"""
570- function lines_required (obj:: Union{Symbol,GlobalRef} , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
570+ function lines_required (obj:: NamedVar , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
571571 isrequired = falses (length (edges. preds))
572- objs = Set {Union{Symbol,GlobalRef} } ([obj])
572+ objs = Set {NamedVar } ([obj])
573573 return lines_required! (isrequired, objs, src, edges; kwargs... )
574574end
575575
576576function lines_required (idx:: Int , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
577577 isrequired = falses (length (edges. preds))
578578 isrequired[idx] = true
579- objs = Set {Union{Symbol,GlobalRef} } ()
579+ objs = Set {NamedVar } ()
580580 return lines_required! (isrequired, objs, src, edges; kwargs... )
581581end
582582
@@ -594,7 +594,7 @@ For example, use `norequire = LoweredCodeUtils.exclude_named_typedefs(src, edges
594594extracting method signatures and not evaluating new definitions.
595595"""
596596function lines_required! (isrequired:: AbstractVector{Bool} , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
597- objs = Set {Union{Symbol,GlobalRef} } ()
597+ objs = Set {NamedVar } ()
598598 return lines_required! (isrequired, objs, src, edges; kwargs... )
599599end
600600
@@ -616,143 +616,68 @@ function exclude_named_typedefs(src::CodeInfo, edges::CodeEdges)
616616end
617617
618618function lines_required! (isrequired:: AbstractVector{Bool} , objs, src:: CodeInfo , edges:: CodeEdges ; norequire = ())
619- # Do a traveral of "numbered" predecessors
620- # We'll mostly use generic graph traversal to discover all the lines we need,
621- # but structs are in a bit of a different category (especially on Julia 1.5+).
622- # It's easiest to discover these at the beginning.
623- typedef_blocks, typedef_names = UnitRange{Int}[], Symbol[]
624- i = 1
625- nstmts = length (src. code)
626- while i <= nstmts
627- stmt = rhs (src. code[i])
628- if istypedef (stmt) && ! isanonymous_typedef (stmt:: Expr )
629- stmt = stmt:: Expr
630- r = typedef_range (src, i)
631- push! (typedef_blocks, r)
632- name = stmt. head === :call ? stmt. args[3 ] : stmt. args[1 ]
633- if isa (name, QuoteNode)
634- name = name. value
635- end
636- isa (name, Symbol) || @show src i r stmt
637- push! (typedef_names, name:: Symbol )
638- i = last (r)+ 1
639- else
640- i += 1
641- end
642- end
643-
644619 # Mark any requested objects (their lines of assignment)
645- objsnew = Set {Union{Symbol,GlobalRef}} ()
646- for obj in objs
647- add_obj! (isrequired, objsnew, obj, edges, norequire)
648- end
649- objs = objsnew
620+ objs = add_requests! (isrequired, objs, edges, norequire)
650621
651622 # Compute basic blocks, which we'll use to make sure we mark necessary control-flow
652- bbs = Core. Compiler. compute_basic_blocks (src. code) # needed for control-flow analysis
653- nblocks = length (bbs. blocks)
623+ cfg = Core. Compiler. compute_basic_blocks (src. code) # needed for control-flow analysis
654624
655- changed:: Bool = true
625+ # We'll mostly use generic graph traversal to discover all the lines we need,
626+ # but structs are in a bit of a different category (especially on Julia 1.5+).
627+ # It's easiest to discover these at the beginning.
628+ typedefs = find_typedefs (src)
629+
630+ changed = true
656631 iter = 0
657632 while changed
658633 changed = false
659634
660635 # Handle ssa predecessors
661- for idx = 1 : nstmts
662- if isrequired[idx]
663- changed |= add_preds! (isrequired, idx, edges, norequire)
664- end
665- end
636+ changed |= add_ssa_preds! (isrequired, src, edges, norequire)
666637
667638 # Handle named dependencies
668- for (obj, uses) in edges. byname
669- obj ∈ objs && continue
670- if any (view (isrequired, uses. succs))
671- changed |= add_obj! (isrequired, objs, obj, edges, norequire)
672- end
673- end
639+ changed |= add_named_dependencies! (isrequired, edges, objs, norequire)
674640
675- # Add control-flow. For any basic block with an evaluated statement inside it,
676- # check to see if the block has any successors, and if so mark that block's exit statement.
677- # Likewise, any preceding blocks should have *their* exit statement marked.
678- for (ibb, bb) in enumerate (bbs. blocks)
679- r = rng (bb)
680- if any (view (isrequired, r))
681- if ibb != nblocks
682- idxlast = r[end ]
683- idxlast ∈ norequire && continue
684- changed |= ! isrequired[idxlast]
685- isrequired[idxlast] = true
686- end
687- for ibbp in bb. preds
688- ibbp > 0 || continue # see Core.Compiler.compute_basic_blocks, near comment re :enter
689- rpred = rng (bbs. blocks[ibbp])
690- idxlast = rpred[end ]
691- idxlast ∈ norequire && continue
692- changed |= ! isrequired[idxlast]
693- isrequired[idxlast] = true
694- end
695- for ibbs in bb. succs
696- ibbs == nblocks && continue
697- rpred = rng (bbs. blocks[ibbs])
698- idxlast = rpred[end ]
699- idxlast ∈ norequire && continue
700- changed |= ! isrequired[idxlast]
701- isrequired[idxlast] = true
702- end
703- end
704- end
641+ # Add control-flow
642+ changed |= add_control_flow! (isrequired, cfg, norequire)
643+
644+ # So far, everything is generic graph traversal. Now we add some domain-specific information
645+ changed |= add_typedefs! (isrequired, src, edges, typedefs, norequire)
705646
706- # So far, everything is generic graph traversal. Now we add some domain-specific information.
707- # New struct definitions, including their constructors, get spread out over many
708- # statements. If we're evaluating any of them, it's important to evaluate *all* of them.
709- idx = 1
710- while idx < length (src. code)
711- stmt = src. code[idx]
712- isrequired[idx] || (idx += 1 ; continue )
713- for (typedefr, typedefn) in zip (typedef_blocks, typedef_names)
714- if idx ∈ typedefr
715- ireq = view (isrequired, typedefr)
716- if ! all (ireq)
717- changed = true
718- ireq .= true
719- # Also mark any by-type constructor(s) associated with this typedef
720- var = get (edges. byname, typedefn, nothing )
721- if var != = nothing
722- for s in var. succs
723- s ∈ norequire && continue
724- stmt2 = src. code[s]
725- if isexpr (stmt2, :method ) && (fname = (stmt2:: Expr ). args[1 ]; fname === false || fname === nothing )
726- isrequired[s] = true
727- end
728- end
729- end
730- end
731- idx = last (typedefr) + 1
732- continue
733- end
734- end
735- # Anonymous functions may not yet include the method definition
736- if isanonymous_typedef (stmt)
737- i = idx + 1
738- while i <= length (src. code) && ! ismethod3 (src. code[i])
739- i += 1
740- end
741- if i <= length (src. code) && (src. code[i]:: Expr ). args[1 ] == false
742- tpreds = terminal_preds (i, edges)
743- if minimum (tpreds) == idx && i ∉ norequire
744- changed |= ! isrequired[i]
745- isrequired[i] = true
746- end
747- end
748- end
749- idx += 1
750- end
751647 iter += 1 # just for diagnostics
752648 end
753649 return isrequired
754650end
755651
652+ function add_requests! (isrequired, objs, edges:: CodeEdges , norequire)
653+ objsnew = Set {NamedVar} ()
654+ for obj in objs
655+ add_obj! (isrequired, objsnew, obj, edges, norequire)
656+ end
657+ return objsnew
658+ end
659+
660+ function add_ssa_preds! (isrequired, src:: CodeInfo , edges:: CodeEdges , norequire)
661+ changed = false
662+ for idx = 1 : length (src. code)
663+ if isrequired[idx]
664+ changed |= add_preds! (isrequired, idx, edges, norequire)
665+ end
666+ end
667+ return changed
668+ end
669+
670+ function add_named_dependencies! (isrequired, edges:: CodeEdges , objs, norequire)
671+ changed = false
672+ for (obj, uses) in edges. byname
673+ obj ∈ objs && continue
674+ if any (view (isrequired, uses. succs))
675+ changed |= add_obj! (isrequired, objs, obj, edges, norequire)
676+ end
677+ end
678+ return changed
679+ end
680+
756681function add_preds! (isrequired, idx, edges:: CodeEdges , norequire)
757682 chngd = false
758683 preds = edges. preds[idx]
@@ -788,6 +713,118 @@ function add_obj!(isrequired, objs, obj, edges::CodeEdges, norequire)
788713 return chngd
789714end
790715
716+ # Add control-flow. For any basic block with an evaluated statement inside it,
717+ # check to see if the block has any successors, and if so mark that block's exit statement.
718+ # Likewise, any preceding blocks should have *their* exit statement marked.
719+ function add_control_flow! (isrequired, cfg, norequire)
720+ changed = false
721+ blocks = cfg. blocks
722+ nblocks = length (blocks)
723+ for (ibb, bb) in enumerate (blocks)
724+ r = rng (bb)
725+ if any (view (isrequired, r))
726+ if ibb != nblocks
727+ idxlast = r[end ]
728+ idxlast ∈ norequire && continue
729+ changed |= ! isrequired[idxlast]
730+ isrequired[idxlast] = true
731+ end
732+ for ibbp in bb. preds
733+ ibbp > 0 || continue # see Core.Compiler.compute_basic_blocks, near comment re :enter
734+ rpred = rng (blocks[ibbp])
735+ idxlast = rpred[end ]
736+ idxlast ∈ norequire && continue
737+ changed |= ! isrequired[idxlast]
738+ isrequired[idxlast] = true
739+ end
740+ for ibbs in bb. succs
741+ ibbs == nblocks && continue
742+ rpred = rng (blocks[ibbs])
743+ idxlast = rpred[end ]
744+ idxlast ∈ norequire && continue
745+ changed |= ! isrequired[idxlast]
746+ isrequired[idxlast] = true
747+ end
748+ end
749+ end
750+ return changed
751+ end
752+
753+ # Do a traveral of "numbered" predecessors and find statement ranges and names of type definitions
754+ function find_typedefs (src:: CodeInfo )
755+ typedef_blocks, typedef_names = UnitRange{Int}[], Symbol[]
756+ i = 1
757+ nstmts = length (src. code)
758+ while i <= nstmts
759+ stmt = rhs (src. code[i])
760+ if istypedef (stmt) && ! isanonymous_typedef (stmt:: Expr )
761+ stmt = stmt:: Expr
762+ r = typedef_range (src, i)
763+ push! (typedef_blocks, r)
764+ name = stmt. head === :call ? stmt. args[3 ] : stmt. args[1 ]
765+ if isa (name, QuoteNode)
766+ name = name. value
767+ end
768+ isa (name, Symbol) || @show src i r stmt
769+ push! (typedef_names, name:: Symbol )
770+ i = last (r)+ 1
771+ else
772+ i += 1
773+ end
774+ end
775+ return typedef_blocks, typedef_names
776+ end
777+
778+ # New struct definitions, including their constructors, get spread out over many
779+ # statements. If we're evaluating any of them, it's important to evaluate *all* of them.
780+ function add_typedefs! (isrequired, src:: CodeInfo , edges:: CodeEdges , (typedef_blocks, typedef_names), norequire)
781+ changed = false
782+ stmts = src. code
783+ idx = 1
784+ while idx < length (stmts)
785+ stmt = stmts[idx]
786+ isrequired[idx] || (idx += 1 ; continue )
787+ for (typedefr, typedefn) in zip (typedef_blocks, typedef_names)
788+ if idx ∈ typedefr
789+ ireq = view (isrequired, typedefr)
790+ if ! all (ireq)
791+ changed = true
792+ ireq .= true
793+ # Also mark any by-type constructor(s) associated with this typedef
794+ var = get (edges. byname, typedefn, nothing )
795+ if var != = nothing
796+ for s in var. succs
797+ s ∈ norequire && continue
798+ stmt2 = stmts[s]
799+ if isexpr (stmt2, :method ) && (fname = (stmt2:: Expr ). args[1 ]; fname === false || fname === nothing )
800+ isrequired[s] = true
801+ end
802+ end
803+ end
804+ end
805+ idx = last (typedefr) + 1
806+ continue
807+ end
808+ end
809+ # Anonymous functions may not yet include the method definition
810+ if isanonymous_typedef (stmt)
811+ i = idx + 1
812+ while i <= length (stmts) && ! ismethod3 (stmts[i])
813+ i += 1
814+ end
815+ if i <= length (stmts) && (stmts[i]:: Expr ). args[1 ] == false
816+ tpreds = terminal_preds (i, edges)
817+ if minimum (tpreds) == idx && i ∉ norequire
818+ changed |= ! isrequired[i]
819+ isrequired[i] = true
820+ end
821+ end
822+ end
823+ idx += 1
824+ end
825+ return changed
826+ end
827+
791828"""
792829 selective_eval!([recurse], frame::Frame, isrequired::AbstractVector{Bool}, istoplevel=false)
793830
0 commit comments