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: Re-use CFG from type inference #50924

Merged
merged 3 commits into from
Aug 18, 2023

Conversation

topolarity
Copy link
Member

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.

@topolarity topolarity requested review from Keno and aviatesk August 15, 2023 15:31
@gbaraldi
Copy link
Member

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.

src/timing.c Outdated Show resolved Hide resolved
@topolarity topolarity force-pushed the reuse-typeinference-cfg branch from 72e319c to 5c0b04a Compare August 15, 2023 17:13
@brenhinkeller brenhinkeller added the compiler:optimizer Optimization passes (mostly in base/compiler/ssair/) label Aug 15, 2023
@topolarity topolarity mentioned this pull request Aug 16, 2023
2 tasks
base/compiler/optimize.jl Outdated Show resolved Hide resolved
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.
@topolarity topolarity force-pushed the reuse-typeinference-cfg branch from 5c0b04a to ce6b332 Compare August 17, 2023 22:13
@Keno Keno merged commit 49e6ff8 into JuliaLang:master Aug 18, 2023
Copy link
Member

@aviatesk aviatesk left a 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)
Copy link
Member

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)
Copy link
Member

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

aviatesk added a commit that referenced this pull request Aug 25, 2023
Dependent on #50924 .

This removes the last remaining usage of `TypedSlot`.
As a bonus, this evicts all of the "tmerge" type-iteration that was
being done in slot2ssa.

---------

Co-authored-by: Shuhei Kadowaki <aviatesk@gmail.com>
topolarity added a commit to topolarity/julia that referenced this pull request Feb 28, 2024
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.
topolarity added a commit to topolarity/julia that referenced this pull request Feb 29, 2024
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.
topolarity added a commit to topolarity/julia that referenced this pull request Feb 29, 2024
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.
topolarity added a commit to topolarity/julia that referenced this pull request Mar 7, 2024
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.
topolarity added a commit to topolarity/julia that referenced this pull request Mar 7, 2024
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.
topolarity added a commit that referenced this pull request Mar 7, 2024
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.
Drvi pushed a commit to RelationalAI/julia that referenced this pull request Mar 8, 2024
…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)
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/)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants