diff --git a/src/passes/cps.jl b/src/passes/cps.jl index ae7e76f..f8581ab 100644 --- a/src/passes/cps.jl +++ b/src/passes/cps.jl @@ -67,10 +67,29 @@ function _functional(ir, tree, vars = [], cs = captures(ir, tree)) return finish(pr) end +# A chain of conditional branches is a nested if/else: move all but the first +# branch of blocks with more than two into a fresh block. +function splitbranches!(ir) + i = 1 + while i <= length(blocks(ir)) + bl = block(ir, i) + if length(branches(bl)) > 2 + new = block!(ir, i+1) + brs = copy(branches(bl)) + empty!(branches(bl)) + push!(branches(bl), brs[1]) + branch!(bl, new) + append!(branches(new), brs[2:end]) + end + i += 1 + end + return ir +end + # Unreachable blocks (e.g. left behind by a `break`) can branch anywhere, which # can't be expressed by nesting block lambdas along the dominator tree, and they # skew the dominators of the blocks they branch to. So drop them first. function functional(ir) - ir = trimblocks!(explicitbranch!(copy(ir))) + ir = trimblocks!(splitbranches!(explicitbranch!(copy(ir)))) _functional(ir, domtree(ir)) end diff --git a/test/compiler.jl b/test/compiler.jl index f80e4b3..1c0205f 100644 --- a/test/compiler.jl +++ b/test/compiler.jl @@ -272,6 +272,38 @@ end isa Pair{Core.CodeInfo,DataType}) end +# Blocks with more than two branches (#100). +@testset "multibranch" begin + f100(x, y) = x && y && error() + g100(a, b, c, d) = a && b && c && d && error() + function h100(n, a, b) + s = 0 + for i = 1:n + a && b && i > 1 && (s += i) + end + return s + end + @test_nowarn functional(IR(typeof(f100), Bool, Bool)) + @test_nowarn functional(IR(typeof(g100), Bool, Bool, Bool, Bool)) + for x in (false, true), y in (false, true) + if x && y + @test_throws ErrorException anf(f100, x, y) + else + @test anf(f100, x, y) == f100(x, y) + end + end + for a in (false, true), b in (false, true), c in (false, true), d in (false, true) + if a && b && c && d + @test_throws ErrorException anf(g100, a, b, c, d) + else + @test anf(g100, a, b, c, d) == g100(a, b, c, d) + end + end + for a in (false, true), b in (false, true), n in (0, 1, 4) + @test anf(h100, n, a, b) == h100(n, a, b) + end +end + function f_try_catch(x) y = 0. try