diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 23517ea3ed8604..07901f8c2f0a2f 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -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 @@ -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 @@ -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 diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index c4e3023184c13c..cb464a51ce45c8 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -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