@@ -560,6 +560,9 @@ function sroa_pass!(ir::IRCode, nargs::Int, mi_cache::MICache) where MICache
560
560
anymutability = true
561
561
end
562
562
continue
563
+ elseif is_array_alloc (stmt)
564
+ anymutability = true
565
+ continue
563
566
# elseif is_known_call(stmt, setfield!, compact)
564
567
# 4 <= length(stmt.args) <= 5 || continue
565
568
# if length(stmt.args) == 5
@@ -695,7 +698,8 @@ function form_new_preserves(origex::Expr, intermediates::Vector{Int}, new_preser
695
698
return newex
696
699
end
697
700
698
- import . EscapeAnalysis: EscapeInfo, IndexableFields, LivenessSet, getaliases
701
+ import . EscapeAnalysis:
702
+ EscapeInfo, IndexableFields, IndexableElements, LivenessSet, ArrayInfo, getaliases
699
703
700
704
function sroa_mutables! (ir:: IRCode , nargs:: Int , mi_cache:: MICache ) where MICache
701
705
# Compute domtree now, needed below, now that we have finished compacting the IR.
@@ -709,12 +713,12 @@ function sroa_mutables!(ir::IRCode, nargs::Int, mi_cache::MICache) where MICache
709
713
eliminated = BitSet ()
710
714
revisit = Tuple{#= related=# Vector{SSAValue}, #= Liveness=# LivenessSet}[]
711
715
all_preserved = true
712
- newpreserves = nothing
716
+ newpreserves = IdDict {Int,Vector{Any}} ()
713
717
while ! isempty (wset)
714
718
idx = pop! (wset)
715
719
ssa = SSAValue (idx)
716
720
stmt = ir[ssa][:inst ]
717
- isexpr (stmt, :new ) || continue
721
+ isexpr (stmt, :new ) || is_array_alloc (stmt) || continue
718
722
einfo = estate[ssa]
719
723
is_load_forwardable (einfo) || continue
720
724
aliases = getaliases (ssa, estate)
@@ -728,151 +732,48 @@ function sroa_mutables!(ir::IRCode, nargs::Int, mi_cache::MICache) where MICache
728
732
delete! (wset, alias. id)
729
733
end
730
734
end
731
- finfos = (einfo. AliasInfo:: IndexableFields ). infos
732
- nfields = length (finfos)
733
-
734
- # Partition defuses by field
735
- fdefuses = Vector {FieldDefUse} (undef, nfields)
736
- for i = 1 : nfields
737
- finfo = finfos[i]
738
- fdu = FieldDefUse ()
739
- for pc in finfo
740
- if pc > 0
741
- push! (fdu. uses, GetfieldLoad (pc)) # use (getfield call)
742
- else
743
- push! (fdu. defs, - pc) # def (setfield! call or :new expression)
744
- end
745
- end
746
- fdefuses[i] = fdu
747
- end
748
-
749
- Liveness = einfo. Liveness
750
- for livepc in Liveness
751
- livestmt = ir[SSAValue (livepc)][:inst ]
752
- if is_known_call (livestmt, Core. ifelse, ir)
753
- # the succeeding domination analysis doesn't account for conditional branching
754
- # by ifelse branching at this moment
755
- @goto next_itr
756
- elseif is_known_call (livestmt, isdefined, ir)
757
- args = livestmt. args
758
- length (args) ≥ 3 || continue
759
- obj = args[2 ]
760
- isa (obj, SSAValue) || continue
761
- obj in related || continue
762
- fld = args[3 ]
763
- fldval = try_compute_field (ir, fld)
764
- fldval === nothing && continue
765
- typ = unwrap_unionall (widenconst (argextype (obj, ir)))
766
- isa (typ, DataType) || continue
767
- fldidx = try_compute_fieldidx (typ, fldval)
768
- fldidx === nothing && continue
769
- push! (fdefuses[fldidx]. uses, IsdefinedUse (livepc))
770
- elseif isexpr (livestmt, :foreigncall )
771
- # we shouldn't eliminate this use if it's used as a direct argument
772
- args = livestmt. args
773
- nccallargs = length (args[3 ]:: SimpleVector )
774
- for i = 6 : (5 + nccallargs)
775
- arg = args[i]
776
- isa (arg, SSAValue) && arg in related && @goto next_liveness
777
- end
778
- # this use is preserve, and may be eliminable
779
- for fidx in 1 : nfields
780
- push! (fdefuses[fidx]. uses, PreserveUse (livepc))
781
- end
782
- end
783
- @label next_liveness
784
- end
785
735
786
- for fidx in 1 : nfields
787
- fdu = fdefuses[fidx]
788
- isempty (fdu. uses) && @goto next_use
789
- # check if all uses have safe definitions first, otherwise we should bail out
790
- # since then we may fail to form new ϕ-nodes
791
- ldu = compute_live_ins (ir. cfg, fdu)
792
- if isempty (ldu. live_in_bbs)
793
- phiblocks = Int[]
794
- else
795
- phiblocks = iterated_dominance_frontier (ir. cfg, ldu, domtree)
796
- end
797
- allblocks = sort! (vcat (phiblocks, ldu. def_bbs))
798
- for use in fdu. uses
799
- isa (use, IsdefinedUse) && continue
800
- if isa (use, PreserveUse) && isempty (fdu. defs)
801
- # nothing to preserve, just ignore this use (may happen when there are unintialized fields)
802
- continue
803
- end
804
- if ! has_safe_def (ir, domtree, allblocks, fdu, getuseidx (use))
805
- all_preserved = false
806
- @goto next_use
807
- end
808
- end
809
- phinodes = IdDict {Int, SSAValue} ()
810
- for b in phiblocks
811
- phinodes[b] = insert_node! (ir, first (ir. cfg. blocks[b]. stmts),
812
- NewInstruction (PhiNode (), Any))
813
- end
814
- # Now go through all uses and rewrite them
815
- for use in fdu. uses
816
- if isa (use, GetfieldLoad)
817
- use = getuseidx (use)
818
- ir[SSAValue (use)][:inst ] = compute_value_for_use (
819
- ir, domtree, allblocks, fdu, phinodes, fidx, use)
820
- push! (eliminated, use)
821
- elseif all_preserved && isa (use, PreserveUse)
822
- if newpreserves === nothing
823
- newpreserves = IdDict {Int,Vector{Any}} ()
824
- end
825
- # record this `use` as replaceable no matter if we preserve new value or not
826
- use = getuseidx (use)
827
- newvalues = get! (()-> Any[], newpreserves, use)
828
- isempty (fdu. defs) && continue # nothing to preserve (may happen when there are unintialized fields)
829
- newval = compute_value_for_use (
830
- ir, domtree, allblocks, fdu, phinodes, fidx, use)
831
- if ! isbitstype (widenconst (argextype (newval, ir)))
832
- push! (newvalues, newval)
833
- end
834
- elseif isa (use, IsdefinedUse)
835
- use = getuseidx (use)
836
- if has_safe_def (ir, domtree, allblocks, fdu, use)
837
- ir[SSAValue (use)][:inst ] = true
838
- push! (eliminated, use)
839
- end
840
- else
841
- throw (" unexpected use" )
842
- end
843
- end
844
- for b in phiblocks
845
- ϕssa = phinodes[b]
846
- n = ir[ϕssa][:inst ]:: PhiNode
847
- t = Bottom
848
- for p in ir. cfg. blocks[b]. preds
849
- push! (n. edges, p)
850
- v = compute_value_for_block (ir, domtree, allblocks, fdu, phinodes, fidx, p)
851
- push! (n. values, v)
852
- if t != = Any
853
- t = tmerge (t, argextype (v, ir))
854
- end
855
- end
856
- ir[ϕssa][:type ] = t
857
- end
858
- @label next_use
736
+ AliasInfo = einfo. AliasInfo
737
+ if isa (AliasInfo, IndexableFields)
738
+ @assert isexpr (stmt, :new ) " invalid escape analysis"
739
+ all_preserved &= load_forward_object! (ir, domtree,
740
+ eliminated, revisit,
741
+ newpreserves, related,
742
+ AliasInfo, einfo. Liveness)
743
+ else
744
+ @assert is_array_alloc (stmt) " invalid escape analysis"
745
+ arrayinfo = estate. arrayinfo
746
+ @assert isa (arrayinfo, ArrayInfo) && haskey (arrayinfo, idx) " invalid escape analysis"
747
+ dims = arrayinfo[idx]
748
+ all_preserved &= load_forward_array! (ir, domtree,
749
+ eliminated, revisit,
750
+ newpreserves, related,
751
+ AliasInfo:: IndexableElements , einfo. Liveness, dims)
859
752
end
860
- push! (revisit, (related, Liveness))
861
- @label next_itr
862
753
end
863
754
864
755
# remove dead setfield! and :new allocs
865
756
deadssas = IdSet {SSAValue} ()
866
- if all_preserved && newpreserves != = nothing
757
+ if all_preserved
867
758
preserved = keys (newpreserves)
868
759
else
869
760
preserved = EMPTY_PRESERVED_SSAS
870
761
end
871
762
mark_dead_ssas! (ir, deadssas, revisit, eliminated, preserved)
872
763
for ssa in deadssas
764
+ # stmt = ir[ssa][:inst]
765
+ # if is_known_call(stmt, setfield!, ir)
766
+ # println("[SROA] eliminated setfield!: ", argtypes_to_type(ir.argtypes[1:nargs]), " ", ssa, ": ", stmt)
767
+ # elseif isexpr(stmt, :new)
768
+ # println("[SROA] eliminated object alloc: ", argtypes_to_type(ir.argtypes[1:nargs]), " ", ssa, ": ", stmt)
769
+ # elseif is_known_call(stmt, arrayset, ir)
770
+ # println("[SROA] eliminated arrayset: ", argtypes_to_type(ir.argtypes[1:nargs]), " ", ssa, ": ", stmt)
771
+ # elseif is_array_alloc(stmt)
772
+ # println("[SROA] eliminated array alloc: ", argtypes_to_type(ir.argtypes[1:nargs]), " ", ssa, ": ", stmt)
773
+ # end
873
774
ir[ssa][:inst ] = nothing
874
775
end
875
- if all_preserved && newpreserves != = nothing
776
+ if all_preserved
876
777
deadssas = Int[ssa. id for ssa in deadssas]
877
778
for (idx, newuses) in newpreserves
878
779
ir[SSAValue (idx)][:inst ] = form_new_preserves (
@@ -883,20 +784,289 @@ function sroa_mutables!(ir::IRCode, nargs::Int, mi_cache::MICache) where MICache
883
784
return ir
884
785
end
885
786
787
+ function load_forward_object! (ir:: IRCode , domtree:: DomTree ,
788
+ eliminated:: BitSet , revisit:: Vector{Tuple{Vector{SSAValue}, LivenessSet}} ,
789
+ newpreserves:: IdDict{Int,Vector{Any}} , related:: Vector{SSAValue} ,
790
+ AliasInfo:: IndexableFields , Liveness:: LivenessSet )
791
+ finfos = AliasInfo. infos
792
+ nfields = length (finfos)
793
+
794
+ # Partition defuses by field
795
+ all_preserved = true
796
+ fdefuses = Vector {IndexedDefUse} (undef, nfields)
797
+ for i = 1 : nfields
798
+ finfo = finfos[i]
799
+ idu = IndexedDefUse ()
800
+ for pc in finfo
801
+ if pc > 0
802
+ push! (idu. uses, LoadUse (pc)) # use (getfield call)
803
+ else
804
+ push! (idu. defs, - pc) # def (setfield! call or :new expression)
805
+ end
806
+ end
807
+ fdefuses[i] = idu
808
+ end
809
+
810
+ for livepc in Liveness
811
+ livestmt = ir[SSAValue (livepc)][:inst ]
812
+ if is_known_call (livestmt, Core. ifelse, ir)
813
+ # the succeeding domination analysis doesn't account for conditional branching
814
+ # by ifelse branching at this moment
815
+ return false
816
+ elseif is_known_call (livestmt, isdefined, ir)
817
+ args = livestmt. args
818
+ length (args) ≥ 3 || continue
819
+ obj = args[2 ]
820
+ isa (obj, SSAValue) || continue
821
+ obj in related || continue
822
+ fld = args[3 ]
823
+ fldval = try_compute_field (ir, fld)
824
+ fldval === nothing && continue
825
+ typ = unwrap_unionall (widenconst (argextype (obj, ir)))
826
+ isa (typ, DataType) || continue
827
+ fldidx = try_compute_fieldidx (typ, fldval)
828
+ fldidx === nothing && continue
829
+ push! (fdefuses[fldidx]. uses, IsdefinedUse (livepc))
830
+ elseif isexpr (livestmt, :foreigncall )
831
+ # we shouldn't eliminate this use if it's used as a direct argument
832
+ args = livestmt. args
833
+ nccallargs = length (args[3 ]:: SimpleVector )
834
+ for i = 6 : (5 + nccallargs)
835
+ arg = args[i]
836
+ isa (arg, SSAValue) && arg in related && @goto next_liveness
837
+ end
838
+ # this use is preserve, and may be eliminable
839
+ for fidx in 1 : nfields
840
+ push! (fdefuses[fidx]. uses, PreserveUse (livepc))
841
+ end
842
+ end
843
+ @label next_liveness
844
+ end
845
+
846
+ for fidx in 1 : nfields
847
+ idu = fdefuses[fidx]
848
+ isempty (idu. uses) && @goto next_use
849
+ # check if all uses have safe definitions first, otherwise we should bail out
850
+ # since then we may fail to form new ϕ-nodes
851
+ ldu = compute_live_ins (ir. cfg, idu)
852
+ if isempty (ldu. live_in_bbs)
853
+ phiblocks = Int[]
854
+ else
855
+ phiblocks = iterated_dominance_frontier (ir. cfg, ldu, domtree)
856
+ end
857
+ allblocks = sort! (vcat (phiblocks, ldu. def_bbs))
858
+ for use in idu. uses
859
+ isa (use, IsdefinedUse) && continue
860
+ if isa (use, PreserveUse) && isempty (idu. defs)
861
+ # nothing to preserve, just ignore this use (may happen when there are unintialized fields)
862
+ continue
863
+ end
864
+ if ! has_safe_def (ir, domtree, allblocks, idu, getuseidx (use))
865
+ all_preserved = false
866
+ @goto next_use
867
+ end
868
+ end
869
+ phinodes = IdDict {Int, SSAValue} ()
870
+ for b in phiblocks
871
+ phinodes[b] = insert_node! (ir, first (ir. cfg. blocks[b]. stmts),
872
+ NewInstruction (PhiNode (), Any))
873
+ end
874
+ # Now go through all uses and rewrite them
875
+ for use in idu. uses
876
+ if isa (use, LoadUse)
877
+ use = getuseidx (use)
878
+ ir[SSAValue (use)][:inst ] = compute_value_for_use (
879
+ ir, domtree, allblocks, idu, phinodes, fidx, use)
880
+ push! (eliminated, use)
881
+ elseif isa (use, PreserveUse)
882
+ all_preserved || continue
883
+ # record this `use` as replaceable no matter if we preserve new value or not
884
+ use = getuseidx (use)
885
+ newvalues = get! (()-> Any[], newpreserves, use)
886
+ isempty (idu. defs) && continue # nothing to preserve (may happen when there are unintialized fields)
887
+ newval = compute_value_for_use (
888
+ ir, domtree, allblocks, idu, phinodes, fidx, use)
889
+ if ! isbitstype (widenconst (argextype (newval, ir)))
890
+ push! (newvalues, newval)
891
+ end
892
+ elseif isa (use, IsdefinedUse)
893
+ use = getuseidx (use)
894
+ if has_safe_def (ir, domtree, allblocks, idu, use)
895
+ ir[SSAValue (use)][:inst ] = true
896
+ push! (eliminated, use)
897
+ end
898
+ else
899
+ throw (" load_forward_object!: unexpected use" )
900
+ end
901
+ end
902
+ for b in phiblocks
903
+ ϕssa = phinodes[b]
904
+ n = ir[ϕssa][:inst ]:: PhiNode
905
+ t = Bottom
906
+ for p in ir. cfg. blocks[b]. preds
907
+ push! (n. edges, p)
908
+ v = compute_value_for_block (ir, domtree, allblocks, idu, phinodes, fidx, p)
909
+ push! (n. values, v)
910
+ if t != = Any
911
+ t = tmerge (t, argextype (v, ir))
912
+ end
913
+ end
914
+ ir[ϕssa][:type ] = t
915
+ end
916
+ @label next_use
917
+ end
918
+ push! (revisit, (related, Liveness))
919
+
920
+ return all_preserved
921
+ end
922
+
923
+ # TODO is_array_isassigned folding?
924
+ function load_forward_array! (ir:: IRCode , domtree:: DomTree ,
925
+ eliminated:: BitSet , revisit:: Vector{Tuple{Vector{SSAValue}, LivenessSet}} ,
926
+ newpreserves:: IdDict{Int,Vector{Any}} , related:: Vector{SSAValue} ,
927
+ AliasInfo:: IndexableElements , Liveness:: LivenessSet , dims:: Vector{Int} )
928
+ elminfos = AliasInfo. infos
929
+ elmkeys = keys (elminfos)
930
+
931
+ # Partition defuses by index
932
+ all_preserved = true
933
+ edefuses = IdDict {Int,IndexedDefUse} ()
934
+ for eidx in elmkeys
935
+ einfo = elminfos[eidx]
936
+ idu = IndexedDefUse ()
937
+ for pc in einfo
938
+ if pc > 0
939
+ push! (idu. uses, LoadUse (pc)) # use (arrayref call)
940
+ else
941
+ push! (idu. defs, - pc) # def (arrayset call)
942
+ end
943
+ end
944
+ edefuses[eidx] = idu
945
+ end
946
+
947
+ for livepc in Liveness
948
+ ssa = SSAValue (livepc)
949
+ livestmt = ir[ssa][:inst ]
950
+ if is_known_call (livestmt, Core. ifelse, ir)
951
+ # the succeeding domination analysis doesn't account for conditional branching
952
+ # by ifelse branching at this moment
953
+ return false
954
+ elseif is_known_call (livestmt, arraylen, ir)
955
+ len = 1
956
+ for dim in dims
957
+ len *= dim
958
+ end
959
+ ir[ssa][:inst ] = len
960
+ push! (eliminated, livepc)
961
+ elseif is_known_call (livestmt, arraysize, ir)
962
+ length (livestmt. args) ≥ 3 || continue
963
+ dim = argextype (livestmt. args[3 ], ir)
964
+ isa (dim, Const) || continue
965
+ dim = dim. val
966
+ isa (dim, Int) || continue
967
+ checkbounds (Bool, dims, dim) || continue
968
+ ir[ssa][:inst ] = dims[dim]
969
+ push! (eliminated, livepc)
970
+ elseif isexpr (livestmt, :foreigncall )
971
+ # we shouldn't eliminate this use if it's used as a direct argument
972
+ args = livestmt. args
973
+ nccallargs = length (args[3 ]:: SimpleVector )
974
+ for i = 6 : (5 + nccallargs)
975
+ arg = args[i]
976
+ isa (arg, SSAValue) && arg in related && @goto next_liveness
977
+ end
978
+ # this use is preserve, and may be eliminable
979
+ for eidx in elmkeys
980
+ push! (edefuses[eidx]. uses, PreserveUse (livepc))
981
+ end
982
+ end
983
+ @label next_liveness
984
+ end
985
+
986
+ for eidx in elmkeys
987
+ idu = edefuses[eidx]
988
+ isempty (idu. uses) && @goto next_use
989
+ # check if all uses have safe definitions first, otherwise we should bail out
990
+ # since then we may fail to form new ϕ-nodes
991
+ ldu = compute_live_ins (ir. cfg, idu)
992
+ if isempty (ldu. live_in_bbs)
993
+ phiblocks = Int[]
994
+ else
995
+ phiblocks = iterated_dominance_frontier (ir. cfg, ldu, domtree)
996
+ end
997
+ allblocks = sort! (vcat (phiblocks, ldu. def_bbs))
998
+ for use in idu. uses
999
+ if isa (use, PreserveUse) && isempty (idu. defs)
1000
+ # nothing to preserve, just ignore this use (may happen when there are unintialized fields)
1001
+ continue
1002
+ end
1003
+ if ! has_safe_def (ir, domtree, allblocks, idu, getuseidx (use))
1004
+ all_preserved = false
1005
+ @goto next_use
1006
+ end
1007
+ end
1008
+ phinodes = IdDict {Int, SSAValue} ()
1009
+ for b in phiblocks
1010
+ phinodes[b] = insert_node! (ir, first (ir. cfg. blocks[b]. stmts),
1011
+ NewInstruction (PhiNode (), Any))
1012
+ end
1013
+ # Now go through all uses and rewrite them
1014
+ for use in idu. uses
1015
+ if isa (use, LoadUse)
1016
+ use = getuseidx (use)
1017
+ ir[SSAValue (use)][:inst ] = compute_value_for_use (
1018
+ ir, domtree, allblocks, idu, phinodes, eidx, use)
1019
+ push! (eliminated, use)
1020
+ elseif isa (use, PreserveUse)
1021
+ all_preserved || continue
1022
+ # record this `use` as replaceable no matter if we preserve new value or not
1023
+ use = getuseidx (use)
1024
+ newvalues = get! (()-> Any[], newpreserves, use)
1025
+ isempty (idu. defs) && continue # nothing to preserve (may happen when there are unintialized fields)
1026
+ newval = compute_value_for_use (
1027
+ ir, domtree, allblocks, idu, phinodes, eidx, use)
1028
+ if ! isbitstype (widenconst (argextype (newval, ir)))
1029
+ push! (newvalues, newval)
1030
+ end
1031
+ else
1032
+ throw (" load_forward_array!: unexpected use" )
1033
+ end
1034
+ end
1035
+ for b in phiblocks
1036
+ ϕssa = phinodes[b]
1037
+ n = ir[ϕssa][:inst ]:: PhiNode
1038
+ t = Bottom
1039
+ for p in ir. cfg. blocks[b]. preds
1040
+ push! (n. edges, p)
1041
+ v = compute_value_for_block (ir, domtree, allblocks, idu, phinodes, eidx, p)
1042
+ push! (n. values, v)
1043
+ if t != = Any
1044
+ t = tmerge (t, argextype (v, ir))
1045
+ end
1046
+ end
1047
+ ir[ϕssa][:type ] = t
1048
+ end
1049
+ @label next_use
1050
+ end
1051
+ push! (revisit, (related, Liveness))
1052
+
1053
+ return all_preserved
1054
+ end
1055
+
886
1056
const EMPTY_PRESERVED_SSAS = keys (IdDict {Int,Vector{Any}} ())
887
1057
const PreservedSets = typeof (EMPTY_PRESERVED_SSAS)
888
1058
889
1059
function is_load_forwardable (x:: EscapeInfo )
890
1060
AliasInfo = x. AliasInfo
891
- return isa (AliasInfo, IndexableFields)
1061
+ return isa (AliasInfo, IndexableFields) || isa (AliasInfo, IndexableElements)
892
1062
end
893
1063
894
- struct FieldDefUse
1064
+ struct IndexedDefUse
895
1065
uses:: Vector{Any}
896
1066
defs:: Vector{Int}
897
1067
end
898
- FieldDefUse () = FieldDefUse (Any[], Int[])
899
- struct GetfieldLoad
1068
+ IndexedDefUse () = IndexedDefUse (Any[], Int[])
1069
+ struct LoadUse
900
1070
idx:: Int
901
1071
end
902
1072
struct PreserveUse
@@ -906,7 +1076,7 @@ struct IsdefinedUse
906
1076
idx:: Int
907
1077
end
908
1078
function getuseidx (@nospecialize use)
909
- if isa (use, GetfieldLoad )
1079
+ if isa (use, LoadUse )
910
1080
return use. idx
911
1081
elseif isa (use, PreserveUse)
912
1082
return use. idx
@@ -916,21 +1086,21 @@ function getuseidx(@nospecialize use)
916
1086
throw (" getuseidx: unexpected use" )
917
1087
end
918
1088
919
- function compute_live_ins (cfg:: CFG , fdu :: FieldDefUse )
1089
+ function compute_live_ins (cfg:: CFG , idu :: IndexedDefUse )
920
1090
uses = Int[]
921
- for use in fdu . uses
1091
+ for use in idu . uses
922
1092
isa (use, IsdefinedUse) && continue
923
1093
push! (uses, getuseidx (use))
924
1094
end
925
- return compute_live_ins (cfg, fdu . defs, uses)
1095
+ return compute_live_ins (cfg, idu . defs, uses)
926
1096
end
927
1097
928
1098
# even when the allocation contains an uninitialized field, we try an extra effort to check
929
1099
# if this load at `idx` have any "safe" `setfield!` calls that define the field
930
1100
# try to find
931
1101
function has_safe_def (ir:: IRCode , domtree:: DomTree , allblocks:: Vector{Int} ,
932
- fdu :: FieldDefUse , use:: Int )
933
- dfu = find_def_for_use (ir, domtree, allblocks, fdu , use)
1102
+ idu :: IndexedDefUse , use:: Int )
1103
+ dfu = find_def_for_use (ir, domtree, allblocks, idu , use)
934
1104
dfu === nothing && return false
935
1105
def = dfu[1 ]
936
1106
def ≠ 0 && return true # found a "safe" definition
@@ -946,7 +1116,7 @@ function has_safe_def(ir::IRCode, domtree::DomTree, allblocks::Vector{Int},
946
1116
pred in seen && return false
947
1117
use = last (ir. cfg. blocks[pred]. stmts)
948
1118
# NOTE this `use` isn't a load, and so the inclusive condition can be used
949
- dfu = find_def_for_use (ir, domtree, allblocks, fdu , use, true )
1119
+ dfu = find_def_for_use (ir, domtree, allblocks, idu , use, true )
950
1120
dfu === nothing && return false
951
1121
def = dfu[1 ]
952
1122
push! (seen, pred)
@@ -961,12 +1131,12 @@ end
961
1131
962
1132
# find the first dominating def for the given use
963
1133
function find_def_for_use (ir:: IRCode , domtree:: DomTree , allblocks:: Vector{Int} ,
964
- fdu :: FieldDefUse , use:: Int , inclusive:: Bool = false )
1134
+ idu :: IndexedDefUse , use:: Int , inclusive:: Bool = false )
965
1135
useblock = block_for_inst (ir. cfg, use)
966
1136
curblock = find_curblock (domtree, allblocks, useblock)
967
1137
curblock === nothing && return nothing
968
1138
local def = 0
969
- for idx in fdu . defs
1139
+ for idx in idu . defs
970
1140
if block_for_inst (ir. cfg, idx) == curblock
971
1141
if curblock != useblock
972
1142
# Find the last def in this block
@@ -995,15 +1165,15 @@ function find_curblock(domtree::DomTree, allblocks::Vector{Int}, curblock::Int)
995
1165
end
996
1166
997
1167
function compute_value_for_use (ir:: IRCode , domtree:: DomTree , allblocks:: Vector{Int} ,
998
- fdu :: FieldDefUse , phinodes:: IdDict{Int, SSAValue} , fidx:: Int , use:: Int )
999
- dfu = find_def_for_use (ir, domtree, allblocks, fdu , use)
1168
+ idu :: IndexedDefUse , phinodes:: IdDict{Int, SSAValue} , fidx:: Int , use:: Int )
1169
+ dfu = find_def_for_use (ir, domtree, allblocks, idu , use)
1000
1170
@assert dfu != = nothing " has_safe_def condition unsatisfied"
1001
1171
def, useblock, curblock = dfu
1002
1172
if def == 0
1003
1173
if ! haskey (phinodes, curblock)
1004
1174
# If this happens, we need to search the predecessors for defs. Which
1005
1175
# one doesn't matter - if it did, we'd have had a phinode
1006
- return compute_value_for_block (ir, domtree, allblocks, fdu , phinodes, fidx, first (ir. cfg. blocks[useblock]. preds))
1176
+ return compute_value_for_block (ir, domtree, allblocks, idu , phinodes, fidx, first (ir. cfg. blocks[useblock]. preds))
1007
1177
end
1008
1178
# The use is the phinode
1009
1179
return phinodes[curblock]
@@ -1013,11 +1183,11 @@ function compute_value_for_use(ir::IRCode, domtree::DomTree, allblocks::Vector{I
1013
1183
end
1014
1184
1015
1185
function compute_value_for_block (ir:: IRCode , domtree:: DomTree , allblocks:: Vector{Int} ,
1016
- fdu :: FieldDefUse , phinodes:: IdDict{Int, SSAValue} , fidx:: Int , curblock:: Int )
1186
+ idu :: IndexedDefUse , phinodes:: IdDict{Int, SSAValue} , fidx:: Int , curblock:: Int )
1017
1187
curblock = find_curblock (domtree, allblocks, curblock)
1018
1188
@assert curblock != = nothing " has_safe_def condition unsatisfied"
1019
1189
def = 0
1020
- for stmt in fdu . defs
1190
+ for stmt in idu . defs
1021
1191
if block_for_inst (ir. cfg, stmt) == curblock
1022
1192
def = max (def, stmt)
1023
1193
end
@@ -1029,9 +1199,12 @@ function val_for_def_expr(ir::IRCode, def::Int, fidx::Int)
1029
1199
ex = ir[SSAValue (def)][:inst ]
1030
1200
if isexpr (ex, :new )
1031
1201
return ex. args[1 + fidx]
1032
- else
1033
- @assert is_known_call (ex, setfield!, ir) " invalid load forwarding"
1202
+ elseif is_known_call (ex, setfield!, ir)
1034
1203
return ex. args[4 ]
1204
+ elseif is_known_call (ex, arrayset, ir)
1205
+ return ex. args[4 ]
1206
+ else
1207
+ throw (" invalid load forwarding" )
1035
1208
end
1036
1209
end
1037
1210
@@ -1100,6 +1273,34 @@ function mark_dead_ssas!(ir::IRCode, deadssas::IdSet{SSAValue},
1100
1273
end
1101
1274
end
1102
1275
return false
1276
+ elseif is_known_call (stmt, arrayset, ir)
1277
+ @assert length (stmt. args) ≥ 4 " invalid escape analysis"
1278
+ ary = stmt. args[3 ]
1279
+ val = stmt. args[4 ]
1280
+ if isa (ary, SSAValue)
1281
+ if ary in related
1282
+ push! (eliminable, ssa)
1283
+ @goto next_live
1284
+ end
1285
+ if isa (val, SSAValue) && val in related
1286
+ if ary in deadssas
1287
+ push! (eliminable, ssa)
1288
+ @goto next_live
1289
+ end
1290
+ for new_revisit_idx in wset
1291
+ if ary in revisit[new_revisit_idx][1 ]
1292
+ delete! (wset, new_revisit_idx)
1293
+ if mark_dead_ssas! (ir, deadssas, revisit, eliminated, preserved, wset, new_revisit_idx)
1294
+ push! (eliminable, ssa)
1295
+ @goto next_live
1296
+ else
1297
+ return false
1298
+ end
1299
+ end
1300
+ end
1301
+ end
1302
+ end
1303
+ return false
1103
1304
elseif isexpr (stmt, :foreigncall )
1104
1305
livepc in preserved && @goto next_live
1105
1306
return false
0 commit comments