Skip to content

SROA pass generates invalid IR when BasicBlocks are out of dominance order #53013

Open
@topolarity

Description

@topolarity

This is basically a hand-written version of #53011 but the bug itself is different

include("test/compiler/irutils.jl")

mutable struct Foo
    x
end

getfield_mi = only(Base.method_instances(Base.getfield, Tuple{Foo, Symbol}, Base.get_world_counter()))
setfield!_mi = only(Base.method_instances(Base.setfield!, Tuple{Foo, Symbol, Any}, Base.get_world_counter()))

let code = Any[
        # Block 1
        #= %1 =# Expr(:new, GlobalRef(Main, :Foo), Core.Compiler.Argument(2)),
        #= %2 =# Core.Compiler.GotoIfNot(false, 5),
        # Block 2
        #= %3 =# Expr(:call, GlobalRef(Base, :getfield), Core.SSAValue(1), Core.QuoteNode(:x)),
        #= %4 =# Core.Compiler.ReturnNode(Core.SSAValue(3)),
        # Block 3
        #= %5 =# Expr(:call, GlobalRef(Base, :getfield), Core.SSAValue(1), Core.QuoteNode(:x)),
        #= %6 =# Expr(:call, GlobalRef(Base, :(+)), Core.SSAValue(5), 1),
        #= %7 =# Expr(:call, GlobalRef(Base, :setfield!), Core.SSAValue(1), Core.QuoteNode(:x), Core.SSAValue(6)),
        #= %8 =# Core.Compiler.GotoNode(3),
    ]
    ir = make_ircode(code)
    ir.stmts.type[1] = Main.Foo
    push!(ir.argtypes, Tuple{})
    push!(ir.argtypes, Int64)

    interp = Core.Compiler.NativeInterpreter()
    inlining = Core.Compiler.InliningState(interp)

    ir = Core.Compiler.compact!(ir, true)
    println(ir)

    ir = Core.Compiler.sroa_pass!(ir, inlining)
    println(ir)

    Core.Compiler.verify_ir(ir)
end

In #53011, the bug is that compaction inserts the bad PhiNode. In this case, it's the SROA pass itself that is misbehaving.

First, compaction deletes the unreachable edge from BB1, leaving the blocks out of dominance order:

 1%1 = %new(Main.Foo, _2)::Foo                                                                                                │
 └──      goto #3                                                                                                                │
 2%3 = Base.getfield(%1, :x)::Any                                                                                             │
 └──      return %33%5 = Base.getfield(%1, :x)::Any                                                                                             │
 │   %6 = (%5 + 1)::Any                                                                                                          │
 │        Base.setfield!(%1, :x, %6)::Any                                                                                        │
 └──      goto #2

Then, the SROA pass constructs a domtree that reports that BB3 dominates BB2, and inserts an invalid SSAValue:

 1%1 = %new(Main.Foo, _2)::Foo                                                                                                │
└──      goto #3                                                                                                                │
2%3 = %6::Any                                                                                                                │
└──      return %33%5 = _2::Any                                                                                                                │
│   %6 = (%5 + 1)::Any                                                                                                          │
│        Base.setfield!(%1, :x, %6)::Any                                                                                        │
└──      goto #2

Finally, the IR verifier makes the same mistake and accepts this code even though codegen will mis-compile this

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugIndicates an unexpected problem or unintended behaviorcompiler:optimizerOptimization passes (mostly in base/compiler/ssair/)

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions