@@ -391,12 +391,6 @@ function CodeEdges(src::CodeInfo)
391
391
end
392
392
393
393
function CodeEdges (src:: CodeInfo , cl:: CodeLinks )
394
- function pushall! (dest, src)
395
- for item in src
396
- push! (dest, item)
397
- end
398
- return dest
399
- end
400
394
# The main task here is to elide the slot-dependencies and convert
401
395
# everything to just ssas & names.
402
396
# Hence we "follow" slot links to their non-slot leaves.
@@ -560,86 +554,125 @@ will end up skipping a subset of such statements, perhaps while repeating others
560
554
561
555
See also [`lines_required!`](@ref) and [`selective_eval!`](@ref).
562
556
"""
563
- function lines_required (obj:: Union{Symbol,GlobalRef} , src:: CodeInfo , edges:: CodeEdges )
557
+ function lines_required (obj:: Union{Symbol,GlobalRef} , src:: CodeInfo , edges:: CodeEdges ; kwargs ... )
564
558
isrequired = falses (length (edges. preds))
565
559
objs = Set {Union{Symbol,GlobalRef}} ([obj])
566
- return lines_required! (isrequired, objs, src, edges)
560
+ return lines_required! (isrequired, objs, src, edges; kwargs ... )
567
561
end
568
562
569
- function lines_required (idx:: Int , src:: CodeInfo , edges:: CodeEdges )
563
+ function lines_required (idx:: Int , src:: CodeInfo , edges:: CodeEdges ; kwargs ... )
570
564
isrequired = falses (length (edges. preds))
571
565
isrequired[idx] = true
572
566
objs = Set {Union{Symbol,GlobalRef}} ()
573
- return lines_required! (isrequired, src, edges)
567
+ return lines_required! (isrequired, src, edges; kwargs ... )
574
568
end
575
569
576
570
"""
577
- lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges)
571
+ lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges; exclude_named_typedefs::Bool=false )
578
572
579
573
Like `lines_required`, but where `isrequired[idx]` has already been set to `true` for all statements
580
574
that you know you need to evaluate. All other statements should be marked `false` at entry.
581
575
On return, the complete set of required statements will be marked `true`.
576
+
577
+ Use `exclude_named_typedefs=true` if you're extracting method signatures and not evaluating new definitions.
582
578
"""
583
- function lines_required! (isrequired:: AbstractVector{Bool} , src:: CodeInfo , edges:: CodeEdges )
579
+ function lines_required! (isrequired:: AbstractVector{Bool} , src:: CodeInfo , edges:: CodeEdges ; kwargs ... )
584
580
objs = Set {Union{Symbol,GlobalRef}} ()
585
- return lines_required! (isrequired, objs, src, edges)
581
+ return lines_required! (isrequired, objs, src, edges; kwargs ... )
586
582
end
587
583
588
- function lines_required! (isrequired:: AbstractVector{Bool} , objs, src:: CodeInfo , edges:: CodeEdges )
584
+ function lines_required! (isrequired:: AbstractVector{Bool} , objs, src:: CodeInfo , edges:: CodeEdges ; exclude_named_typedefs :: Bool = false )
589
585
# Do a traveral of "numbered" predecessors
590
- function add_preds! (isrequired, idx, edges:: CodeEdges )
586
+ function add_preds! (isrequired, idx, edges:: CodeEdges , norequire )
591
587
changed = false
592
588
preds = edges. preds[idx]
593
589
for p in preds
594
590
isrequired[p] && continue
591
+ p ∈ norequire && continue
595
592
isrequired[p] = true
596
593
changed = true
597
- add_preds! (isrequired, p, edges)
594
+ add_preds! (isrequired, p, edges, norequire )
598
595
end
599
596
return changed
600
597
end
601
- function add_succs! (isrequired, idx, edges:: CodeEdges , succs)
598
+ function add_succs! (isrequired, idx, edges:: CodeEdges , succs, norequire )
602
599
changed = false
603
600
for p in succs
604
601
isrequired[p] && continue
602
+ p ∈ norequire && continue
605
603
isrequired[p] = true
606
604
changed = true
607
- add_succs! (isrequired, p, edges, edges. succs[p])
605
+ add_succs! (isrequired, p, edges, edges. succs[p], norequire )
608
606
end
609
607
return changed
610
608
end
611
- function add_obj! (isrequired, objs, obj, edges:: CodeEdges )
609
+ function add_obj! (isrequired, objs, obj, edges:: CodeEdges , norequire )
612
610
changed = false
613
611
for d in edges. byname[obj]. assigned
614
- isrequired[d] || add_preds! (isrequired, d, edges)
612
+ d ∈ norequire && continue
613
+ isrequired[d] || add_preds! (isrequired, d, edges, norequire)
615
614
isrequired[d] = true
616
615
changed = true
617
616
end
618
617
push! (objs, obj)
619
618
return changed
620
619
end
621
620
621
+ # We'll mostly use generic graph traversal to discover all the lines we need,
622
+ # but structs are in a bit of a different category (especially on Julia 1.5+).
623
+ # It's easiest to discover these at the beginning.
624
+ # Moreover, if we're excluding named type definitions, we'll add them to `norequire`
625
+ # to prevent them from being marked.
626
+ typedef_blocks, typedef_names = UnitRange{Int}[], Symbol[]
627
+ norequire = BitSet ()
628
+ nstmts = length (src. code)
629
+ i = 1
630
+ while i <= nstmts
631
+ stmt = rhs (src. code[i])
632
+ if istypedef (stmt) && ! isanonymous_typedef (stmt)
633
+ r = typedef_range (src, i)
634
+ push! (typedef_blocks, r)
635
+ name = stmt. head === :call ? stmt. args[3 ] : stmt. args[1 ]
636
+ if isa (name, QuoteNode)
637
+ name = name. value
638
+ end
639
+ isa (name, Symbol) || @show src i r stmt
640
+ push! (typedef_names, name:: Symbol )
641
+ i = last (r)+ 1
642
+ if exclude_named_typedefs && ! isanonymous_typedef (stmt)
643
+ pushall! (norequire, r)
644
+ end
645
+ else
646
+ i += 1
647
+ end
648
+ end
649
+
650
+ # Mark any requested objects (their lines of assignment)
622
651
objsnew = Set {Union{Symbol,GlobalRef}} ()
623
652
for obj in objs
624
- add_obj! (isrequired, objsnew, obj, edges)
653
+ add_obj! (isrequired, objsnew, obj, edges, norequire )
625
654
end
626
655
objs = objsnew
656
+
657
+ # Compute basic blocks, which we'll use to make sure we mark necessary control-flow
627
658
bbs = Core. Compiler. compute_basic_blocks (src. code) # needed for control-flow analysis
659
+ nblocks = length (bbs. blocks)
660
+
628
661
changed = true
629
662
iter = 0
630
663
while changed
631
664
changed = false
632
665
# Handle ssa predecessors
633
- for idx = 1 : length (isrequired)
666
+ for idx = 1 : nstmts
634
667
if isrequired[idx]
635
- changed |= add_preds! (isrequired, idx, edges)
668
+ changed |= add_preds! (isrequired, idx, edges, norequire )
636
669
end
637
670
end
638
671
# Handle named dependencies
639
672
for (obj, uses) in edges. byname
640
673
obj ∈ objs && continue
641
674
if any (view (isrequired, uses. succs))
642
- changed |= add_obj! (isrequired, objs, obj, edges)
675
+ changed |= add_obj! (isrequired, objs, obj, edges, norequire )
643
676
end
644
677
end
645
678
# Add control-flow. For any basic block with an evaluated statement inside it,
@@ -648,22 +681,24 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
648
681
for (ibb, bb) in enumerate (bbs. blocks)
649
682
r = rng (bb)
650
683
if any (view (isrequired, r))
651
- # if !isempty(bb.succs)
652
- if ibb != length (bbs. blocks)
684
+ if ibb != nblocks
653
685
idxlast = r[end ]
686
+ idxlast ∈ norequire && continue
654
687
changed |= ! isrequired[idxlast]
655
688
isrequired[idxlast] = true
656
689
end
657
690
for ibbp in bb. preds
658
691
rpred = rng (bbs. blocks[ibbp])
659
692
idxlast = rpred[end ]
693
+ idxlast ∈ norequire && continue
660
694
changed |= ! isrequired[idxlast]
661
695
isrequired[idxlast] = true
662
696
end
663
697
for ibbs in bb. succs
664
- ibbs == length (bbs . blocks) && continue
698
+ ibbs == nblocks && continue
665
699
rpred = rng (bbs. blocks[ibbs])
666
700
idxlast = rpred[end ]
701
+ idxlast ∈ norequire && continue
667
702
changed |= ! isrequired[idxlast]
668
703
isrequired[idxlast] = true
669
704
end
@@ -674,55 +709,35 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
674
709
# statements. If we're evaluating any of them, it's important to evaluate *all* of them.
675
710
for (idx, stmt) in enumerate (src. code)
676
711
isrequired[idx] || continue
677
- if isexpr (stmt, :(= ))
678
- stmt = stmt. args[2 ]
679
- end
680
- # Is this a struct definition?
681
- if (isa (stmt, Expr) && stmt. head ∈ structheads) || # < Julia 1.5
682
- (isexpr (stmt, :call ) && callee_matches (stmt. args[1 ], Core, :_structtype )) # >= Julia 1.5
683
- stmt = stmt:: Expr
684
- name = stmt. args[stmt. head === :call ? 3 : 1 ]
685
- if isa (name, QuoteNode)
686
- name = name. value
687
- end
688
- name = name:: NamedVar
689
- # Some lines we need have been marked as successors of this name
690
- for d in edges. byname[name]. succs
691
- stmt2 = src. code[d]
692
- if isa (stmt2, Expr)
693
- head = stmt2. head
694
- if head === :method || head === :global || head === :const
695
- changed |= ! isrequired[d]
696
- isrequired[d] = true
697
- end
698
- end
699
- # Julia 1.5+: others are successor of a slotnum->ssa load
700
- if isslotnum (stmt2)
701
- for s in edges. succs[d]
702
- stmt3 = src. code[s]
703
- if isexpr (stmt3, :call ) && (callee_matches (stmt3. args[1 ], Core, :_setsuper! ) ||
704
- callee_matches (stmt3. args[1 ], Core, :_typebody! ))
705
- changed |= ! isrequired[s]
706
- isrequired[s] = true
712
+ for (typedefr, typedefn) in zip (typedef_blocks, typedef_names)
713
+ if idx ∈ typedefr
714
+ ireq = view (isrequired, typedefr)
715
+ if ! all (ireq)
716
+ changed = true
717
+ ireq .= true
718
+ # Also mark any by-type constructor(s) associated with this typedef
719
+ var = get (edges. byname, typedefn, nothing )
720
+ if var != = nothing
721
+ for s in var. succs
722
+ s ∈ norequire && continue
723
+ stmt2 = src. code[s]
724
+ if isexpr (stmt2, :method ) && (stmt2:: Expr ). args[1 ] === false
725
+ isrequired[s] = true
726
+ end
707
727
end
708
728
end
709
729
end
710
- # Julia 1.5+: for non-parametric types, the Core._setsuper! call happens without the slotname->ssa load
711
- if isexpr (stmt2, :call ) && callee_matches ((stmt2:: Expr ). args[1 ], Core, :_setsuper! )
712
- changed |= ! isrequired[d]
713
- isrequired[d] = true
714
- end
715
730
end
716
731
end
717
732
# Anonymous functions may not yet include the method definition
718
- if isexpr (stmt, :thunk ) && isanonymous_typedef (stmt. args[ 1 ] )
733
+ if isanonymous_typedef (stmt)
719
734
i = idx + 1
720
735
while i <= length (src. code) && ! ismethod3 (src. code[i])
721
736
i += 1
722
737
end
723
738
if i <= length (src. code) && src. code[i]. args[1 ] == false
724
739
tpreds = terminal_preds (i, edges)
725
- if minimum (tpreds) == idx
740
+ if minimum (tpreds) == idx && i ∉ norequire
726
741
changed |= ! isrequired[i]
727
742
isrequired[i] = true
728
743
end
@@ -734,14 +749,6 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
734
749
return isrequired
735
750
end
736
751
737
- function callee_matches (f, mod, sym)
738
- is_global_ref (f, mod, sym) && return true
739
- if isdefined (mod, sym)
740
- is_quotenode (f, getfield (mod, sym)) && return true
741
- end
742
- return false
743
- end
744
-
745
752
"""
746
753
selective_eval!([recurse], frame::Frame, isrequired::AbstractVector{Bool}, istoplevel=false)
747
754
0 commit comments