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{Int}` are all instances of `getfield` / `isdefined` on 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
@@ -27,7 +27,10 @@ struct SSADefUse
2727end
2828SSADefUse () = SSADefUse (Int[], Int[], Int[])
2929
30- compute_live_ins (cfg:: CFG , du:: SSADefUse ) = compute_live_ins (cfg, du. defs, du. uses)
30+ function compute_live_ins (cfg:: CFG , du:: SSADefUse )
31+ # filter out `isdefined` usages
32+ return compute_live_ins (cfg, du. defs, filter (> (0 ), du. uses))
33+ end
3134
3235# assume `stmt == getfield(obj, field, ...)` or `stmt == setfield!(obj, field, val, ...)`
3336try_compute_field_stmt (ir:: Union{IncrementalCompact,IRCode} , stmt:: Expr ) =
@@ -725,7 +728,7 @@ function sroa_pass!(ir::IRCode)
725728 for ((_, idx), stmt) in compact
726729 # check whether this statement is `getfield` / `setfield!` (or other "interesting" statement)
727730 isa (stmt, Expr) || continue
728- is_setfield = false
731+ is_setfield = is_isdefined = false
729732 field_ordering = :unspecified
730733 if is_known_call (stmt, setfield!, compact)
731734 4 <= length (stmt. args) <= 5 || continue
@@ -741,6 +744,13 @@ function sroa_pass!(ir::IRCode)
741744 field_ordering = argextype (stmt. args[4 ], compact)
742745 widenconst (field_ordering) === Bool && (field_ordering = :unspecified )
743746 end
747+ elseif is_known_call (stmt, isdefined, compact)
748+ 3 <= length (stmt. args) <= 4 || continue
749+ is_isdefined = true
750+ if length (stmt. args) == 4
751+ field_ordering = argextype (stmt. args[4 ], compact)
752+ widenconst (field_ordering) === Bool && (field_ordering = :unspecified )
753+ end
744754 elseif isexpr (stmt, :foreigncall )
745755 nccallargs = length (stmt. args[3 ]:: SimpleVector )
746756 preserved = Int[]
@@ -795,13 +805,11 @@ function sroa_pass!(ir::IRCode)
795805 lift_comparison! (=== , compact, idx, stmt, lifting_cache)
796806 elseif is_known_call (stmt, isa, compact)
797807 lift_comparison! (isa, compact, idx, stmt, lifting_cache)
798- elseif is_known_call (stmt, isdefined, compact)
799- lift_comparison! (isdefined, compact, idx, stmt, lifting_cache)
800808 end
801809 continue
802810 end
803811
804- # analyze this `getfield` / `setfield!` call
812+ # analyze this `getfield` / `isdefined` / ` setfield!` call
805813
806814 field = try_compute_field_stmt (compact, stmt)
807815 field === nothing && continue
@@ -812,10 +820,15 @@ function sroa_pass!(ir::IRCode)
812820 if isa (struct_typ, Union) && struct_typ <: Tuple
813821 struct_typ = unswitchtupleunion (struct_typ)
814822 end
823+ if isa (struct_typ, Union) && is_isdefined
824+ lift_comparison! (isdefined, compact, idx, stmt, lifting_cache)
825+ continue
826+ end
815827 isa (struct_typ, DataType) || continue
816828
817829 struct_typ. name. atomicfields == C_NULL || continue # TODO : handle more
818- if ! (field_ordering === :unspecified || (field_ordering isa Const && field_ordering. val === :not_atomic ))
830+ if ! ((field_ordering === :unspecified ) ||
831+ (field_ordering isa Const && field_ordering. val === :not_atomic ))
819832 continue
820833 end
821834
@@ -836,6 +849,8 @@ function sroa_pass!(ir::IRCode)
836849 mid, defuse = get! (defuses, def. id, (SPCSet (), SSADefUse ()))
837850 if is_setfield
838851 push! (defuse. defs, idx)
852+ elseif is_isdefined
853+ push! (defuse. uses, - idx)
839854 else
840855 push! (defuse. uses, idx)
841856 end
@@ -844,6 +859,8 @@ function sroa_pass!(ir::IRCode)
844859 continue
845860 elseif is_setfield
846861 continue # invalid `setfield!` call, but just ignore here
862+ elseif is_isdefined
863+ continue # TODO ?
847864 end
848865
849866 # perform SROA on immutable structs here on
@@ -927,9 +944,9 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
927944 typ = typ:: DataType
928945 # Partition defuses by field
929946 fielddefuse = SSADefUse[SSADefUse () for _ = 1 : fieldcount (typ)]
930- all_forwarded = true
947+ all_eliminated = all_forwarded = true
931948 for use in defuse. uses
932- stmt = ir[SSAValue (use)][:inst ] # == `getfield` call
949+ stmt = ir[SSAValue (abs ( use)) ][:inst ] # == `getfield`/`isdefined ` call
933950 # We may have discovered above that this use is dead
934951 # after the getfield elim of immutables. In that case,
935952 # it would have been deleted. That's fine, just ignore
@@ -969,7 +986,15 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
969986 blocks[fidx] = phiblocks, allblocks
970987 if fidx + 1 > length (defexpr. args)
971988 for use in du. uses
972- has_safe_def (ir, get_domtree (), allblocks, du, newidx, use) || @goto skip
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
994+ else
995+ all_eliminated = false
996+ end
997+ end
973998 end
974999 end
9751000 end
@@ -991,8 +1016,12 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
9911016 NewInstruction (PhiNode (), ftyp))
9921017 end
9931018 # Now go through all uses and rewrite them
994- for stmt in du. uses
995- ir[SSAValue (stmt)][:inst ] = compute_value_for_use (ir, domtree, allblocks, du, phinodes, fidx, stmt)
1019+ 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
1023+ continue # already rewritten if possible
1024+ end
9961025 end
9971026 if ! isbitstype (ftyp)
9981027 if preserve_uses != = nothing
@@ -1010,6 +1039,10 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int, Tuple{SPCSet, SSADefUse
10101039 end
10111040 end
10121041 end
1042+ all_eliminated || continue
1043+ # all "usages" (i.e. `getfield` and `isdefined` calls) are eliminated,
1044+ # now eliminate "definitions" (`setfield!`) calls
1045+ # (NOTE the allocation itself will be eliminated by DCE pass later)
10131046 for stmt in du. defs
10141047 stmt == newidx && continue
10151048 ir[SSAValue (stmt)][:inst ] = nothing
0 commit comments