@@ -577,34 +577,52 @@ function lines_required(idx::Int, src::CodeInfo, edges::CodeEdges; kwargs...)
577
577
isrequired = falses (length (edges. preds))
578
578
isrequired[idx] = true
579
579
objs = Set {Union{Symbol,GlobalRef}} ()
580
- return lines_required! (isrequired, src, edges; kwargs... )
580
+ return lines_required! (isrequired, objs, src, edges; kwargs... )
581
581
end
582
582
583
583
"""
584
- lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges; exclude_named_typedefs::Bool=false)
584
+ lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges;
585
+ norequire = ())
585
586
586
587
Like `lines_required`, but where `isrequired[idx]` has already been set to `true` for all statements
587
588
that you know you need to evaluate. All other statements should be marked `false` at entry.
588
589
On return, the complete set of required statements will be marked `true`.
589
590
590
- Use `exclude_named_typedefs=true` if you're extracting method signatures and not evaluating new definitions.
591
+ `norequire` keyword argument specifies statements (represented as iterator of `Int`s) that
592
+ should _not_ be marked as a requirement.
593
+ For example, use `norequire = LoweredCodeUtils.exclude_named_typedefs(src, edges)` if you're
594
+ extracting method signatures and not evaluating new definitions.
591
595
"""
592
596
function lines_required! (isrequired:: AbstractVector{Bool} , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
593
597
objs = Set {Union{Symbol,GlobalRef}} ()
594
598
return lines_required! (isrequired, objs, src, edges; kwargs... )
595
599
end
596
600
597
- function lines_required! (isrequired:: AbstractVector{Bool} , objs, src:: CodeInfo , edges:: CodeEdges ; exclude_named_typedefs:: Bool = false )
601
+ function exclude_named_typedefs (src:: CodeInfo , edges:: CodeEdges )
602
+ norequire = BitSet ()
603
+ i = 1
604
+ nstmts = length (src. code)
605
+ while i <= nstmts
606
+ stmt = rhs (src. code[i])
607
+ if istypedef (stmt) && ! isanonymous_typedef (stmt:: Expr )
608
+ r = typedef_range (src, i)
609
+ pushall! (norequire, r)
610
+ i = last (r)+ 1
611
+ else
612
+ i += 1
613
+ end
614
+ end
615
+ return norequire
616
+ end
617
+
618
+ function lines_required! (isrequired:: AbstractVector{Bool} , objs, src:: CodeInfo , edges:: CodeEdges ; norequire = ())
598
619
# Do a traveral of "numbered" predecessors
599
620
# We'll mostly use generic graph traversal to discover all the lines we need,
600
621
# but structs are in a bit of a different category (especially on Julia 1.5+).
601
622
# It's easiest to discover these at the beginning.
602
- # Moreover, if we're excluding named type definitions, we'll add them to `norequire`
603
- # to prevent them from being marked.
604
623
typedef_blocks, typedef_names = UnitRange{Int}[], Symbol[]
605
- norequire = BitSet ()
606
- nstmts = length (src. code)
607
624
i = 1
625
+ nstmts = length (src. code)
608
626
while i <= nstmts
609
627
stmt = rhs (src. code[i])
610
628
if istypedef (stmt) && ! isanonymous_typedef (stmt:: Expr )
@@ -618,9 +636,6 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
618
636
isa (name, Symbol) || @show src i r stmt
619
637
push! (typedef_names, name:: Symbol )
620
638
i = last (r)+ 1
621
- if exclude_named_typedefs && ! isanonymous_typedef (stmt)
622
- pushall! (norequire, r)
623
- end
624
639
else
625
640
i += 1
626
641
end
@@ -641,19 +656,22 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
641
656
iter = 0
642
657
while changed
643
658
changed = false
659
+
644
660
# Handle ssa predecessors
645
661
for idx = 1 : nstmts
646
662
if isrequired[idx]
647
663
changed |= add_preds! (isrequired, idx, edges, norequire)
648
664
end
649
665
end
666
+
650
667
# Handle named dependencies
651
668
for (obj, uses) in edges. byname
652
669
obj ∈ objs && continue
653
670
if any (view (isrequired, uses. succs))
654
671
changed |= add_obj! (isrequired, objs, obj, edges, norequire)
655
672
end
656
673
end
674
+
657
675
# Add control-flow. For any basic block with an evaluated statement inside it,
658
676
# check to see if the block has any successors, and if so mark that block's exit statement.
659
677
# Likewise, any preceding blocks should have *their* exit statement marked.
@@ -684,6 +702,7 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
684
702
end
685
703
end
686
704
end
705
+
687
706
# So far, everything is generic graph traversal. Now we add some domain-specific information.
688
707
# New struct definitions, including their constructors, get spread out over many
689
708
# statements. If we're evaluating any of them, it's important to evaluate *all* of them.
@@ -833,7 +852,7 @@ Mark each line of code with its requirement status.
833
852
function print_with_code (io:: IO , src:: CodeInfo , isrequired:: AbstractVector{Bool} )
834
853
nd = ndigits (length (isrequired))
835
854
preprint (:: IO ) = nothing
836
- preprint (io:: IO , idx:: Int ) = print ( io, lpad (idx, nd), ' ' , isrequired[idx] ? " t " : " f " )
855
+ preprint (io:: IO , idx:: Int ) = (c = isrequired[idx]; printstyled ( io, lpad (idx, nd), ' ' , c ? " t " : " f " ; color = c ? :cyan : :plain ) )
837
856
postprint (:: IO ) = nothing
838
857
postprint (io:: IO , idx:: Int , bbchanged:: Bool ) = nothing
839
858
0 commit comments