-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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: Re-use CFG from type inference #50924
Conversation
Potentially we could call adopt_thread on these threads but I would hold off on that until we figure some of the weird behaviour around them. |
72e319c
to
5c0b04a
Compare
This change will allow us to re-use Inference-collected information at the basic block level, such as the `bb_vartables`. There were a couple of spots where the `unreachable` insertion pass at IRCode conversion (i.e. optimizer entry) was ignoring statically divergent code that inference had discovered: - `%x = SlotNumber(3)` can throw and cause the following statements to be statically unreachable - `goto #b if not %cond` can be statically throwing if %cond is known to never be Bool (or to always throw during its own evaluation) CFG re-computation was hiding these bugs by flowing through the "Core.Const(...)"-wrapped statements that would follow, inserting unnecessary but harmless extra branches in the CFG.
These checks are important to make sure that `verify_ir` does not end up triggering out-of-bounds errors as it checks IR. It also fills out some of the verification: - Confirms that cfg.index is correct - Confirms that cfg.blocks[i].stmts cover the complete IR
For some reason the recent CFG change caused us to generate: ``` 1-element Vector{Any}: CodeInfo( 1 ─ nothing::Nothing └── return true ) => Bool ``` instead of ``` 1-element Vector{Any}: CodeInfo( 1 ─ return true ) => Bool ``` but I believe this difference is harmless.
5c0b04a
to
ce6b332
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks very nice. Sorry for comments after merge, but we can address them later anyway.
@@ -551,9 +551,9 @@ function finish(me::InferenceState, interp::AbstractInterpreter) | |||
# annotate fulltree with type information, | |||
# either because we are the outermost code, or we might use this later | |||
doopt = (me.cached || me.parent !== nothing) | |||
recompute_cfg = type_annotate!(interp, me, doopt) | |||
type_annotate!(interp, me, doopt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can eliminate anything related to any_unreachable
within type_annotate!
now.
idx += 1 | ||
prevloc = codeloc | ||
end | ||
if code[idx] isa Expr && ssavaluetypes[idx] === Union{} | ||
if ssavaluetypes[idx] === Union{} && !(code[idx] isa Core.Const) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we want to set up a special and explicit marker type instead of Const
to indicate a statement is known to be unreachable, e.g.
struct Unreachable
stmt
Unreachable(@nospecialize stmt) = new(stmt)
end
This is a partial back-port of JuliaLang#50924, where we discovered that the optimizer would ignore: 1. must-throw `%XX = SlotNumber(_)` statements 2. must-throw `goto #bb if not %x` statements when re-building the CFG during IRCode conversion. This is mostly harmless, except that in the case of (1) we can accidentally fall through the statically deleted (`Const()`-wrapped) code from inference and observe a control-flow edge that never should have existed, such as an edge into a catch block. Such an edge is invalid semantically and breaks our SSA conversion. This one-line change fixes (1) but not (2), which is enough for IR validity. We should follow-up with a tweak to `compute_basic_blocks` to enforce this requirement.
This is a partial back-port of JuliaLang#50924, where we discovered that the optimizer would ignore: 1. must-throw `%XX = SlotNumber(_)` statements 2. must-throw `goto #bb if not %x` statements when re-building the CFG during IRCode conversion. This is mostly harmless, except that in the case of (1) we can accidentally fall through the statically deleted (`Const()`-wrapped) code from inference and observe a control-flow edge that never should have existed, such as an edge into a catch block. Such an edge is invalid semantically and breaks our SSA conversion. This one-line change fixes (1) but not (2), which is enough for IR validity. We should follow-up with a tweak to `compute_basic_blocks` to enforce this requirement.
This is a partial back-port of JuliaLang#50924, where we discovered that the optimizer would ignore: 1. must-throw `%XX = SlotNumber(_)` statements 2. must-throw `goto #bb if not %x` statements when re-building the CFG during IRCode conversion. This is mostly harmless, except that in the case of (1) we can accidentally fall through the statically deleted (`Const()`-wrapped) code from inference and observe a control-flow edge that never should have existed, such as an edge into a catch block. Such an edge is invalid semantically and breaks our SSA conversion. This one-line change fixes (1) but not (2), which is enough for IR validity. We should follow-up with a tweak to `compute_basic_blocks` to enforce this requirement.
This is a partial back-port of JuliaLang#50924, where we discovered that the optimizer would ignore: 1. must-throw `%XX = SlotNumber(_)` statements 2. must-throw `goto #bb if not %x` statements when re-building the CFG during IRCode conversion. This is mostly harmless, except that in the case of (1) we can accidentally fall through the statically deleted (`Const()`-wrapped) code from inference and observe a control-flow edge that never should have existed, such as an edge into a catch block. Such an edge is invalid semantically and breaks our SSA conversion. This one-line change fixes (1) but not (2), which is enough for IR validity. We should follow-up with a tweak to `compute_basic_blocks` to enforce this requirement. The test added here is very brittle, but it's better than nothing until we have utilities to provide hand-written (pre-optimizer) IR and pass it through part of our pipeline.
This is a partial back-port of JuliaLang#50924, where we discovered that the optimizer would ignore: 1. must-throw `%XX = SlotNumber(_)` statements 2. must-throw `goto #bb if not %x` statements when re-building the CFG during IRCode conversion. This is mostly harmless, except that in the case of (1) we can accidentally fall through the statically deleted (`Const()`-wrapped) code from inference and observe a control-flow edge that never should have existed, such as an edge into a catch block. Such an edge is invalid semantically and breaks our SSA conversion. This one-line change fixes (1) but not (2), which is enough for IR validity. We should follow-up with a tweak to `compute_basic_blocks` to enforce this requirement. The test added here is very brittle, but it's better than nothing until we have utilities to provide hand-written (pre-optimizer) IR and pass it through part of our pipeline.
This is a partial back-port of #50924, where we discovered that the optimizer would ignore: 1. must-throw `%XX = SlotNumber(_)` statements 2. must-throw `goto #bb if not %x` statements This is mostly harmless, except that in the case of (1) we can accidentally fall through the statically deleted (`Const()`-wrapped) code from inference and end up observing a control-flow edge that never existed. If the spurious edge is to a catch block, then the edge is invalid semantically and breaks our SSA conversion. This one-line change fixes (1) but not (2), which is enough for IR validity. Resolves part of #53366.
…53512) This is a partial back-port of JuliaLang#50924, where we discovered that the optimizer would ignore: 1. must-throw `%XX = SlotNumber(_)` statements 2. must-throw `goto #bb if not %x` statements This is mostly harmless, except that in the case of (1) we can accidentally fall through the statically deleted (`Const()`-wrapped) code from inference and end up observing a control-flow edge that never existed. If the spurious edge is to a catch block, then the edge is invalid semantically and breaks our SSA conversion. This one-line change fixes (1) but not (2), which is enough for IR validity. Resolves part of JuliaLang#53366. (cherry picked from commit 035d17a)
This change will allow us to re-use Inference-collected information at the basic block level, such as the
bb_vartables
.There were a couple of spots where the
unreachable
insertion pass at IRCode conversion (i.e. optimizer entry) was ignoring statically divergent code that inference had discovered:%x = SlotNumber(3)
can throw and cause the following statements to be statically unreachablegoto #b if not %cond
can be statically throwing if %cond is known to never be Bool (or to always throw during its own evaluation)CFG re-computation was hiding these bugs by flowing through the "Core.Const(...)"-wrapped statements that would follow, inserting unnecessary but harmless extra branches in the CFG.