@@ -394,7 +394,7 @@ Analyze `src` and determine the chain of dependencies.
394
394
- `edges.preds[i]` lists the preceding statements that statement `i` depends on.
395
395
- `edges.succs[i]` lists the succeeding statements that depend on statement `i`.
396
396
- `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 `.
398
398
"""
399
399
function CodeEdges (src:: CodeInfo )
400
400
src. inferred && error (" supply lowered but not inferred code" )
@@ -557,7 +557,7 @@ function terminal_preds(i::Int, edges::CodeEdges)
557
557
end
558
558
559
559
"""
560
- isrequired = lines_required(obj::Union{Symbol,GlobalRef} , src::CodeInfo, edges::CodeEdges)
560
+ isrequired = lines_required(obj::$NamedVar , src::CodeInfo, edges::CodeEdges)
561
561
isrequired = lines_required(idx::Int, src::CodeInfo, edges::CodeEdges)
562
562
563
563
Determine 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
567
567
568
568
See also [`lines_required!`](@ref) and [`selective_eval!`](@ref).
569
569
"""
570
- function lines_required (obj:: Union{Symbol,GlobalRef} , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
570
+ function lines_required (obj:: NamedVar , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
571
571
isrequired = falses (length (edges. preds))
572
- objs = Set {Union{Symbol,GlobalRef} } ([obj])
572
+ objs = Set {NamedVar } ([obj])
573
573
return lines_required! (isrequired, objs, src, edges; kwargs... )
574
574
end
575
575
576
576
function lines_required (idx:: Int , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
577
577
isrequired = falses (length (edges. preds))
578
578
isrequired[idx] = true
579
- objs = Set {Union{Symbol,GlobalRef} } ()
579
+ objs = Set {NamedVar } ()
580
580
return lines_required! (isrequired, objs, src, edges; kwargs... )
581
581
end
582
582
@@ -594,7 +594,7 @@ For example, use `norequire = LoweredCodeUtils.exclude_named_typedefs(src, edges
594
594
extracting method signatures and not evaluating new definitions.
595
595
"""
596
596
function lines_required! (isrequired:: AbstractVector{Bool} , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
597
- objs = Set {Union{Symbol,GlobalRef} } ()
597
+ objs = Set {NamedVar } ()
598
598
return lines_required! (isrequired, objs, src, edges; kwargs... )
599
599
end
600
600
@@ -616,143 +616,68 @@ function exclude_named_typedefs(src::CodeInfo, edges::CodeEdges)
616
616
end
617
617
618
618
function 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
-
644
619
# 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)
650
621
651
622
# 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
654
624
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
656
631
iter = 0
657
632
while changed
658
633
changed = false
659
634
660
635
# 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)
666
637
667
638
# 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)
674
640
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)
705
646
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
751
647
iter += 1 # just for diagnostics
752
648
end
753
649
return isrequired
754
650
end
755
651
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
+
756
681
function add_preds! (isrequired, idx, edges:: CodeEdges , norequire)
757
682
chngd = false
758
683
preds = edges. preds[idx]
@@ -788,6 +713,118 @@ function add_obj!(isrequired, objs, obj, edges::CodeEdges, norequire)
788
713
return chngd
789
714
end
790
715
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
+
791
828
"""
792
829
selective_eval!([recurse], frame::Frame, isrequired::AbstractVector{Bool}, istoplevel=false)
793
830
0 commit comments