@@ -561,6 +561,8 @@ function terminal_preds(i::Int, edges::CodeEdges)
561
561
return s
562
562
end
563
563
564
+ initialize_isrequired (n) = fill! (Vector {Union{Bool,Symbol}} (undef, n), false )
565
+
564
566
"""
565
567
isrequired = lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges)
566
568
isrequired = lines_required(idx::Int, src::CodeInfo, edges::CodeEdges)
@@ -573,20 +575,20 @@ will end up skipping a subset of such statements, perhaps while repeating others
573
575
See also [`lines_required!`](@ref) and [`selective_eval!`](@ref).
574
576
"""
575
577
function lines_required (obj:: GlobalRef , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
576
- isrequired = falses (length (edges . preds ))
578
+ isrequired = initialize_isrequired (length (src . code ))
577
579
objs = Set {GlobalRef} ([obj])
578
580
return lines_required! (isrequired, objs, src, edges; kwargs... )
579
581
end
580
582
581
583
function lines_required (idx:: Int , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
582
- isrequired = falses (length (edges. preds))
584
+ isrequired = initialize_isrequired (length (edges. preds))
583
585
isrequired[idx] = true
584
586
objs = Set {GlobalRef} ()
585
587
return lines_required! (isrequired, objs, src, edges; kwargs... )
586
588
end
587
589
588
590
"""
589
- lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges;
591
+ lines_required!(isrequired::AbstractVector{Union{ Bool,Symbol} }, src::CodeInfo, edges::CodeEdges;
590
592
norequire = ())
591
593
592
594
Like `lines_required`, but where `isrequired[idx]` has already been set to `true` for all statements
@@ -598,7 +600,7 @@ should _not_ be marked as a requirement.
598
600
For example, use `norequire = LoweredCodeUtils.exclude_named_typedefs(src, edges)` if you're
599
601
extracting method signatures and not evaluating new definitions.
600
602
"""
601
- function lines_required! (isrequired:: AbstractVector{Bool} , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
603
+ function lines_required! (isrequired:: AbstractVector{Union{ Bool,Symbol} } , src:: CodeInfo , edges:: CodeEdges ; kwargs... )
602
604
objs = Set {GlobalRef} ()
603
605
return lines_required! (isrequired, objs, src, edges; kwargs... )
604
606
end
@@ -620,7 +622,7 @@ function exclude_named_typedefs(src::CodeInfo, edges::CodeEdges)
620
622
return norequire
621
623
end
622
624
623
- function lines_required! (isrequired:: AbstractVector{Bool} , objs, src:: CodeInfo , edges:: CodeEdges ; norequire = ())
625
+ function lines_required! (isrequired:: AbstractVector{Union{ Bool,Symbol} } , objs, src:: CodeInfo , edges:: CodeEdges ; norequire = ())
624
626
# Mark any requested objects (their lines of assignment)
625
627
objs = add_requests! (isrequired, objs, edges, norequire)
626
628
@@ -638,6 +640,9 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
638
640
iter = 0
639
641
while changed
640
642
changed = false
643
+ @show iter
644
+ print_with_code (stdout , src, isrequired)
645
+ println ()
641
646
642
647
# Handle ssa predecessors
643
648
changed |= add_ssa_preds! (isrequired, src, edges, norequire)
@@ -646,8 +651,8 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
646
651
changed |= add_named_dependencies! (isrequired, edges, objs, norequire)
647
652
648
653
# Add control-flow
649
- changed |= add_loops! (isrequired, cfg)
650
- changed |= add_control_flow! (isrequired, cfg, domtree, postdomtree)
654
+ changed |= add_loops! (isrequired, cfg, domtree, postdomtree )
655
+ changed |= add_control_flow! (isrequired, src, cfg, domtree, postdomtree)
651
656
652
657
# So far, everything is generic graph traversal. Now we add some domain-specific information
653
658
changed |= add_typedefs! (isrequired, src, edges, typedefs, norequire)
669
674
function add_ssa_preds! (isrequired, src:: CodeInfo , edges:: CodeEdges , norequire)
670
675
changed = false
671
676
for idx = 1 : length (src. code)
672
- if isrequired[idx]
677
+ if isrequired[idx] == true
673
678
changed |= add_preds! (isrequired, idx, edges, norequire)
674
679
end
675
680
end
@@ -680,7 +685,7 @@ function add_named_dependencies!(isrequired, edges::CodeEdges, objs, norequire)
680
685
changed = false
681
686
for (obj, uses) in edges. byname
682
687
obj ∈ objs && continue
683
- if any (view (isrequired, uses. succs))
688
+ if any (== ( true ), view (isrequired, uses. succs))
684
689
changed |= add_obj! (isrequired, objs, obj, edges, norequire)
685
690
end
686
691
end
@@ -691,7 +696,7 @@ function add_preds!(isrequired, idx, edges::CodeEdges, norequire)
691
696
chngd = false
692
697
preds = edges. preds[idx]
693
698
for p in preds
694
- isrequired[p] && continue
699
+ isrequired[p] == true && continue
695
700
p ∈ norequire && continue
696
701
isrequired[p] = true
697
702
chngd = true
702
707
function add_succs! (isrequired, idx, edges:: CodeEdges , succs, norequire)
703
708
chngd = false
704
709
for p in succs
705
- isrequired[p] && continue
710
+ isrequired[p] == true && continue
706
711
p ∈ norequire && continue
707
712
isrequired[p] = true
708
713
chngd = true
713
718
function add_obj! (isrequired, objs, obj, edges:: CodeEdges , norequire)
714
719
chngd = false
715
720
for d in edges. byname[obj]. assigned
721
+ isrequired[d] == true && continue
716
722
d ∈ norequire && continue
717
- isrequired[d] || add_preds! (isrequired, d, edges, norequire)
723
+ add_preds! (isrequired, d, edges, norequire)
718
724
isrequired[d] = true
719
725
chngd = true
720
726
end
@@ -725,38 +731,33 @@ end
725
731
# # Add control-flow
726
732
727
733
# Mark loops that contain evaluated statements
728
- function add_loops! (isrequired, cfg)
734
+ function add_loops! (isrequired, cfg, domtree, postdomtree )
729
735
changed = false
730
736
for (ibb, bb) in enumerate (cfg. blocks)
731
- needed = false
732
737
for ibbp in bb. preds
733
738
# Is there a backwards-pointing predecessor, and if so are there any required statements between the two?
734
739
ibbp > ibb || continue # not a loop-block predecessor
735
- r, rp = rng (bb), rng (cfg. blocks[ibbp])
736
- r = first (r): first (rp)- 1
737
- needed |= any (view (isrequired, r))
738
- end
739
- if needed
740
- # Mark the final statement of all predecessors
741
- for ibbp in bb. preds
742
- rp = rng (cfg. blocks[ibbp])
743
- changed |= ! isrequired[last (rp)]
744
- isrequired[last (rp)] = true
740
+ if postdominates (postdomtree, ibb, ibbp)
741
+ r = rng (cfg. blocks[ibbp])
742
+ if isrequired[r[end ]] != true
743
+ isrequired[r[end ]] = true
744
+ changed = true
745
+ end
745
746
end
746
747
end
747
748
end
748
749
return changed
749
750
end
750
751
751
- function add_control_flow! (isrequired, cfg, domtree, postdomtree)
752
+ function add_control_flow! (isrequired, src, cfg, domtree, postdomtree)
752
753
changed, _changed = false , true
753
754
blocks = cfg. blocks
754
755
nblocks = length (blocks)
755
756
while _changed
756
757
_changed = false
757
758
for (ibb, bb) in enumerate (blocks)
758
759
r = rng (bb)
759
- if any (view (isrequired, r))
760
+ if any (== ( true ), view (isrequired, r))
760
761
# Walk up the dominators
761
762
jbb = ibb
762
763
while jbb != 1
@@ -766,8 +767,11 @@ function add_control_flow!(isrequired, cfg, domtree, postdomtree)
766
767
for s in dbb. succs
767
768
if ! postdominates (postdomtree, jbb, s)
768
769
idxlast = rng (dbb)[end ]
769
- _changed |= ! isrequired[idxlast]
770
- isrequired[idxlast] = true
770
+ if isrequired[idxlast] != true
771
+ println (" add 1: " , idxlast)
772
+ _changed = true
773
+ isrequired[idxlast] = true
774
+ end
771
775
break
772
776
end
773
777
end
@@ -780,11 +784,27 @@ function add_control_flow!(isrequired, cfg, domtree, postdomtree)
780
784
# Check if the exit of this block is a GotoNode or `return`
781
785
if length (pdbb. succs) < 2
782
786
idxlast = rng (pdbb)[end ]
783
- _changed |= ! isrequired[idxlast]
784
- isrequired[idxlast] = true
787
+ stmt = src. code[idxlast]
788
+ if isa (stmt, GotoNode) || isa (stmt, Core. ReturnNode)
789
+ if isrequired[idxlast] == false
790
+ println (" add 2: " , idxlast)
791
+ _changed = true
792
+ isrequired[idxlast] = :exit
793
+ end
794
+ end
785
795
end
786
796
jbb = postdomtree. idoms_bb[jbb]
787
797
end
798
+ elseif length (r) == 1
799
+ # pdbb = blocks[ibb]
800
+ # if length(pdbb.succs) < 2 && isa(src.code[r[1]], GotoNode)
801
+ # idxlast = r[end]
802
+ # if isrequired[idxlast] == false
803
+ # println("add 3: ", idxlast)
804
+ # _changed = true
805
+ # isrequired[idxlast] = true
806
+ # end
807
+ # end
788
808
end
789
809
end
790
810
changed |= _changed
@@ -825,11 +845,11 @@ function add_typedefs!(isrequired, src::CodeInfo, edges::CodeEdges, (typedef_blo
825
845
idx = 1
826
846
while idx < length (stmts)
827
847
stmt = stmts[idx]
828
- isrequired[idx] || (idx += 1 ; continue )
848
+ isrequired[idx] == true || (idx += 1 ; continue )
829
849
for (typedefr, typedefn) in zip (typedef_blocks, typedef_names)
830
850
if idx ∈ typedefr
831
851
ireq = view (isrequired, typedefr)
832
- if ! all (ireq)
852
+ if ! all (== ( true ), ireq)
833
853
changed = true
834
854
ireq .= true
835
855
# Also mark any by-type constructor(s) associated with this typedef
@@ -857,8 +877,10 @@ function add_typedefs!(isrequired, src::CodeInfo, edges::CodeEdges, (typedef_blo
857
877
if i <= length (stmts) && (stmts[i]:: Expr ). args[1 ] == false
858
878
tpreds = terminal_preds (i, edges)
859
879
if minimum (tpreds) == idx && i ∉ norequire
860
- changed |= ! isrequired[i]
861
- isrequired[i] = true
880
+ if isrequired[i] != true
881
+ changed = true
882
+ isrequired[i] = true
883
+ end
862
884
end
863
885
end
864
886
end
@@ -877,15 +899,17 @@ function add_inplace!(isrequired, src, edges, norequire)
877
899
callee_matches (fname, Base, :pop! ) ||
878
900
callee_matches (fname, Base, :empty! ) ||
879
901
callee_matches (fname, Base, :setindex! ))
880
- _changed = ! isrequired[j]
881
- isrequired[j] = true
902
+ if isrequired[j] != true
903
+ _changed = true
904
+ isrequired[j] = true
905
+ end
882
906
end
883
907
return _changed
884
908
end
885
909
886
910
changed = false
887
911
for (i, isreq) in pairs (isrequired)
888
- isreq || continue
912
+ isreq == true || continue
889
913
for j in edges. succs[i]
890
914
j ∈ norequire && continue
891
915
stmt = src. code[j]
@@ -930,14 +954,16 @@ This will return either a `BreakpointRef`, the value obtained from the last exec
930
954
(if stored to `frame.framedata.ssavlues`), or `nothing`.
931
955
Typically, assignment to a variable binding does not result in an ssa store by JuliaInterpreter.
932
956
"""
933
- function selective_eval! (@nospecialize (recurse), frame:: Frame , isrequired:: AbstractVector{Bool} , istoplevel:: Bool = false )
957
+ function selective_eval! (@nospecialize (recurse), frame:: Frame , isrequired, istoplevel:: Bool = false )
934
958
pc = pcexec = pclast = frame. pc
935
959
while isa (pc, Int)
936
960
frame. pc = pc
937
961
te = isrequired[pc]
938
962
pclast = pcexec:: Int
939
- if te
963
+ if te == true
940
964
pcexec = pc = step_expr! (recurse, frame, istoplevel)
965
+ elseif te == :exit
966
+ pc = nothing
941
967
else
942
968
pc = next_or_nothing! (frame)
943
969
end
@@ -946,11 +972,11 @@ function selective_eval!(@nospecialize(recurse), frame::Frame, isrequired::Abstr
946
972
pcexec = (pcexec === nothing ? pclast : pcexec):: Int
947
973
frame. pc = pcexec
948
974
node = pc_expr (frame)
949
- is_return (node) && return isrequired[pcexec] ? lookup_return (frame, node) : nothing
975
+ is_return (node) && return isrequired[pcexec] == true ? lookup_return (frame, node) : nothing
950
976
isassigned (frame. framedata. ssavalues, pcexec) && return frame. framedata. ssavalues[pcexec]
951
977
return nothing
952
978
end
953
- function selective_eval! (frame:: Frame , isrequired:: AbstractVector{Bool} , istoplevel:: Bool = false )
979
+ function selective_eval! (frame:: Frame , isrequired, istoplevel:: Bool = false )
954
980
selective_eval! (finish_and_return!, frame, isrequired, istoplevel)
955
981
end
956
982
@@ -959,29 +985,32 @@ end
959
985
960
986
Like [`selective_eval!`](@ref), except it sets `frame.pc` to the first `true` statement in `isrequired`.
961
987
"""
962
- function selective_eval_fromstart! (@nospecialize (recurse), frame, isrequired, istoplevel:: Bool = false )
988
+ function selective_eval_fromstart! (@nospecialize (recurse), frame:: Frame , isrequired, istoplevel:: Bool = false )
963
989
pc = findfirst (isrequired)
964
990
pc === nothing && return nothing
965
991
frame. pc = pc
966
992
return selective_eval! (recurse, frame, isrequired, istoplevel)
967
993
end
968
- function selective_eval_fromstart! (frame:: Frame , isrequired:: AbstractVector{Bool} , istoplevel:: Bool = false )
994
+ function selective_eval_fromstart! (frame:: Frame , isrequired, istoplevel:: Bool = false )
969
995
selective_eval_fromstart! (finish_and_return!, frame, isrequired, istoplevel)
970
996
end
971
997
972
998
"""
973
- print_with_code(io, src::CodeInfo, isrequired::AbstractVector{Bool})
999
+ print_with_code(io, src::CodeInfo, isrequired::AbstractVector{Union{ Bool,Symbol} })
974
1000
975
1001
Mark each line of code with its requirement status.
976
1002
977
1003
!!! compat "Julia 1.6"
978
1004
This function produces dummy output if suitable support is missing in your version of Julia.
979
1005
980
1006
"""
981
- function print_with_code (io:: IO , src:: CodeInfo , isrequired:: AbstractVector{Bool} )
1007
+ function print_with_code (io:: IO , src:: CodeInfo , isrequired:: AbstractVector{Union{Bool,Symbol}} )
1008
+ function markchar (c)
1009
+ return c === true ? ' t' : (c === false ? ' f' : (c === :exit ? ' e' : ' x' ))
1010
+ end
982
1011
nd = ndigits (length (isrequired))
983
1012
preprint (:: IO ) = nothing
984
- preprint (io:: IO , idx:: Int ) = (c = isrequired[idx]; printstyled (io, lpad (idx, nd), ' ' , c ? " t " : " f " ; color = c ? :cyan : :plain ))
1013
+ preprint (io:: IO , idx:: Int ) = (c = markchar ( isrequired[idx]) ; printstyled (io, lpad (idx, nd), ' ' , c; color = c ∈ ( ' t ' , ' e ' ) ? :cyan : :plain ))
985
1014
postprint (:: IO ) = nothing
986
1015
postprint (io:: IO , idx:: Int , bbchanged:: Bool ) = nothing
987
1016
0 commit comments