@@ -60,7 +60,7 @@ the type of `SlotNumber(cnd.slot)` will be limited by `cnd.thentype`
60
60
and in the false branch, it will be limited by `cnd.elsetype`.
61
61
Example:
62
62
```julia
63
- let cond = isa(x::Union{Int, Float}, Int)::Conditional(x, Int, Float)
63
+ let cond = isa(x::Union{Int, Float}, Int)::Conditional(x, _, Int, Float)
64
64
if cond
65
65
# May assume x is `Int` now
66
66
else
71
71
"""
72
72
struct Conditional
73
73
slot:: Int
74
+ ssadef:: Int
74
75
thentype
75
76
elsetype
76
77
# `isdefined` indicates this `Conditional` is from `@isdefined slot`, implying that
77
78
# the `undef` information of `slot` can be improved in the then branch.
78
79
# Since this is only beneficial for local inference, it is not translated into `InterConditional`.
79
80
isdefined:: Bool
80
- function Conditional (slot:: Int , @nospecialize (thentype), @nospecialize (elsetype);
81
+ function Conditional (slot:: Int , ssadef :: Int , @nospecialize (thentype), @nospecialize (elsetype);
81
82
isdefined:: Bool = false )
82
83
assert_nested_slotwrapper (thentype)
83
84
assert_nested_slotwrapper (elsetype)
84
- return new (slot, thentype, elsetype, isdefined)
85
+ return new (slot, ssadef, thentype, elsetype, isdefined)
85
86
end
86
87
end
87
- Conditional (var:: SlotNumber , @nospecialize (thentype), @nospecialize (elsetype); isdefined:: Bool = false ) =
88
- Conditional (slot_id (var), thentype, elsetype; isdefined)
88
+ Conditional (var:: SlotNumber , ssadef :: Int , @nospecialize (thentype), @nospecialize (elsetype); isdefined:: Bool = false ) =
89
+ Conditional (slot_id (var), ssadef, thentype, elsetype; isdefined)
89
90
90
91
import Core: InterConditional
91
92
"""
@@ -105,8 +106,10 @@ InterConditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetyp
105
106
InterConditional (slot_id (var), thentype, elsetype)
106
107
107
108
const AnyConditional = Union{Conditional,InterConditional}
108
- Conditional (cnd:: InterConditional ) = Conditional (cnd. slot, cnd. thentype, cnd. elsetype)
109
- InterConditional (cnd:: Conditional ) = InterConditional (cnd. slot, cnd. thentype, cnd. elsetype)
109
+ function InterConditional (cnd:: Conditional )
110
+ @assert cnd. ssadef == 0
111
+ InterConditional (cnd. slot, cnd. thentype, cnd. elsetype)
112
+ end
110
113
111
114
"""
112
115
alias::MustAlias
184
187
struct StateUpdate
185
188
var:: SlotNumber
186
189
vtype:: VarState
187
- conditional:: Bool
188
- StateUpdate (var:: SlotNumber , vtype:: VarState , conditional:: Bool = false ) = new (var, vtype, conditional)
190
+ end
191
+
192
+ """
193
+ Similar to `StateUpdate`, except with the additional guarantee that object identity
194
+ is preserved by the update (i.e. `x (before) === x (after)`).
195
+ """
196
+ struct StateRefinement
197
+ slot:: Int
198
+ # XXX : This should be an intersection of the old type with the new
199
+ # (i.e. newtyp ⊑ oldtyp)
200
+ newtyp
201
+ undef:: Bool
202
+
203
+ StateRefinement (slot:: Int , @nospecialize (newtyp), undef:: Bool ) = new (slot, newtyp, undef)
189
204
end
190
205
191
206
"""
328
343
return false
329
344
end
330
345
346
+ is_same_conditionals (a:: Conditional , b:: Conditional ) = a. slot == b. slot && a. ssadef == b. ssadef
331
347
is_same_conditionals (a:: C , b:: C ) where C<: AnyConditional = a. slot == b. slot
332
348
333
349
@nospecializeinfer is_lattice_bool (lattice:: AbstractLattice , @nospecialize (typ)) = typ != = Bottom && ⊑ (lattice, typ, Bool)
387
403
elsefields === nothing || (elsefields[i] = elsetype)
388
404
end
389
405
end
390
- return Conditional (slot,
406
+ return Conditional (slot, typemin (Int), # TODO
391
407
thenfields === nothing ? Bottom : PartialStruct (vartyp. typ, thenfields),
392
408
elsefields === nothing ? Bottom : PartialStruct (vartyp. typ, elsefields))
393
409
else
404
420
elsefields === nothing || push! (elsefields, t)
405
421
end
406
422
end
407
- return Conditional (slot,
423
+ return Conditional (slot, typemin (Int),
408
424
thenfields === nothing ? Bottom : PartialStruct (vartyp_widened, thenfields),
409
425
elsefields === nothing ? Bottom : PartialStruct (vartyp_widened, elsefields))
410
426
end
@@ -745,34 +761,39 @@ widenconst(::LimitedAccuracy) = error("unhandled LimitedAccuracy")
745
761
# state management #
746
762
# ###################
747
763
748
- function smerge (lattice:: AbstractLattice , sa:: Union{NotFound,VarState} , sb:: Union{NotFound,VarState} )
764
+ function smerge (lattice:: AbstractLattice , sa:: Union{NotFound,VarState} , sb:: Union{NotFound,VarState} , join_pc :: Int )
749
765
sa === sb && return sa
750
766
sa === NOT_FOUND && return sb
751
767
sb === NOT_FOUND && return sa
752
- return VarState (tmerge (lattice, sa. typ, sb. typ), sa. undef | sb. undef)
768
+ return VarState (tmerge (lattice, sa. typ, sb. typ), sa. ssadef == sb . ssadef ? sa . ssadef : join_pc, sa . undef | sb. undef)
753
769
end
754
770
755
- @nospecializeinfer @inline schanged (lattice:: AbstractLattice , @nospecialize (n), @nospecialize (o)) =
756
- (n != = o) && (o === NOT_FOUND || (n != = NOT_FOUND && ! (n. undef <= o. undef && ⊑ (lattice, n. typ, o. typ))))
771
+ @nospecializeinfer @inline schanged (lattice:: AbstractLattice , @nospecialize (n), @nospecialize (o), join_pc :: Int ) =
772
+ (n != = o) && (o === NOT_FOUND || (n != = NOT_FOUND && ! (n. undef <= o. undef && (n . ssadef == o . ssadef || o . ssadef == join_pc) && ⊑ (lattice, n. typ, o. typ))))
757
773
758
774
# remove any lattice elements that wrap the reassigned slot object from the vartable
759
- function invalidate_slotwrapper (vt:: VarState , changeid:: Int , ignore_conditional :: Bool )
775
+ function invalidate_slotwrapper (vt:: VarState , changeid:: Int )
760
776
newtyp = ignorelimited (vt. typ)
761
- if (! ignore_conditional && isa (newtyp, Conditional) && newtyp. slot == changeid) ||
762
- (isa (newtyp, MustAlias) && newtyp. slot == changeid)
777
+ if (( isa (newtyp, Conditional) && newtyp. slot == changeid) ||
778
+ (isa (newtyp, MustAlias) && newtyp. slot == changeid) )
763
779
newtyp = @noinline widenwrappedslotwrapper (vt. typ)
764
- return VarState (newtyp, vt. undef)
780
+ return VarState (newtyp, vt. ssadef, vt . undef)
765
781
end
766
782
return nothing
767
783
end
768
784
769
- function stupdate! (lattice:: AbstractLattice , state:: VarTable , changes:: VarTable )
785
+ function stupdate! (lattice:: AbstractLattice , state:: VarTable , changes:: VarTable , join_pc :: Int )
770
786
changed = false
771
787
for i = 1 : length (state)
772
788
newtype = changes[i]
773
789
oldtype = state[i]
774
- if schanged (lattice, newtype, oldtype)
775
- state[i] = smerge (lattice, oldtype, newtype)
790
+ # In addition to computing the type, the merge here computes the "reaching definition"
791
+ # for a slot. The provided `join_pc` is a "virtual" PC, which corresponds to the ϕ-block
792
+ # that would exist at the beginning of the BasicBlock.
793
+ #
794
+ # This effectively applies the "path-convergence criterion" for SSA construction.
795
+ if schanged (lattice, newtype, oldtype, join_pc)
796
+ state[i] = smerge (lattice, oldtype, newtype, join_pc)
776
797
changed = true
777
798
end
778
799
end
789
810
function stoverwrite1! (state:: VarTable , change:: StateUpdate )
790
811
changeid = slot_id (change. var)
791
812
for i = 1 : length (state)
792
- invalidated = invalidate_slotwrapper (state[i], changeid, change . conditional )
813
+ invalidated = invalidate_slotwrapper (state[i], changeid)
793
814
if invalidated != = nothing
794
815
state[i] = invalidated
795
816
end
@@ -799,3 +820,9 @@ function stoverwrite1!(state::VarTable, change::StateUpdate)
799
820
state[changeid] = newtype
800
821
return state
801
822
end
823
+
824
+ function strefine1! (state:: VarTable , refinement:: StateRefinement )
825
+ (; newtyp, undef, slot) = refinement
826
+ state[slot] = VarState (newtyp, state[slot]. ssadef, undef)
827
+ return state
828
+ end
0 commit comments