1010 du::SSADefUse
1111
1212This 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
1414- `du.defs::Vector{Int}` are all instances of `setfield!` on the struct
1515The terminology refers to the uses/defs of the "slot bundle" that the mutable struct represents.
1616
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.
2223"""
2324struct SSADefUse
24- uses:: Vector{Int }
25+ uses:: Vector{Any }
2526 defs:: Vector{Int}
26- ccall_preserve_uses:: Vector{Int}
2727end
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
2951
3052function 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
3258 compute_live_ins (cfg, du. defs, uses)
3359end
3460
@@ -89,7 +115,8 @@ function compute_value_for_block(ir::IRCode, domtree::DomTree, allblocks::Vector
89115 def == 0 ? phinodes[curblock] : val_for_def_expr (ir, def, fidx)
90116end
91117
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 )
93120 def, useblock, curblock = find_def_for_use (ir, domtree, allblocks, du, use)
94121 if def == 0
95122 if ! haskey (phinodes, curblock)
@@ -787,8 +814,8 @@ function sroa_pass!(ir::IRCode)
787814 if defuses === nothing
788815 defuses = IdDict {Int, Tuple{SPCSet, SSADefUse}} ()
789816 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) )
792819 union! (mid, intermediaries)
793820 end
794821 continue
@@ -846,13 +873,13 @@ function sroa_pass!(ir::IRCode)
846873 if defuses === nothing
847874 defuses = IdDict {Int, Tuple{SPCSet, SSADefUse}} ()
848875 end
849- mid, defuse = get! (defuses, def . id, ( SPCSet (), SSADefUse ()))
876+ mid, defuse = get! (() -> ( SPCSet (),SSADefUse ()), defuses, def . id )
850877 if is_setfield
851878 push! (defuse. defs, idx)
852879 elseif is_isdefined
853- push! (defuse. uses, - idx)
880+ push! (defuse. uses, IsdefinedUse ( idx) )
854881 else
855- push! (defuse. uses, idx)
882+ push! (defuse. uses, GetfieldUse ( idx) )
856883 end
857884 union! (mid, intermediaries)
858885 end
@@ -923,7 +950,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
923950 # escapes and we cannot eliminate the allocation. This works, because we're guaranteed
924951 # not to include any intermediaries that have dead uses. As a result, missing uses will only ever
925952 # 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)
927954 nuses = 0
928955 for idx in intermediaries
929956 nuses += used_ssas[idx]
@@ -946,7 +973,14 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
946973 fielddefuse = SSADefUse[SSADefUse () for _ = 1 : fieldcount (typ)]
947974 all_eliminated = all_forwarded = true
948975 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
950984 # We may have discovered above that this use is dead
951985 # after the getfield elim of immutables. In that case,
952986 # 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
9851019 allblocks = sort (vcat (phiblocks, ldu. def_bbs))
9861020 blocks[fidx] = phiblocks, allblocks
9871021 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
9941028 else
9951029 all_eliminated = false
9961030 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
9971038 end
1039+ useidx = getuseidx (use)
1040+ has_safe_def (ir, get_domtree (), allblocks, du, newidx, useidx) || @goto skip
9981041 end
9991042 end
10001043 end
@@ -1003,8 +1046,7 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
10031046 # This needs to be after we iterate through the IR with `IncrementalCompact`
10041047 # because removing dead blocks can invalidate the domtree.
10051048 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
10081050 for fidx in 1 : ndefuse
10091051 du = fielddefuse[fidx]
10101052 ftyp = fieldtype (typ, fidx)
@@ -1017,17 +1059,26 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
10171059 end
10181060 # Now go through all uses and rewrite them
10191061 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)
10231067 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)
10301079 end
1080+ else
1081+ @assert false " sroa_mutables!: unexpected use"
10311082 end
10321083 end
10331084 for b in phiblocks
@@ -1054,8 +1105,9 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
10541105 push! (intermediaries, newidx)
10551106 end
10561107 # 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)
10591111 end
10601112
10611113 @label skip
0 commit comments