10
10
du::SSADefUse
11
11
12
12
This struct keeps track of all uses of some mutable struct allocated in the current function:
13
- - `du.uses::Vector{Int }` are all instances of `getfield` on the struct
13
+ - `du.uses::Vector{Any }` are some "usages" (like `getfield`) of the struct
14
14
- `du.defs::Vector{Int}` are all instances of `setfield!` on the struct
15
15
The terminology refers to the uses/defs of the "slot bundle" that the mutable struct represents.
16
16
17
- In addition we keep track of all instances of a `:foreigncall` that preserves of this mutable
18
- struct in `du.ccall_preserve_uses`. Somewhat counterintuitively, we don't actually need to
19
- make sure that the struct itself is live (or even allocated) at a `ccall` site.
20
- If there are no other places where the struct escapes (and thus e.g. where its address is taken),
21
- it need not be allocated. We do however, need to make sure to preserve any elements of this struct.
17
+ `du.uses` tracks all instances of `getfield` and `isdefined` calls on the struct.
18
+ Additionally it also tracks all instances of a `:foreigncall` that preserves of this mutable
19
+ struct. Somewhat counterintuitively, we don't actually need to make sure that the struct
20
+ itself is live (or even allocated) at a `ccall` site. If there are no other places where
21
+ the struct escapes (and thus e.g. where its address is taken), it need not be allocated.
22
+ We do however, need to make sure to preserve any elements of this struct.
22
23
"""
23
24
struct SSADefUse
24
- uses:: Vector{Int }
25
+ uses:: Vector{Any }
25
26
defs:: Vector{Int}
26
- ccall_preserve_uses:: Vector{Int}
27
27
end
28
- SSADefUse () = SSADefUse (Int[], Int[], Int[])
28
+ SSADefUse () = SSADefUse (Any[], Int[])
29
+
30
+ struct GetfieldUse
31
+ idx:: Int
32
+ end
33
+ struct PreserveUse
34
+ idx:: Int
35
+ end
36
+ struct NoPreserve end
37
+ struct IsdefinedUse
38
+ idx:: Int
39
+ end
40
+ function getuseidx (@nospecialize use)
41
+ if isa (use, GetfieldUse)
42
+ return use. idx
43
+ elseif isa (use, PreserveUse)
44
+ return use. idx
45
+ elseif isa (use, IsdefinedUse)
46
+ return use. idx
47
+ else
48
+ @assert false " getuseidx: unexpected use"
49
+ end
50
+ end
29
51
30
52
function compute_live_ins (cfg:: CFG , du:: SSADefUse )
31
- uses = filter (> (0 ), du. uses)
53
+ uses = Int[]
54
+ for use in du. uses
55
+ isa (use, IsdefinedUse) && continue
56
+ push! (uses, getuseidx (use))
57
+ end
32
58
compute_live_ins (cfg, du. defs, uses)
33
59
end
34
60
@@ -89,7 +115,8 @@ function compute_value_for_block(ir::IRCode, domtree::DomTree, allblocks::Vector
89
115
def == 0 ? phinodes[curblock] : val_for_def_expr (ir, def, fidx)
90
116
end
91
117
92
- function compute_value_for_use (ir:: IRCode , domtree:: DomTree , allblocks:: Vector{Int} , du:: SSADefUse , phinodes:: IdDict{Int, SSAValue} , fidx:: Int , use:: Int )
118
+ function compute_value_for_use (ir:: IRCode , domtree:: DomTree , allblocks:: Vector{Int} ,
119
+ du:: SSADefUse , phinodes:: IdDict{Int, SSAValue} , fidx:: Int , use:: Int )
93
120
def, useblock, curblock = find_def_for_use (ir, domtree, allblocks, du, use)
94
121
if def == 0
95
122
if ! haskey (phinodes, curblock)
@@ -787,8 +814,8 @@ function sroa_pass!(ir::IRCode)
787
814
if defuses === nothing
788
815
defuses = IdDict {Int, Tuple{SPCSet, SSADefUse}} ()
789
816
end
790
- mid, defuse = get! (defuses, defidx, ( SPCSet (), SSADefUse ()))
791
- push! (defuse. ccall_preserve_uses, idx)
817
+ mid, defuse = get! (() -> ( SPCSet (),SSADefUse ()), defuses, defidx )
818
+ push! (defuse. uses, PreserveUse ( idx) )
792
819
union! (mid, intermediaries)
793
820
end
794
821
continue
@@ -846,13 +873,13 @@ function sroa_pass!(ir::IRCode)
846
873
if defuses === nothing
847
874
defuses = IdDict {Int, Tuple{SPCSet, SSADefUse}} ()
848
875
end
849
- mid, defuse = get! (defuses, def . id, ( SPCSet (), SSADefUse ()))
876
+ mid, defuse = get! (() -> ( SPCSet (),SSADefUse ()), defuses, def . id )
850
877
if is_setfield
851
878
push! (defuse. defs, idx)
852
879
elseif is_isdefined
853
- push! (defuse. uses, - idx)
880
+ push! (defuse. uses, IsdefinedUse ( idx) )
854
881
else
855
- push! (defuse. uses, idx)
882
+ push! (defuse. uses, GetfieldUse ( idx) )
856
883
end
857
884
union! (mid, intermediaries)
858
885
end
@@ -923,7 +950,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
923
950
# escapes and we cannot eliminate the allocation. This works, because we're guaranteed
924
951
# not to include any intermediaries that have dead uses. As a result, missing uses will only ever
925
952
# show up in the nuses_total count.
926
- nleaves = length (defuse. uses) + length (defuse. defs) + length (defuse . ccall_preserve_uses)
953
+ nleaves = length (defuse. uses) + length (defuse. defs)
927
954
nuses = 0
928
955
for idx in intermediaries
929
956
nuses += used_ssas[idx]
@@ -946,7 +973,14 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
946
973
fielddefuse = SSADefUse[SSADefUse () for _ = 1 : fieldcount (typ)]
947
974
all_eliminated = all_forwarded = true
948
975
for use in defuse. uses
949
- stmt = ir[SSAValue (abs (use))][:inst ] # == `getfield`/`isdefined` call
976
+ if isa (use, PreserveUse)
977
+ for fdu in fielddefuse
978
+ push! (fdu. uses, use)
979
+ end
980
+ continue
981
+ end
982
+ useidx = getuseidx (use)
983
+ stmt = ir[SSAValue (useidx)][:inst ] # == `getfield`/`isdefined` call
950
984
# We may have discovered above that this use is dead
951
985
# after the getfield elim of immutables. In that case,
952
986
# it would have been deleted. That's fine, just ignore
@@ -985,16 +1019,25 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
985
1019
allblocks = sort (vcat (phiblocks, ldu. def_bbs))
986
1020
blocks[fidx] = phiblocks, allblocks
987
1021
if fidx + 1 > length (defexpr. args)
988
- for use in du. uses
989
- if use > 0 # == `getfield` use
990
- has_safe_def (ir, get_domtree (), allblocks, du, newidx, use) || @goto skip
991
- else # == `isdefined` use
992
- if has_safe_def (ir, get_domtree (), allblocks, du, newidx, - use )
993
- ir[SSAValue (- use )][:inst ] = true
1022
+ for i = 1 : length ( du. uses)
1023
+ use = du . uses[i]
1024
+ if isa (use, IsdefinedUse)
1025
+ useidx = getuseidx ( use)
1026
+ if has_safe_def (ir, get_domtree (), allblocks, du, newidx, useidx )
1027
+ ir[SSAValue (useidx )][:inst ] = true
994
1028
else
995
1029
all_eliminated = false
996
1030
end
1031
+ continue
1032
+ elseif isa (use, PreserveUse)
1033
+ if length (du. defs) == 1 # allocation with this field unintialized
1034
+ # there is nothing to preserve, just ignore this use
1035
+ du. uses[i] = NoPreserve ()
1036
+ continue
1037
+ end
997
1038
end
1039
+ useidx = getuseidx (use)
1040
+ has_safe_def (ir, get_domtree (), allblocks, du, newidx, useidx) || @goto skip
998
1041
end
999
1042
end
1000
1043
end
@@ -1003,8 +1046,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
1003
1046
# This needs to be after we iterate through the IR with `IncrementalCompact`
1004
1047
# because removing dead blocks can invalidate the domtree.
1005
1048
domtree = get_domtree ()
1006
- preserve_uses = isempty (defuse. ccall_preserve_uses) ? nothing :
1007
- IdDict {Int, Vector{Any}} ((idx=> Any[] for idx in SPCSet (defuse. ccall_preserve_uses)))
1049
+ local preserve_uses = nothing
1008
1050
for fidx in 1 : ndefuse
1009
1051
du = fielddefuse[fidx]
1010
1052
ftyp = fieldtype (typ, fidx)
@@ -1017,17 +1059,26 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
1017
1059
end
1018
1060
# Now go through all uses and rewrite them
1019
1061
for use in du. uses
1020
- if use > 0 # == `getfield` use
1021
- ir[SSAValue (use)][:inst ] = compute_value_for_use (ir, domtree, allblocks, du, phinodes, fidx, use)
1022
- else # == `isdefined` use
1062
+ if isa (use, GetfieldUse)
1063
+ useidx = getuseidx (use)
1064
+ ir[SSAValue (useidx)][:inst ] = compute_value_for_use (ir, domtree, allblocks,
1065
+ du, phinodes, fidx, useidx)
1066
+ elseif isa (use, IsdefinedUse)
1023
1067
continue # already rewritten if possible
1024
- end
1025
- end
1026
- if ! isbitstype (ftyp)
1027
- if preserve_uses != = nothing
1028
- for (use, list) in preserve_uses
1029
- push! (list, compute_value_for_use (ir, domtree, allblocks, du, phinodes, fidx, use))
1068
+ elseif isa (use, NoPreserve)
1069
+ # nothing to preserve (may happen when there are unintialized fields)
1070
+ elseif isa (use, PreserveUse)
1071
+ useidx = getuseidx (use)
1072
+ newval = compute_value_for_use (
1073
+ ir, domtree, allblocks, du, phinodes, fidx, useidx)
1074
+ if ! isbitstype (widenconst (argextype (newval, ir)))
1075
+ if preserve_uses === nothing
1076
+ preserve_uses = IdDict {Int, Vector{Any}} ()
1077
+ end
1078
+ push! (get! (()-> Any[], preserve_uses, useidx), newval)
1030
1079
end
1080
+ else
1081
+ @assert false " sroa_mutables!: unexpected use"
1031
1082
end
1032
1083
end
1033
1084
for b in phiblocks
@@ -1054,8 +1105,9 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
1054
1105
push! (intermediaries, newidx)
1055
1106
end
1056
1107
# Insert the new preserves
1057
- for (use, new_preserves) in preserve_uses
1058
- ir[SSAValue (use)][:inst ] = form_new_preserves (ir[SSAValue (use)][:inst ]:: Expr , intermediaries, new_preserves)
1108
+ for (useidx, new_preserves) in preserve_uses
1109
+ ir[SSAValue (useidx)][:inst ] = form_new_preserves (ir[SSAValue (useidx)][:inst ]:: Expr ,
1110
+ intermediaries, new_preserves)
1059
1111
end
1060
1112
1061
1113
@label skip
0 commit comments