Skip to content

Commit

Permalink
optimize: Delete incoming unreachable edges from PhiNode
Browse files Browse the repository at this point in the history
Incoming IR very rarely contains PhiNodes, but we do allow it to make things
easier on downstream packages like Diffractor that want to generate the same
code structures in both typed and untyped mode. However, this does of course
mean that once inference is finished, any PhiNodes in the original source
must be adjusted to maintain IRCode invariants. One particular important
invariant here is that the edges list in a PhiNode must match the
predecessor list, so in particular if a predecessor becomes unreachable
during inference, we must filter that edge before passing it on to the
optimizer.
  • Loading branch information
Keno committed Mar 27, 2024
1 parent 6737a1d commit ec970c3
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 0 deletions.
13 changes: 13 additions & 0 deletions base/compiler/optimize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,19 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState)
code[i] = nothing
end
end
elseif isa(expr, PhiNode)
new_edges = Int32[]
new_vals = Any[]
for j = 1:length(expr.edges)
(expr.edges[j] in sv.unreachable) && continue
push!(new_edges, expr.edges[j])
if isassigned(expr.values, j)
push!(new_vals, expr.values[j])
else
resize!(new_vals, length(new_edges))
end
end
code[i] = PhiNode(new_edges, new_vals)
end
end
end
Expand Down
26 changes: 26 additions & 0 deletions test/compiler/interpreter_exec.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,29 @@ let m = Meta.@lower 1 + 1
@test :b === @eval $m
@test isempty(current_exceptions())
end

# Test that things don't break if one branch of the frontend PhiNode becomes unreachable
let m = Meta.@lower 1 + 1
@assert Meta.isexpr(m, :thunk)
src = m.args[1]::CodeInfo
src.code = Any[
# block 1
GlobalRef(@__MODULE__, :global_error_switch),
GotoIfNot(SSAValue(1), 4),
# block 2
Expr(:call, error, "This error is expected"),
# block 3
PhiNode(Int32[2, 3], Any[1, 2]),
ReturnNode(SSAValue(4))
]
nstmts = length(src.code)
src.ssavaluetypes = nstmts
src.ssaflags = fill(UInt8(0x00), nstmts)
src.debuginfo = Core.DebugInfo(:none)
Core.Compiler.verify_ir(Core.Compiler.inflate_ir(src))
global global_error_switch = true
@test_throws ErrorException @eval $m
global global_error_switch = false
@test 1 === @eval $m
@test isempty(current_exceptions())
end

0 comments on commit ec970c3

Please sign in to comment.