Skip to content

Commit 3eeafa6

Browse files
authored
optimizer: enable SROA with constant globals (#42355)
```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 ```
1 parent a8be283 commit 3eeafa6

File tree

2 files changed

+66
-10
lines changed

2 files changed

+66
-10
lines changed

base/compiler/ssair/passes.jl

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,27 @@ struct SSADefUse
1919
end
2020
SSADefUse() = SSADefUse(Int[], Int[], Int[])
2121

22-
try_compute_fieldidx_expr(typ::DataType, expr::Expr) = try_compute_fieldidx_args(typ, expr.args)
23-
function try_compute_fieldidx_args(typ::DataType, args::Vector{Any})
24-
field = args[3]
25-
isa(field, QuoteNode) && (field = field.value)
22+
function try_compute_field_stmt(compact::IncrementalCompact, stmt::Expr)
23+
field = stmt.args[3]
24+
# fields are usually literals, handle them manually
25+
if isa(field, QuoteNode)
26+
field = field.value
27+
elseif isa(field, Int)
28+
# try to resolve other constants, e.g. global reference
29+
else
30+
field = compact_exprtype(compact, field)
31+
if isa(field, Const)
32+
field = field.val
33+
else
34+
return nothing
35+
end
36+
end
2637
isa(field, Union{Int, Symbol}) || return nothing
38+
return field
39+
end
40+
41+
function try_compute_fieldidx_stmt(compact::IncrementalCompact, stmt::Expr, typ::DataType)
42+
field = try_compute_field_stmt(compact, stmt)
2743
return try_compute_fieldidx(typ, field)
2844
end
2945

@@ -636,10 +652,8 @@ function getfield_elim_pass!(ir::IRCode)
636652
else
637653
continue
638654
end
639-
## Normalize the field argument to getfield/setfield
640-
field = stmt.args[3]
641-
isa(field, QuoteNode) && (field = field.value)
642-
isa(field, Union{Int, Symbol}) || continue
655+
field = try_compute_field_stmt(compact, stmt)
656+
field === nothing && continue
643657

644658
struct_typ = unwrap_unionall(widenconst(compact_exprtype(compact, stmt.args[2])))
645659
if isa(struct_typ, Union) && struct_typ <: Tuple
@@ -779,13 +793,13 @@ function getfield_elim_pass!(ir::IRCode)
779793
# it would have been deleted. That's fine, just ignore
780794
# the use in that case.
781795
stmt === nothing && continue
782-
field = try_compute_fieldidx_expr(typ, stmt)
796+
field = try_compute_fieldidx_stmt(compact, stmt::Expr, typ)
783797
field === nothing && (ok = false; break)
784798
push!(fielddefuse[field].uses, use)
785799
end
786800
ok || continue
787801
for use in defuse.defs
788-
field = try_compute_fieldidx_expr(typ, ir[SSAValue(use)])
802+
field = try_compute_fieldidx_stmt(compact, ir[SSAValue(use)]::Expr, typ)
789803
field === nothing && (ok = false; break)
790804
push!(fielddefuse[field].defs, use)
791805
end

test/compiler/irpasses.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,45 @@ exc39508 = ErrorException("expected")
383383
return err
384384
end
385385
@test test39508() === exc39508
386+
387+
let # `getfield_elim_pass!` should work with constant globals
388+
# immutable pass
389+
src = @eval Module() begin
390+
const REF_FLD = :x
391+
struct ImmutableRef{T}
392+
x::T
393+
end
394+
395+
code_typed((Int,)) do x
396+
r = ImmutableRef{Int}(x) # should be eliminated
397+
x = getfield(r, REF_FLD) # should be eliminated
398+
return sin(x)
399+
end |> only |> first
400+
end
401+
@test !any(src.code) do @nospecialize(stmt)
402+
Meta.isexpr(stmt, :call) || return false
403+
ft = Core.Compiler.argextype(stmt.args[1], src, Any[], src.slottypes)
404+
return Core.Compiler.widenconst(ft) == typeof(getfield)
405+
end
406+
@test !any(src.code) do @nospecialize(stmt)
407+
return Meta.isexpr(stmt, :new)
408+
end
409+
410+
# mutable pass
411+
src = @eval Module() begin
412+
const REF_FLD = :x
413+
code_typed() do
414+
r = Ref{Int}(42) # should be eliminated
415+
x = getfield(r, REF_FLD) # should be eliminated
416+
return sin(x)
417+
end |> only |> first
418+
end
419+
@test !any(src.code) do @nospecialize(stmt)
420+
Meta.isexpr(stmt, :call) || return false
421+
ft = Core.Compiler.argextype(stmt.args[1], src, Any[], src.slottypes)
422+
return Core.Compiler.widenconst(ft) == typeof(getfield)
423+
end
424+
@test !any(src.code) do @nospecialize(stmt)
425+
return Meta.isexpr(stmt, :new)
426+
end
427+
end

0 commit comments

Comments
 (0)