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

Add some CFG manipulation tools for IRCode #45305

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open

Conversation

tkf
Copy link
Member

@tkf tkf commented May 13, 2022

This PR adds CFG manipulation function allocate_new_blocks! and a derived function allocate_gotoifnot_sequence! to help CFG manipulation of IRCodes.

allocate_new_blocks!(ir::IRCode, statement_positions) -> info::NewBlocksInfo
Create new "singleton" basic blocks (i.e., it contains a single instruction)
before `statement_positions`. This function adds `2 * length(statement_positions)`
blocks; i.e., `length(statement_positions)` blocks for the new singleton basic
blocks and the remaining `length(statement_positions)` blocks for the basic
block containing the instructions starting at each `statement_positions`.
The caller must ensure that:
@assert issorted(statement_positions)
@assert all(1 <= p <= length(ir.stmts) for p in statement_positions)
Note that this function does not wire up the CFG for newly created BBs. It just
inserts the dummy `GotoNode(0)` at the end of the new singleton BBs and the BB
_before_ (in terms of `ir.cfg.bocks`) it. The predecessors of the BB just
before the newly added singleton BB and the successors of the BB just after the
newly added singleton BB are re-wired. See `allocate_gotoifnot_sequence!` for
an example for creating a valid CFG.
For example, given an `ir` containing:
#bb
%1 = instruction_1
%2 = instruction_2
`allocate_new_blocks!(ir, [2])` produces
#bb′
%1 = instruction_1
goto #0 # dummy
#new_bb_1
goto #0 # dummy
#new_bb_2
%2 = instruction_2
The predecessors of `#bb′` are equivalent to the predecessors of `#bb`. The successors of
`#new_bb_2` are equivalent to the successors of `#bb`.
The returned object `info` can be passed to `foreach_allocated_new_block` for
iterating over allocated basic blocks. An indexable object `info.ssachangemap`
can be used for mapping old SSA values to the new locations.
Properties of `info::NewBlocksInfo`:
* `target_blocks`: A list of original IDs of the basic blocks in which new blocks are
inserted; i.e., the basic blocks containing instructions `statement_positions`.
* `ssachangemap`: Given an original statement position `iold`, the new statement position is
`ssachangemap[iold]`.
* `bbchangemap`: Given an original basic block id `iold`, the new basic block ID is
`bbchangemap[iold]`.

allocate_gotoifnot_sequence!(ir::IRCode, statement_positions) -> info::NewBlocksInfo
Insert a new basic block before each `statement_positions` and a `GotoIfNot`
that jumps over the newly added BB. Unlike `allocate_new_blocks!`, this
function results in an IR with valid CFG.
Read `allocate_new_blocks!` on the preconditions on `statement_positions`.
For example, given an `ir` containing:
#bb
%1 = instruction_1
%2 = instruction_2
`allocate_new_blocks!(ir, [2])` produces
#bb′
%1 = instruction_1
goto #new_bb_2 if not false
#new_bb_1 # this bb is unreachable
goto #new_bb_2
#new_bb_2
%2 = instruction_2


This PR also adds a bit more somewhat automated way to wrap Core.Compiler interface functions as Base interface functions; e.g.,

for T in [:IRCode, :InstructionStream, :Instruction, :StmtRange, :UseRef]
@eval getindex(xs::Core.Compiler.$T, args...) = Core.Compiler.getindex(xs, args...)

Some of the manually-handled definitions in IRShow are now handled this way. These definitions are very handy for writing tests and also playing with them in REPL. But I think this PR can be done without them if it's better to do this elsewhere.

@vtjnash
Copy link
Sponsor Member

vtjnash commented May 13, 2022

I don't know this code well, so @aviatesk or @ianatol should probably review

@tkf
Copy link
Member Author

tkf commented May 13, 2022

It looks like the tester_win32 failure is OOM #44948
https://build.julialang.org/#/builders/42/builds/5170

Copy link
Member

@ianatol ianatol left a comment

Choose a reason for hiding this comment

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

SGTM in terms of code quality, though I don't quite understand the use case. Could you explain a bit more when we would want to use allocate_new_blocks! and allocate_gotoifnot_sequence!?

base/compiler/ssair/ir.jl Outdated Show resolved Hide resolved
base/compiler/ssair/ir.jl Outdated Show resolved Hide resolved
base/compiler/ssair/ir.jl Show resolved Hide resolved
@vchuravy
Copy link
Member

So I think in the LICM PR I wanted to split an edge. Can you add a test for this?

a:
  br %cond, #b, #d
b: 
  ; insertion-point
  br %cond2, #c, #d
c:
  br #b
d:
  ret

Goal:

a:
  br %cond, #new, #d
new:
  br #b
b: 
  br %cond2, #c, #d
c:
  br #b
d:
  ret

Also if you add comments to the test explaining what transformation is happening it would be easier to think through if we cover all test cases.

@vchuravy vchuravy requested review from yhls and Keno and removed request for vtjnash May 13, 2022 21:55
@Keno
Copy link
Member

Keno commented May 16, 2022

I like the general direction of this. In the past I had suggested we make a stdlib for this, but I'm fine with putting it in Base for now and making code movement decisions later. In general, it would be good to have a relatively clean and stable API for IRCode. Various utilities exist in various packages, so consolidating them is a good idea.

tkf and others added 2 commits May 16, 2022 14:41
Co-authored-by: Ian Atol <ian.atol@juliacomputing.com>
@oscardssmith
Copy link
Member

IMO, this PR would make more sense if there was a second commit in it that makes more of the compiler to use these tools.

base/compiler/ssair/ir.jl Outdated Show resolved Hide resolved
Comment on lines 1632 to 1636
Create new "singleton" basic blocks (i.e., it contains a single instruction)
before `statement_positions`. This function adds `2 * length(statement_positions)`
blocks; i.e., `length(statement_positions)` blocks for the new singleton basic
blocks and the remaining `length(statement_positions)` blocks for the basic
block containing the instructions starting at each `statement_positions`.
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Suggested change
Create new "singleton" basic blocks (i.e., it contains a single instruction)
before `statement_positions`. This function adds `2 * length(statement_positions)`
blocks; i.e., `length(statement_positions)` blocks for the new singleton basic
blocks and the remaining `length(statement_positions)` blocks for the basic
block containing the instructions starting at each `statement_positions`.
Create new "singleton" basic blocks (i.e., it contains a single dummy instruction)
before `statement_positions`. This function adds `2 * length(statement_positions)`
instructions; i.e., `length(statement_positions)` instructions for the new singleton basic
blocks and the remaining `length(statement_positions)` instructions for the basic
block containing the dummy instructions starting at each `statement_positions`.

explains better?

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried to pull some ideas from this. I also cleaned up the API which simplified the explanation.

base/compiler/ssair/ir.jl Outdated Show resolved Hide resolved
@aviatesk
Copy link
Sponsor Member

Thanks, these tools look very useful and the code quality is great.

IMO, this PR would make more sense if there was a second commit in it that makes more of the compiler to use these tools.

I'm not sure that there are such places where we manipulate CFG of IRCode currently. The purpose of this PR is to land the tools for CFG manipulation first so that we can do experiment optimizations involving CFG transformation in the future without inventing these tools.

@tkf
Copy link
Member Author

tkf commented May 19, 2022

So I think in the LICM PR I wanted to split an edge. Can you add a test for this?

@vchuravy I created a sketch for how to do LICM https://nbviewer.org/gist/tkf/d4734be24d2694a3afd669f8f50e6b0f/00_notebook.ipynb (see the last example). It's tedious to create the input IR "by hand" so I want to do this after #45306 if we are to add it to the test.

@ianatol Does this also answer your question about when we would want to use these APIs? Another example was for doing the lowering of modifyfield! at IRCode-level https://github.com/tkf/julia/blob/518569a70ccfc14894c698cba3aabcb907eaedba/base/compiler/ssair/atomics.jl#L459-L492 which is how I initially prototyped this API. This is probably not the right direction for modifyfield! lowering but I think it is a good realistic example.

if you add comments to the test explaining what transformation is happening

There are single-sentence explanations in the testset labels. I also added some pseudocode for explaining the input and output IRs.

base/compiler/ssair/ir.jl Outdated Show resolved Hide resolved
@vchuravy vchuravy requested review from aviatesk and topolarity and removed request for yhls October 31, 2023 00:18
@vchuravy vchuravy added the compiler:optimizer Optimization passes (mostly in base/compiler/ssair/) label Oct 31, 2023
@vchuravy vchuravy self-assigned this Oct 31, 2023
@vchuravy
Copy link
Member

vchuravy commented Oct 31, 2023

Okay rebased onto ToT, and I am in the process of rewriting #36832 on top of this.

@vchuravy vchuravy mentioned this pull request Oct 31, 2023
3 tasks
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.

7 participants