Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

optimizer: Handle path-excluded Core.ifelse arguments #50312

Merged
merged 1 commit into from
Jun 29, 2023

Conversation

topolarity
Copy link
Member

@topolarity topolarity commented Jun 27, 2023

There are cases where a relevant PiNode implies that Core.ifelse(%cond, %a, %b) never yields %b.

I opted to take the basic path here and just lower to Core.ifelse(%cond, %a, %a). An improved solution would probably replace with %a directly, but I haven't convinced myself of the legality conditions for that.

Resolves #50276.

@topolarity topolarity requested review from Keno and aviatesk June 27, 2023 20:36
@Keno
Copy link
Member

Keno commented Jun 27, 2023

An improved solution would probably replace with %a directly

I think that should be always legal.

@topolarity
Copy link
Member Author

I think that should be always legal.

My main concern was about accidentally erasing an exception throw.

What ensures that we preserve that effect? Is it that the original Core.ifelse call is guaranteed to be preserved?

@Keno
Copy link
Member

Keno commented Jun 27, 2023

Ah, I guess make it conditional on IR_FLAG_NOTHROW. I supposed it's possible that someone is passing something other than a boolean as the first argument. Though even then you could still take the ifelse out of the def-use chain and just make it a separate statement. That can help some downstream passes that walk def-use chains.

@oscardssmith
Copy link
Member

Core.ifelse evals both branches so replacing it's result with an assignment doesn't change anything. It might make the other branch "dead code" but that will only be deleted if the effects pan out.

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.
@topolarity topolarity added the status:merge me PR is reviewed. Merge when all tests are passing label Jun 28, 2023
test/compiler/irpasses.jl Outdated Show resolved Hide resolved
Comment on lines +750 to +771
# 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),
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice 👍

@vtjnash vtjnash merged commit 5db930e into JuliaLang:master Jun 29, 2023
1 check passed
@oscardssmith oscardssmith added performance Must go faster compiler:optimizer Optimization passes (mostly in base/compiler/ssair/) and removed status:merge me PR is reviewed. Merge when all tests are passing labels Jun 29, 2023
@topolarity topolarity added kind:bugfix This change fixes an existing bug and removed performance Must go faster labels Jun 29, 2023
only_result = (then_result === SKIP_TOKEN) ? else_result : then_result

# Replace Core.ifelse(%cond, %a, %b) with %a
compact[lf.ssa][:inst] = only_result
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second look, this bypasses the count removal of the cond. This probably needs to be:

compact[lf.ssa] = nothing
compact[lf.ssa][:inst] = only_result

Possibly also consider turning on the oracle check for the tests, which would have caught this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler:optimizer Optimization passes (mostly in base/compiler/ssair/) kind:bugfix This change fixes an existing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Method error during optimization
5 participants