Skip to content

Commit

Permalink
optimize: Handle path-excluded Core.ifelse arguments
Browse files Browse the repository at this point in the history
It's possible for PiNodes to effectively imply statically the condition
of a Core.ifelse. For example:
```julia
    23 ─ %60  = Core.ifelse(%47, false, true)::Bool
    │    %61  = Core.ifelse(%47, %58, false)::Union{Missing, Bool}
    25 ─        goto JuliaLang#27 if not %60
    26 ─ %65  = π (%61, Bool)
    └───        ...
```

In basic block JuliaLang#26, the PiNode gives us enough information to conclude
that `%47 === false` if control flow reaches that point. The previous
code incorrectly assumed that this kind of pruning would only be done
for PhiNodes.

Resolves JuliaLang#50276.
  • Loading branch information
topolarity committed Jun 27, 2023
1 parent 0e147eb commit cb51a92
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
5 changes: 5 additions & 0 deletions base/compiler/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,11 @@ function perform_lifting!(compact::IncrementalCompact,
else_result = lifted_value(compact, old_node_ssa, else_result,
lifted_philikes, lifted_leaves, reverse_mapping)

# In cases where the Core.ifelse condition is statically-known, e.g., thanks
# to a PiNode from a guarding conditional, we replace with the other branch.
then_result === SKIP_TOKEN && (then_result = else_result)
else_result === SKIP_TOKEN && (else_result = then_result)

@assert then_result !== SKIP_TOKEN && then_result !== UNDEF_TOKEN
@assert else_result !== SKIP_TOKEN && else_result !== UNDEF_TOKEN

Expand Down
52 changes: 52 additions & 0 deletions test/compiler/irpasses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,58 @@ let m = Meta.@lower 1 + 1
@test Core.Compiler.verify_ir(ir) === nothing
end

# A lifted Core.ifelse with an eliminated branch (#50276)
let m = Meta.@lower 1 + 1
@assert Meta.isexpr(m, :thunk)
src = m.args[1]::CodeInfo
src.code = Any[
# block 1
#= %1: =# Core.Argument(2),
# block 2
#= %2: =# Expr(:call, Core.ifelse, SSAValue(1), true, missing),
#= %3: =# GotoIfNot(SSAValue(2), 11),
# block 3
#= %4: =# PiNode(SSAValue(2), Bool), # <-- This PiNode is the trigger of the bug, since it
# means that only one branch of the Core.ifelse
# is lifted.
#= %5: =# GotoIfNot(false, 8),
# block 2
#= %6: =# nothing,
#= %7: =# GotoNode(8),
# block 4
#= %8: =# PhiNode(Int32[5, 7], Any[SSAValue(4), SSAValue(6)]),
# ^-- N.B. This PhiNode also needs to have a Union{ ... } type in order
# for lifting to be performed (it is skipped for e.g. `Bool`)
#
#= %9: =# Expr(:call, isa, SSAValue(8), Missing),
#= %10: =# ReturnNode(SSAValue(9)),
# block 5
#= %11: =# ReturnNode(false),
]
src.ssavaluetypes = Any[
Any,
Union{Missing, Bool},
Any,
Bool,
Any,
Missing,
Any,
Union{Nothing, Bool},
Bool,
Any,
Any
]
nstmts = length(src.code)
src.codelocs = fill(one(Int32), nstmts)
src.ssaflags = fill(one(Int32), nstmts)
src.slotflags = fill(zero(UInt8), 3)
ir = Core.Compiler.inflate_ir(src)
Main.Base.IRShow.show_ir(stdout, ir)
@test Core.Compiler.verify_ir(ir) === nothing
ir = @test_nowarn Core.Compiler.sroa_pass!(ir)
@test Core.Compiler.verify_ir(ir) === nothing
end

# Issue #31546 - missing widenconst in SROA
function f_31546(x)
(a, b) = x == "r" ? (false, false) :
Expand Down

0 comments on commit cb51a92

Please sign in to comment.