@@ -577,34 +577,52 @@ function lines_required(idx::Int, src::CodeInfo, edges::CodeEdges; kwargs...)
577577 isrequired = falses (length (edges. preds))
578578 isrequired[idx] = true
579579 objs = Set {Union{Symbol,GlobalRef}} ()
580- return lines_required! (isrequired, src, edges; kwargs... )
580+ return lines_required! (isrequired, objs, src, edges; kwargs... )
581581end
582582
583583"""
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 = ())
585586
586587Like `lines_required`, but where `isrequired[idx]` has already been set to `true` for all statements
587588that you know you need to evaluate. All other statements should be marked `false` at entry.
588589On return, the complete set of required statements will be marked `true`.
589590
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.
591595"""
592596function lines_required! (isrequired:: AbstractVector{Bool} , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
593597 objs = Set {Union{Symbol,GlobalRef}} ()
594598 return lines_required! (isrequired, objs, src, edges; kwargs... )
595599end
596600
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 = ())
598619 # Do a traveral of "numbered" predecessors
599620 # We'll mostly use generic graph traversal to discover all the lines we need,
600621 # but structs are in a bit of a different category (especially on Julia 1.5+).
601622 # 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.
604623 typedef_blocks, typedef_names = UnitRange{Int}[], Symbol[]
605- norequire = BitSet ()
606- nstmts = length (src. code)
607624 i = 1
625+ nstmts = length (src. code)
608626 while i <= nstmts
609627 stmt = rhs (src. code[i])
610628 if istypedef (stmt) && ! isanonymous_typedef (stmt:: Expr )
@@ -618,9 +636,6 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
618636 isa (name, Symbol) || @show src i r stmt
619637 push! (typedef_names, name:: Symbol )
620638 i = last (r)+ 1
621- if exclude_named_typedefs && ! isanonymous_typedef (stmt)
622- pushall! (norequire, r)
623- end
624639 else
625640 i += 1
626641 end
@@ -641,19 +656,22 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
641656 iter = 0
642657 while changed
643658 changed = false
659+
644660 # Handle ssa predecessors
645661 for idx = 1 : nstmts
646662 if isrequired[idx]
647663 changed |= add_preds! (isrequired, idx, edges, norequire)
648664 end
649665 end
666+
650667 # Handle named dependencies
651668 for (obj, uses) in edges. byname
652669 obj ∈ objs && continue
653670 if any (view (isrequired, uses. succs))
654671 changed |= add_obj! (isrequired, objs, obj, edges, norequire)
655672 end
656673 end
674+
657675 # Add control-flow. For any basic block with an evaluated statement inside it,
658676 # check to see if the block has any successors, and if so mark that block's exit statement.
659677 # Likewise, any preceding blocks should have *their* exit statement marked.
@@ -684,6 +702,7 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
684702 end
685703 end
686704 end
705+
687706 # So far, everything is generic graph traversal. Now we add some domain-specific information.
688707 # New struct definitions, including their constructors, get spread out over many
689708 # 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.
833852function print_with_code (io:: IO , src:: CodeInfo , isrequired:: AbstractVector{Bool} )
834853 nd = ndigits (length (isrequired))
835854 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 ) )
837856 postprint (:: IO ) = nothing
838857 postprint (io:: IO , idx:: Int , bbchanged:: Bool ) = nothing
839858
0 commit comments