Skip to content

Commit

Permalink
optimizer: enable SROA with constant globals (JuliaLang#42355)
Browse files Browse the repository at this point in the history
```julia
julia> const REF_FLD = :x;

julia> code_typed() do
           r = Ref{Int}(42) # should be eliminated
           x = getfield(r, REF_FLD) # should be eliminated
           return sin(x)
       end |> only
CodeInfo(
1 ─ %1 = Base.sitofp(Float64, 42)::Float64
│   %2 = invoke Base.Math.sin(%1::Float64)::Float64
└──      return %2
) => Float64
```
  • Loading branch information
aviatesk authored and LilithHafner committed Mar 8, 2022
1 parent ee7fba6 commit 56ec8fd
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 10 deletions.
34 changes: 24 additions & 10 deletions base/compiler/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,27 @@ struct SSADefUse
end
SSADefUse() = SSADefUse(Int[], Int[], Int[])

try_compute_fieldidx_expr(typ::DataType, expr::Expr) = try_compute_fieldidx_args(typ, expr.args)
function try_compute_fieldidx_args(typ::DataType, args::Vector{Any})
field = args[3]
isa(field, QuoteNode) && (field = field.value)
function try_compute_field_stmt(compact::IncrementalCompact, stmt::Expr)
field = stmt.args[3]
# fields are usually literals, handle them manually
if isa(field, QuoteNode)
field = field.value
elseif isa(field, Int)
# try to resolve other constants, e.g. global reference
else
field = compact_exprtype(compact, field)
if isa(field, Const)
field = field.val
else
return nothing
end
end
isa(field, Union{Int, Symbol}) || return nothing
return field
end

function try_compute_fieldidx_stmt(compact::IncrementalCompact, stmt::Expr, typ::DataType)
field = try_compute_field_stmt(compact, stmt)
return try_compute_fieldidx(typ, field)
end

Expand Down Expand Up @@ -636,10 +652,8 @@ function getfield_elim_pass!(ir::IRCode)
else
continue
end
## Normalize the field argument to getfield/setfield
field = stmt.args[3]
isa(field, QuoteNode) && (field = field.value)
isa(field, Union{Int, Symbol}) || continue
field = try_compute_field_stmt(compact, stmt)
field === nothing && continue

struct_typ = unwrap_unionall(widenconst(compact_exprtype(compact, stmt.args[2])))
if isa(struct_typ, Union) && struct_typ <: Tuple
Expand Down Expand Up @@ -779,13 +793,13 @@ function getfield_elim_pass!(ir::IRCode)
# it would have been deleted. That's fine, just ignore
# the use in that case.
stmt === nothing && continue
field = try_compute_fieldidx_expr(typ, stmt)
field = try_compute_fieldidx_stmt(compact, stmt::Expr, typ)
field === nothing && (ok = false; break)
push!(fielddefuse[field].uses, use)
end
ok || continue
for use in defuse.defs
field = try_compute_fieldidx_expr(typ, ir[SSAValue(use)])
field = try_compute_fieldidx_stmt(compact, ir[SSAValue(use)]::Expr, typ)
field === nothing && (ok = false; break)
push!(fielddefuse[field].defs, use)
end
Expand Down
42 changes: 42 additions & 0 deletions test/compiler/irpasses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -383,3 +383,45 @@ exc39508 = ErrorException("expected")
return err
end
@test test39508() === exc39508

let # `getfield_elim_pass!` should work with constant globals
# immutable pass
src = @eval Module() begin
const REF_FLD = :x
struct ImmutableRef{T}
x::T
end

code_typed((Int,)) do x
r = ImmutableRef{Int}(x) # should be eliminated
x = getfield(r, REF_FLD) # should be eliminated
return sin(x)
end |> only |> first
end
@test !any(src.code) do @nospecialize(stmt)
Meta.isexpr(stmt, :call) || return false
ft = Core.Compiler.argextype(stmt.args[1], src, Any[], src.slottypes)
return Core.Compiler.widenconst(ft) == typeof(getfield)
end
@test !any(src.code) do @nospecialize(stmt)
return Meta.isexpr(stmt, :new)
end

# mutable pass
src = @eval Module() begin
const REF_FLD = :x
code_typed() do
r = Ref{Int}(42) # should be eliminated
x = getfield(r, REF_FLD) # should be eliminated
return sin(x)
end |> only |> first
end
@test !any(src.code) do @nospecialize(stmt)
Meta.isexpr(stmt, :call) || return false
ft = Core.Compiler.argextype(stmt.args[1], src, Any[], src.slottypes)
return Core.Compiler.widenconst(ft) == typeof(getfield)
end
@test !any(src.code) do @nospecialize(stmt)
return Meta.isexpr(stmt, :new)
end
end

0 comments on commit 56ec8fd

Please sign in to comment.