Skip to content

Commit e5db600

Browse files
committed
Possible fix
1 parent 6a9fb50 commit e5db600

File tree

3 files changed

+87
-58
lines changed

3 files changed

+87
-58
lines changed

src/codeedges.jl

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,8 @@ function terminal_preds(i::Int, edges::CodeEdges)
561561
return s
562562
end
563563

564+
initialize_isrequired(n) = fill!(Vector{Union{Bool,Symbol}}(undef, n), false)
565+
564566
"""
565567
isrequired = lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges)
566568
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
573575
See also [`lines_required!`](@ref) and [`selective_eval!`](@ref).
574576
"""
575577
function lines_required(obj::GlobalRef, src::CodeInfo, edges::CodeEdges; kwargs...)
576-
isrequired = falses(length(edges.preds))
578+
isrequired = initialize_isrequired(length(src.code))
577579
objs = Set{GlobalRef}([obj])
578580
return lines_required!(isrequired, objs, src, edges; kwargs...)
579581
end
580582

581583
function lines_required(idx::Int, src::CodeInfo, edges::CodeEdges; kwargs...)
582-
isrequired = falses(length(edges.preds))
584+
isrequired = initialize_isrequired(length(edges.preds))
583585
isrequired[idx] = true
584586
objs = Set{GlobalRef}()
585587
return lines_required!(isrequired, objs, src, edges; kwargs...)
586588
end
587589

588590
"""
589-
lines_required!(isrequired::AbstractVector{Bool}, src::CodeInfo, edges::CodeEdges;
591+
lines_required!(isrequired::AbstractVector{Union{Bool,Symbol}}, src::CodeInfo, edges::CodeEdges;
590592
norequire = ())
591593
592594
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.
598600
For example, use `norequire = LoweredCodeUtils.exclude_named_typedefs(src, edges)` if you're
599601
extracting method signatures and not evaluating new definitions.
600602
"""
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...)
602604
objs = Set{GlobalRef}()
603605
return lines_required!(isrequired, objs, src, edges; kwargs...)
604606
end
@@ -620,7 +622,7 @@ function exclude_named_typedefs(src::CodeInfo, edges::CodeEdges)
620622
return norequire
621623
end
622624

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 = ())
624626
# Mark any requested objects (their lines of assignment)
625627
objs = add_requests!(isrequired, objs, edges, norequire)
626628

@@ -638,6 +640,9 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
638640
iter = 0
639641
while changed
640642
changed = false
643+
@show iter
644+
print_with_code(stdout, src, isrequired)
645+
println()
641646

642647
# Handle ssa predecessors
643648
changed |= add_ssa_preds!(isrequired, src, edges, norequire)
@@ -646,8 +651,8 @@ function lines_required!(isrequired::AbstractVector{Bool}, objs, src::CodeInfo,
646651
changed |= add_named_dependencies!(isrequired, edges, objs, norequire)
647652

648653
# 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)
651656

652657
# So far, everything is generic graph traversal. Now we add some domain-specific information
653658
changed |= add_typedefs!(isrequired, src, edges, typedefs, norequire)
@@ -669,7 +674,7 @@ end
669674
function add_ssa_preds!(isrequired, src::CodeInfo, edges::CodeEdges, norequire)
670675
changed = false
671676
for idx = 1:length(src.code)
672-
if isrequired[idx]
677+
if isrequired[idx] == true
673678
changed |= add_preds!(isrequired, idx, edges, norequire)
674679
end
675680
end
@@ -680,7 +685,7 @@ function add_named_dependencies!(isrequired, edges::CodeEdges, objs, norequire)
680685
changed = false
681686
for (obj, uses) in edges.byname
682687
obj objs && continue
683-
if any(view(isrequired, uses.succs))
688+
if any(==(true), view(isrequired, uses.succs))
684689
changed |= add_obj!(isrequired, objs, obj, edges, norequire)
685690
end
686691
end
@@ -691,7 +696,7 @@ function add_preds!(isrequired, idx, edges::CodeEdges, norequire)
691696
chngd = false
692697
preds = edges.preds[idx]
693698
for p in preds
694-
isrequired[p] && continue
699+
isrequired[p] == true && continue
695700
p norequire && continue
696701
isrequired[p] = true
697702
chngd = true
@@ -702,7 +707,7 @@ end
702707
function add_succs!(isrequired, idx, edges::CodeEdges, succs, norequire)
703708
chngd = false
704709
for p in succs
705-
isrequired[p] && continue
710+
isrequired[p] == true && continue
706711
p norequire && continue
707712
isrequired[p] = true
708713
chngd = true
@@ -713,8 +718,9 @@ end
713718
function add_obj!(isrequired, objs, obj, edges::CodeEdges, norequire)
714719
chngd = false
715720
for d in edges.byname[obj].assigned
721+
isrequired[d] == true && continue
716722
d norequire && continue
717-
isrequired[d] || add_preds!(isrequired, d, edges, norequire)
723+
add_preds!(isrequired, d, edges, norequire)
718724
isrequired[d] = true
719725
chngd = true
720726
end
@@ -725,38 +731,33 @@ end
725731
## Add control-flow
726732

727733
# Mark loops that contain evaluated statements
728-
function add_loops!(isrequired, cfg)
734+
function add_loops!(isrequired, cfg, domtree, postdomtree)
729735
changed = false
730736
for (ibb, bb) in enumerate(cfg.blocks)
731-
needed = false
732737
for ibbp in bb.preds
733738
# Is there a backwards-pointing predecessor, and if so are there any required statements between the two?
734739
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
745746
end
746747
end
747748
end
748749
return changed
749750
end
750751

751-
function add_control_flow!(isrequired, cfg, domtree, postdomtree)
752+
function add_control_flow!(isrequired, src, cfg, domtree, postdomtree)
752753
changed, _changed = false, true
753754
blocks = cfg.blocks
754755
nblocks = length(blocks)
755756
while _changed
756757
_changed = false
757758
for (ibb, bb) in enumerate(blocks)
758759
r = rng(bb)
759-
if any(view(isrequired, r))
760+
if any(==(true), view(isrequired, r))
760761
# Walk up the dominators
761762
jbb = ibb
762763
while jbb != 1
@@ -766,8 +767,11 @@ function add_control_flow!(isrequired, cfg, domtree, postdomtree)
766767
for s in dbb.succs
767768
if !postdominates(postdomtree, jbb, s)
768769
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
771775
break
772776
end
773777
end
@@ -780,11 +784,27 @@ function add_control_flow!(isrequired, cfg, domtree, postdomtree)
780784
# Check if the exit of this block is a GotoNode or `return`
781785
if length(pdbb.succs) < 2
782786
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
785795
end
786796
jbb = postdomtree.idoms_bb[jbb]
787797
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
788808
end
789809
end
790810
changed |= _changed
@@ -825,11 +845,11 @@ function add_typedefs!(isrequired, src::CodeInfo, edges::CodeEdges, (typedef_blo
825845
idx = 1
826846
while idx < length(stmts)
827847
stmt = stmts[idx]
828-
isrequired[idx] || (idx += 1; continue)
848+
isrequired[idx] == true || (idx += 1; continue)
829849
for (typedefr, typedefn) in zip(typedef_blocks, typedef_names)
830850
if idx typedefr
831851
ireq = view(isrequired, typedefr)
832-
if !all(ireq)
852+
if !all(==(true), ireq)
833853
changed = true
834854
ireq .= true
835855
# 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
857877
if i <= length(stmts) && (stmts[i]::Expr).args[1] == false
858878
tpreds = terminal_preds(i, edges)
859879
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
862884
end
863885
end
864886
end
@@ -877,15 +899,17 @@ function add_inplace!(isrequired, src, edges, norequire)
877899
callee_matches(fname, Base, :pop!) ||
878900
callee_matches(fname, Base, :empty!) ||
879901
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
882906
end
883907
return _changed
884908
end
885909

886910
changed = false
887911
for (i, isreq) in pairs(isrequired)
888-
isreq || continue
912+
isreq == true || continue
889913
for j in edges.succs[i]
890914
j norequire && continue
891915
stmt = src.code[j]
@@ -930,14 +954,16 @@ This will return either a `BreakpointRef`, the value obtained from the last exec
930954
(if stored to `frame.framedata.ssavlues`), or `nothing`.
931955
Typically, assignment to a variable binding does not result in an ssa store by JuliaInterpreter.
932956
"""
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)
934958
pc = pcexec = pclast = frame.pc
935959
while isa(pc, Int)
936960
frame.pc = pc
937961
te = isrequired[pc]
938962
pclast = pcexec::Int
939-
if te
963+
if te == true
940964
pcexec = pc = step_expr!(recurse, frame, istoplevel)
965+
elseif te == :exit
966+
pc = nothing
941967
else
942968
pc = next_or_nothing!(frame)
943969
end
@@ -946,11 +972,11 @@ function selective_eval!(@nospecialize(recurse), frame::Frame, isrequired::Abstr
946972
pcexec = (pcexec === nothing ? pclast : pcexec)::Int
947973
frame.pc = pcexec
948974
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
950976
isassigned(frame.framedata.ssavalues, pcexec) && return frame.framedata.ssavalues[pcexec]
951977
return nothing
952978
end
953-
function selective_eval!(frame::Frame, isrequired::AbstractVector{Bool}, istoplevel::Bool=false)
979+
function selective_eval!(frame::Frame, isrequired, istoplevel::Bool=false)
954980
selective_eval!(finish_and_return!, frame, isrequired, istoplevel)
955981
end
956982

@@ -959,29 +985,32 @@ end
959985
960986
Like [`selective_eval!`](@ref), except it sets `frame.pc` to the first `true` statement in `isrequired`.
961987
"""
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)
963989
pc = findfirst(isrequired)
964990
pc === nothing && return nothing
965991
frame.pc = pc
966992
return selective_eval!(recurse, frame, isrequired, istoplevel)
967993
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)
969995
selective_eval_fromstart!(finish_and_return!, frame, isrequired, istoplevel)
970996
end
971997

972998
"""
973-
print_with_code(io, src::CodeInfo, isrequired::AbstractVector{Bool})
999+
print_with_code(io, src::CodeInfo, isrequired::AbstractVector{Union{Bool,Symbol}})
9741000
9751001
Mark each line of code with its requirement status.
9761002
9771003
!!! compat "Julia 1.6"
9781004
This function produces dummy output if suitable support is missing in your version of Julia.
9791005
9801006
"""
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
9821011
nd = ndigits(length(isrequired))
9831012
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))
9851014
postprint(::IO) = nothing
9861015
postprint(io::IO, idx::Int, bbchanged::Bool) = nothing
9871016

src/packagedef.jl

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ if Base.VERSION < v"1.10"
2121
else
2222
const construct_domtree = Core.Compiler.construct_domtree
2323
const construct_postdomtree = Core.Compiler.construct_postdomtree
24+
const dominates = Core.Compiler.dominates
2425
const postdominates = Core.Compiler.postdominates
2526
end
2627

@@ -46,10 +47,8 @@ if ccall(:jl_generating_output, Cint, ()) == 1
4647
isrequired = lines_required(GlobalRef(@__MODULE__, :s), src, edges)
4748
lines_required(GlobalRef(@__MODULE__, :s), src, edges; norequire=())
4849
lines_required(GlobalRef(@__MODULE__, :s), src, edges; norequire=exclude_named_typedefs(src, edges))
49-
for isreq in (isrequired, convert(Vector{Bool}, isrequired))
50-
lines_required!(isreq, src, edges; norequire=())
51-
lines_required!(isreq, src, edges; norequire=exclude_named_typedefs(src, edges))
52-
end
50+
lines_required!(isrequired, src, edges; norequire=())
51+
lines_required!(isrequired, src, edges; norequire=exclude_named_typedefs(src, edges))
5352
frame = Frame(@__MODULE__, src)
5453
# selective_eval_fromstart!(frame, isrequired, true)
5554
precompile(selective_eval_fromstart!, (typeof(frame), typeof(isrequired), Bool)) # can't @eval during precompilation

0 commit comments

Comments
 (0)