Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

builtin: add ifelse handling #4

Merged
merged 1 commit into from
Jun 30, 2021
Merged

builtin: add ifelse handling #4

merged 1 commit into from
Jun 30, 2021

Conversation

aviatesk
Copy link
Owner

No description provided.

@aviatesk aviatesk changed the title add ifelse handling builtin: add ifelse handling Jun 30, 2021
@aviatesk aviatesk requested a review from TH3CHARLie June 30, 2021 09:17
Copy link
Collaborator

@TH3CHARLie TH3CHARLie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Only one minor comment or confusion.

@@ -216,6 +217,20 @@ function find_escapes(ir::IRCode, nargs::Int)
continue
elseif ft === typeof(isa) || ft === typeof(typeof) || ft === typeof(Core.sizeof)
continue
elseif ft === typeof(ifelse) && length(stmt.args) === 4
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if length(stmt.args) == 4 would be faster than this and if this check has already done before our analysis (i.e. any ifelse call without 4 args will fail)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I think we can remove this kind of check once we tackle #6 (, where we will assert a call won't throw at runtime ahead of this check, and it will have filtered out those malformed builtin calls).
For now, I'd like to preserve this since otherwise we may encounter BoundsError in the next line.

@aviatesk aviatesk force-pushed the avi/ifelse branch 2 times, most recently from 1e6418b to d31a460 Compare June 30, 2021 11:48
@aviatesk aviatesk merged commit dc4e8a5 into master Jun 30, 2021
@aviatesk aviatesk deleted the avi/ifelse branch June 30, 2021 11:56
aviatesk added a commit that referenced this pull request Nov 15, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, although it is still analyzed as `ThrownEscape`.
aviatesk added a commit that referenced this pull request Nov 15, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, although it is still analyzed as `ThrownEscape`.
aviatesk added a commit that referenced this pull request Nov 15, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 15, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 15, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 15, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 15, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 15, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 16, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 16, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 16, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 16, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 16, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 16, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 17, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 17, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 17, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 18, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 19, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 19, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 19, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 23, 2021
This commit implements a simple, flow-insensitive alias analysis using an
approach inspired by the escape analysis algorithm explained in the old JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" `state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an aliased value later.
Note that in a case when the fields of object `x` can't known precisely (i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation since escape information
imposed on `x` will end up being propagated to all of its fields anyway at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}                                                                                                                                        │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                                                                                                                                                  │╻       broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                                                                                                                                                   ││
  ◌ └──      goto #3 if not true                                                                                                                                                                           ││╻╷      materialize
  ◌ 2 ─      nothing::Nothing                                                                                                                                                                              │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                                                                                                                                                                 │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                                                                                                                                                       ││││┃       getindex
  ◌ 4 ─      goto #5                                                                                                                                                                                       ││││
  ◌ 5 ─      goto #6                                                                                                                                                                                       │││
  ◌ 6 ─      goto #7                                                                                                                                                                                       ││
  ◌ 7 ─      return %6                                                                                                                                                                                     │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still `_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 24, 2021
This commit implements a simple, flow-insensitive alias analysis using 
an
approach inspired by the escape analysis algorithm explained in the old 
JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible 
field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" 
`state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA 
statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa 
Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered 
in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information 
imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an 
aliased value later.
Note that in a case when the fields of object `x` can't known precisely 
(i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is 
propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation 
since escape information
imposed on `x` will end up being propagated to all of its fields anyway 
at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and 
Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}      
                                                                         
                                                         │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                
                                                                         
                                                         │╻       
broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                 
                                                                         
                                                         ││
  ◌ └──      goto #3 if not true                                         
                                                                         
                                                         ││╻╷      
materialize
  ◌ 2 ─      nothing::Nothing                                            
                                                                         
                                                         │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                               
                                                                         
                                                         │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                     
                                                                         
                                                         ││││┃       
getindex
  ◌ 4 ─      goto #5                                                     
                                                                         
                                                         ││││
  ◌ 5 ─      goto #6                                                     
                                                                         
                                                         │││
  ◌ 6 ─      goto #7                                                     
                                                                         
                                                         ││
  ◌ 7 ─      return %6                                                   
                                                                         
                                                         │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, 
Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still 
`_2` is analyzed so.
aviatesk added a commit that referenced this pull request Nov 24, 2021
This commit implements a simple, flow-insensitive alias analysis using 
an
approach inspired by the escape analysis algorithm explained in the old 
JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible 
field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set" 
`state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA 
statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa 
Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered 
in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information 
imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an 
aliased value later.
Note that in a case when the fields of object `x` can't known precisely 
(i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is 
propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation 
since escape information
imposed on `x` will end up being propagated to all of its fields anyway 
at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and 
Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}      
                                                                         
                                                         │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}                
                                                                         
                                                         │╻       
broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}                 
                                                                         
                                                         ││
  ◌ └──      goto #3 if not true                                         
                                                                         
                                                         ││╻╷      
materialize
  ◌ 2 ─      nothing::Nothing                                            
                                                                         
                                                         │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String                               
                                                                         
                                                         │││╻╷╷╷╷   copy
  ◌ └──      goto #4                                                     
                                                                         
                                                         ││││┃       
getindex
  ◌ 4 ─      goto #5                                                     
                                                                         
                                                         ││││
  ◌ 5 ─      goto #6                                                     
                                                                         
                                                         │││
  ◌ 6 ─      goto #7                                                     
                                                                         
                                                         ││
  ◌ 7 ─      return %6                                                   
                                                                         
                                                         │

julia> EscapeAnalysis.get_aliases(result.state.aliasset, 
Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still 
`_2` is analyzed so.
aviatesk added a commit that referenced this pull request Dec 21, 2021
This commit implements a simple, flow-insensitive alias analysis using
an
approach inspired by the escape analysis algorithm explained in the old
JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible
field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set"
`state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA
statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa
Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered
in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information
imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an
aliased value later.
Note that in a case when the fields of object `x` can't known precisely
(i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is
propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation
since escape information
imposed on `x` will end up being propagated to all of its fields anyway
at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and
Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}

                                                         │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}

                                                         │╻
broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}

                                                         ││
  ◌ └──      goto #3 if not true

                                                         ││╻╷
materialize
  ◌ 2 ─      nothing::Nothing

                                                         │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String

                                                         │││╻╷╷╷╷   copy
  ◌ └──      goto #4

                                                         ││││┃
getindex
  ◌ 4 ─      goto #5

                                                         ││││
  ◌ 5 ─      goto #6

                                                         │││
  ◌ 6 ─      goto #7

                                                         ││
  ◌ 7 ─      return %6

                                                         │

julia> EscapeAnalysis.get_aliases(result.state.aliasset,
Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still
`_2` is analyzed so.
aviatesk added a commit that referenced this pull request Dec 23, 2021
This commit implements a simple, flow-insensitive alias analysis using
an
approach inspired by the escape analysis algorithm explained in the old
JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible
field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set"
`state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA
statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa
Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered
in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information
imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an
aliased value later.
Note that in a case when the fields of object `x` can't known precisely
(i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is
propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation
since escape information
imposed on `x` will end up being propagated to all of its fields anyway
at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and
Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}

                                                         │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}

                                                         │╻
broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}

                                                         ││
  ◌ └──      goto #3 if not true

                                                         ││╻╷
materialize
  ◌ 2 ─      nothing::Nothing

                                                         │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String

                                                         │││╻╷╷╷╷   copy
  ◌ └──      goto #4

                                                         ││││┃
getindex
  ◌ 4 ─      goto #5

                                                         ││││
  ◌ 5 ─      goto #6

                                                         │││
  ◌ 6 ─      goto #7

                                                         ││
  ◌ 7 ─      return %6

                                                         │

julia> EscapeAnalysis.get_aliases(result.state.aliasset,
Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still
`_2` is analyzed so.
aviatesk added a commit that referenced this pull request Dec 24, 2021
This commit implements a simple, flow-insensitive alias analysis using
an
approach inspired by the escape analysis algorithm explained in the old
JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible
field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set"
`state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA
statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa
Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered
in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information
imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an
aliased value later.
Note that in a case when the fields of object `x` can't known precisely
(i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is
propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation
since escape information
imposed on `x` will end up being propagated to all of its fields anyway
at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and
Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}

                                                         │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}

                                                         │╻
broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}

                                                         ││
  ◌ └──      goto #3 if not true

                                                         ││╻╷
materialize
  ◌ 2 ─      nothing::Nothing

                                                         │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String

                                                         │││╻╷╷╷╷   copy
  ◌ └──      goto #4

                                                         ││││┃
getindex
  ◌ 4 ─      goto #5

                                                         ││││
  ◌ 5 ─      goto #6

                                                         │││
  ◌ 6 ─      goto #7

                                                         ││
  ◌ 7 ─      return %6

                                                         │

julia> EscapeAnalysis.get_aliases(result.state.aliasset,
Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still
`_2` is analyzed so.
aviatesk added a commit that referenced this pull request Dec 25, 2021
This commit implements a simple, flow-insensitive alias analysis using
an
approach inspired by the escape analysis algorithm explained in the old
JVM paper [^JVM05].

`EscapeLattice` is extended so that it also keeps track of possible
field values.
In more detail, `x::EscapeLattice` has the new field called
`x.FieldSet::Union{Vector{IdSet{Any}},Bool}`, where:
- `x.FieldSets === false` indicates the fields of `x` isn't analyzed yet
- `x.FieldSets === true` indicates the fields of `x` can't be analyzed,
  e.g. the type of `x` is not concrete and thus the number of its fields
  can't known precisely
- otherwise `x.FieldSets::Vector{IdSet{Any}}` holds all the possible
  values of each field, where `x.FieldSets[i]` keeps all possibilities
  that the `i`th field can be

And now, in addition to managing escape lattice elements, the analysis
state also maintains an "alias set"
`state.aliasset::IntDisjointSet{Int}`,
which is implemented as a disjoint set of aliased arguments and SSA
statements.
When the fields of object `x` are known precisely (i.e. `x.FieldSets isa
Vector{IdSet{Any}}` holds),
the alias set is updated each time `z = getfield(x, y)` is encountered
in a way that `z` is
aliased to all values of `x.FieldSets[y]`, so that escape information
imposed on `z` will be
propagated to all the aliased values and `z` can be replaced with an
aliased value later.
Note that in a case when the fields of object `x` can't known precisely
(i.e. `x.FieldSets` is `true`),
when `z = getfield(x, y)` is analyzed, escape information of `z` is
propagated to `x` rather
than any of `x`'s fields, which is the most conservative propagation
since escape information
imposed on `x` will end up being propagated to all of its fields anyway
at definitions of `x`
(i.e. `:new` expression or `setfield!` call).

[^JVM05]: Escape Analysis in the Context of Dynamic Compilation and
Deoptimization.
          Thomas Kotzmann and Hanspeter Mössenböck, 2005, June.
          <https://dl.acm.org/doi/10.1145/1064979.1064996>.

Now this alias analysis should allow us to implement a "stronger" SROA,
which eliminates the allocation of `r` within the following code:
```julia
julia> result = analyze_escapes((String,)) do s
           r = Ref(s)
           broadcast(identity, r)
       end
\#3(_2::String *, _3::Base.RefValue{String} ◌) in Main at REPL[2]:2
2 ↓ 1 ─ %1 = %new(Base.RefValue{String}, _2)::Base.RefValue{String}

                                                         │╻╷╷     Ref
3 ✓ │   %2 = Core.tuple(%1)::Tuple{Base.RefValue{String}}

                                                         │╻
broadcast
  ↓ │   %3 = Core.getfield(%2, 1)::Base.RefValue{String}

                                                         ││
  ◌ └──      goto #3 if not true

                                                         ││╻╷
materialize
  ◌ 2 ─      nothing::Nothing

                                                         │
  * 3 ┄ %6 = Base.getfield(%3, :x)::String

                                                         │││╻╷╷╷╷   copy
  ◌ └──      goto #4

                                                         ││││┃
getindex
  ◌ 4 ─      goto #5

                                                         ││││
  ◌ 5 ─      goto #6

                                                         │││
  ◌ 6 ─      goto #7

                                                         ││
  ◌ 7 ─      return %6

                                                         │

julia> EscapeAnalysis.get_aliases(result.state.aliasset,
Core.SSAValue(6), result.ir)
2-element Vector{Union{Core.Argument, Core.SSAValue}}:
 Core.Argument(2)
 :(%6)
```
Note that the allocation `%1` isn't analyzed as `ReturnEscape`, still
`_2` is analyzed so.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants