Julia: Malformed SSA generation

Created on 20 Feb 2019  ยท  6Comments  ยท  Source: JuliaLang/julia

A stripped down version of https://github.com/FluxML/Zygote.jl/issues/54#issuecomment-465378937, which has more description. This IR leads to an internal error in the optimiser when test(1) is called (the function still runs OK, though some variants of this were causing crashes).

Full reproducer:

using Core.Compiler: GotoNode, SSAValue

xcall(m, f, args...) = Expr(:call, GlobalRef(m, f), args...)

function construct()
  ci = @code_lowered identity(1)

  ci.code = Any[
    nothing,
    nothing,
    nothing,
    nothing,
    nothing,
    nothing,
    nothing,
    xcall(Base, :rand, Bool),
    Expr(:gotoifnot, SSAValue(8), 16),
    GotoNode(11),
    nothing,
    xcall(Base, :rand, Bool),
    Expr(:(=), Core.SlotNumber(3), Core.SlotNumber(2)),
    Expr(:gotoifnot, SSAValue(12), 22),
    GotoNode(20),
    Expr(:(=), Core.SlotNumber(4), Core.SlotNumber(2)),
    GotoNode(18),
    Expr(:(=), Core.SlotNumber(3), Core.SlotNumber(4)),
    GotoNode(22),
    Expr(:(=), Core.SlotNumber(4), Core.SlotNumber(3)),
    GotoNode(18),
    xcall(Base, :identity, Core.SlotNumber(3)),
    Expr(:return, Core.SSAValue(22))]

  ci.slotflags = [0x00, 0x00, 0x00, 0x00]
  ci.slotnames = [Symbol("#self#"), :x, :a, :b]
  ci.codelocs = [Int32(0) for x in ci.code]
  ci.ssavaluetypes = length(ci.code)
  return ci
end

@generated function test(x)
  return construct()
end

test(1)

Most helpful comment

This one was fun. I'll use it as a homework if I ever teach a compiler course.

All 6 comments

julia> ci = @code_lowered test(1)
CodeInfo(
1 โ”€       nothing
โ”‚         nothing
โ”‚         nothing
โ”‚         nothing
โ”‚         nothing
โ”‚         nothing
โ”‚         nothing
โ”‚   %8  = Base.rand(Bool)
โ””โ”€โ”€       goto #5 if not %8
2 โ”€       goto #3
3 โ”€       nothing
โ”‚   %12 = Base.rand(Bool)
โ”‚         a = x
โ””โ”€โ”€       goto #8 if not %12
4 โ”€       goto #7
5 โ”€       b = x
โ””โ”€โ”€       goto #6
6 โ”„       a = b
โ””โ”€โ”€       goto #8
7 โ”€       b = a
โ””โ”€โ”€       goto #6
8 โ”„ %22 = Base.identity(a)
โ””โ”€โ”€       return %22
)

julia> m = @which test(1);
julia> r = Core.Compiler.code_for_method(m, Tuple{typeof(m), Int}, Core.svec(), typemax(UInt64));
julia> Core.Compiler.Core.Compiler.just_construct_ssa(ci, Base.copy_exprargs(ci.code), 1, Core.Compiler.OptimizationState(r, ci, Core.Compiler.DEFAULT_PARAMS))
 1 โ”€       nothing::Any                                                                                                                                                         โ”‚
 โ”‚         nothing::Any                                                                                                                                                         โ”‚
 โ”‚         nothing::Any                                                                                                                                                         โ”‚
 โ”‚         nothing::Any                                                                                                                                                         โ”‚
 โ”‚         nothing::Any                                                                                                                                                         โ”‚
 โ”‚         nothing::Any                                                                                                                                                         โ”‚
 โ”‚         nothing::Any                                                                                                                                                         โ”‚
 โ”‚   %8  = Base.rand(Bool)::Any                                                                                                                                                 โ”‚
 โ””โ”€โ”€       goto #7 if not %8                                                                                                                                                    โ”‚
 2 โ”€       nothing::Any                                                                                                                                                         โ”‚
 3 โ”€       nothing::Any                                                                                                                                                         โ”‚
 โ”‚   %12 = Base.rand(Bool)::Any                                                                                                                                                 โ”‚
 โ”‚   %13 = _2::Any                                                                                                                                                              โ”‚
 โ””โ”€โ”€       goto #6 if not %12                                                                                                                                                   โ”‚
 4 โ”€       nothing::Any                                                                                                                                                         โ”‚
 5 โ”€ %16 = %13::Any                                                                                                                                                             โ”‚
 โ””โ”€โ”€       goto #8                                                                                                                                                              โ”‚
 6 โ”„ %18 = Base.identity(%22)::Any                                                                                                                                              โ”‚
 โ””โ”€โ”€       return %18                                                                                                                                                           โ”‚
 7 โ”€ %20 = _2::Any                                                                                                                                                              โ”‚
 โ””โ”€โ”€       nothing::Any                                                                                                                                                         โ”‚
 8 โ”„ %24 = ฯ† (#5 => %16, #7 => %20)::Any                                                                                                                                        โ”‚
 โ”‚   %22 = %24::Any
 โ””โ”€โ”€       goto #6                                                                                                                                                              โ”‚

note that I think that last IR is mis-sorted also

This does look like a bug in SSA construction. As for the mis-sorting, that's probably ok since that node is likely pending (if so, it should be color coded as such, not sure if that got lost in all the IR printing changes).

just lost in the copy-to-text. yes, node%24 is pending. bb#6 still seems potentially to be in the wrong place (it post-dominates the function, and thus I'd expect it to remain last)

julia> Core.Compiler.compact!(ans)
 1 โ”€ %1  = Base.rand(Bool)::Any                                                                                                                                                            โ”‚
 โ””โ”€โ”€       goto #7 if not %1                                                                                                                                                               โ”‚
 2 โ”€       nothing::Nothing                                                                                                                                                                โ”‚
 3 โ”€ %4  = Base.rand(Bool)::Any                                                                                                                                                            โ”‚
 โ””โ”€โ”€       goto #6 if not %4                                                                                                                                                               โ”‚
 4 โ”€       nothing::Nothing                                                                                                                                                                โ”‚
 5 โ”€       goto #8                                                                                                                                                                         โ”‚
 6 โ”„ %8  = Base.identity(%22)::Any                                                                                                                                                         โ”‚
 โ””โ”€โ”€       return %8                                                                                                                                                                       โ”‚
 7 โ”€       nothing::Nothing                                                                                                                                                                โ”‚
 8 โ”„       nothing::Any                                                                                                                                                                    โ”‚
 โ””โ”€โ”€       goto #6                                                                                                                                                                         โ”‚

Sure, but the phi node is missing before domsorting also.

Looks like an SNCA bug.

with

using AbstractTrees
using Core.Compiler: DomTree

AbstractTrees.treekind(d::DomTree) = AbstractTrees.IndexedTree()
AbstractTrees.childindices(d::DomTree, i::Int) = d[i].children
AbstractTrees.childindices(::DomTree, ::DomTree) = (1,)
AbstractTrees.parentlinks(::DomTree) = AbstractTrees.StoredParents()
AbstractTrees.printnode(io::IO, i::Int, d::DomTree) = print(io, i)
Base.getindex(d::DomTree, i) = d.nodes[i]
Base.show(io::IO, d::DomTree) = print_tree(io, 1; roottree = d)

we see

julia> cfg = Core.Compiler.compute_basic_blocks(ci.code)
1   =>  5, 2
2   =>  3
3   =>  8, 4
4   =>  7
5   =>  6
6   =>  8
7   =>  6
8   =>


julia> Core.Compiler.construct_domtree(cfg)
1
โ”œโ”€ 2
โ”‚  โ””โ”€ 3
โ”‚     โ”œโ”€ 4
โ”‚     โ”‚  โ””โ”€ 7
โ”‚     โ””โ”€ 8
โ”œโ”€ 5
โ””โ”€ 6


julia> Core.eval(Core.Compiler, quote
       function construct_domtree_naive(cfg::CFG)
           idoms = naive_idoms(cfg)
           # Compute children
           nblocks = length(cfg.blocks)
           domtree = DomTreeNode[DomTreeNode() for _ = 1:nblocks]
           for (idx, idom) in Iterators.enumerate(idoms)
               (idx == 1 || idom == 0) && continue
               push!(domtree[idom].children, idx)
           end
           # Recursively set level
           update_level!(domtree, 1, 1)
           DomTree(idoms, domtree)
       end
       end)
construct_domtree_naive (generic function with 1 method)

julia> Core.Compiler.construct_domtree_naive(cfg)
1
โ”œโ”€ 2
โ”‚  โ””โ”€ 3
โ”‚     โ””โ”€ 4
โ”‚        โ””โ”€ 7
โ”œโ”€ 5
โ”œโ”€ 6
โ””โ”€ 8

This one was fun. I'll use it as a homework if I ever teach a compiler course.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

StefanKarpinski picture StefanKarpinski  ยท  3Comments

omus picture omus  ยท  3Comments

iamed2 picture iamed2  ยท  3Comments

sbromberger picture sbromberger  ยท  3Comments

dpsanders picture dpsanders  ยท  3Comments