Repository navigation
Avoid terminator: None in custom mir parser - #165502
panstromek wants to merge 2 commits into
Conversation
|
r? @nnethercote rustbot has assigned @nnethercote. Use Why was this reviewer chosen?The reviewer was selected based on:
|
This comment has been minimized.
This comment has been minimized.
…minator This was only needed because `is_cleanup` property was parsed separately from the basic block data, so we do this by parsing `is_cleanup` into a vec of bools first, and later transform it into the bb data when parsing blocks themselves.
16f2ca6 to
ca5705a
Compare
| // just a sanity check to make sure order of bbs | ||
| // in cleanup blocks matches the one in the body | ||
| assert_eq!(inserted_bb, bb); |
There was a problem hiding this comment.
I'm not sure I understand what this checks / what "order of BBs in cleanup blocks" means. We're comparing bb = i, which we get from enumerate of block_defs, with the index in the body's basic block list. That should always be true, unless something sneakily pushes another basic block before this loop or inside parse_block_def. Maybe that's what you had in mind, but either way the comment needs to be clearer.
There was a problem hiding this comment.
Maybe a better comment would be just "to make sure that these line up correctly." or "to make sure we're adding the correct block id"
But I'll step back and explain the issue, because this is a variation of a problem that happens in multiple places that use terminator: None, and maybe you'll get a better idea for what you would want to see commented here.
This code parses the blocks from two sources in two passes - first there's a list of declarations, parsed by parse_block_decls. We just get block numbers and is_cleanup flags from those. And then there's this code which parses the blocks themselves. Even here, these two list already don't line up. The declaration list omits the entry block, but definition list includes it, that's why we add the empty is_cleanup: false during construction.
The original code only has one place that creates basic blocks and assigns their ids (the declaration parser), and then always refers to existing blocks by id. The new code has (kinda) two places that assign ids. One is in the declaration parser, and one is the definition parser here. The old code implicitly prevented situation where you parse the block definition into incorrect id, the new code ensures that with the assert.
That should always be true, unless something sneakily pushes another basic block before this loop or inside parse_block_def
Yes. I added the assert to avoid this code implicitly assuming that other parts of the pipeline don't add basic blocks in other places (and I've actually hit this assert during refactoring, that's how I found out the implicit basic block added during construction)
There was a problem hiding this comment.
Okay, I understand better now, thanks. I think I was also misguided by my knowledge of the surface syntax that mir! accepts. Some unorganized thoughts:
- It's not just the cleanup block data,
block_mapalso has to match how we actually assign block indices. For example, you can name a (non-entry) blockfooand references to blockfooelsewhere will be resolved to the index offoowithin the list of block declarations (+1 for implicit entry block). That's another reason why order of blocks inbodyhas to match declaration order. - I think this isn't entirely a new problem. Before, we had only one loop allocate blocks in
body, but we still had the same implicit dependency on how these blocks get initialized inparse_block_defs. Trying to create too many blocks probably would've panicked with an index out of bounds, but initializing fewer than were declared could've gone silently wrong. - While block def parsing currently separates "allocate block index in
body" from "actually push the block" this does not seem inherent. We could instead callparse_block_deffirst, get back aBasicBlockData, push it into thebody, and then use the index at which it was inserted to look up whether it's a cleanup block. That would remove the "block gets wrongis_cleanup" problem, but won't do anything to ensure the block is created at the right index w.r.t.block_map. - It seems more of a coincidence that the declaration phase "allocates block indices" by pushing to the
cleanup_blocksvector.- We just happen to have another
IndexVecindexed by basic blocks at hand, but in any other context, it wouldn't be the canonical source for which blocks exist. - Arguably we don't even need the "is cleanup" flag in the declarations and could instead associate it with the block definition.
- I realize that we need to assign block indices somehow, to populate
block_mapif nothing else, and so using anIndexVecand populating it during declaration parsing kills two birds with one stone. But the more I think about it, the more it seems "too clever for its own good".
- We just happen to have another
I now have some ideas for how this whole process could be refactored, but that's probably way out of scope for this PR. Instead I would propose:
- At the end, assert that checks that all of
cleanup_blocks,block_map, and the resultingbodyagree on the total number of blocks (block_mapshould have one less because the entry block is anonymous). That won't catch getting the order wrong, but the most likely way to mess up the order is by creating too extra blocks or skipping one. - For this comment, I'd say the main point for me is ensuring we create one block per
block_defso that order and indices match the block declarations parsed above. Mentioning only cleanup without names here is misleading, but mentioning both together could be helpful.
| thir: &'a Thir<'tcx>, | ||
| source_scope: SourceScope, | ||
| body: &'a mut Body<'tcx>, | ||
| cleanup_blocks: IndexVec<BasicBlock, bool>, |
There was a problem hiding this comment.
Should this be a GrowableBitSet? The memory footprint doesn't matter, but it seems conceptually more appropriate than IndexVec<_, bool>.
There was a problem hiding this comment.
Maybe, I was thinking about it, but I wanted to preserve the hidden assumptions from the original code - this code not only tracks the cleanup flags, but it also allocates basic block ids with this vector.
If there's fewer block declarations then block definitions, accessing this vec will panic when block definitions are parsed. GrowableBitSet wouldn't panic.
There was a problem hiding this comment.
As mentioned in the other comment I just posted, I think the use of this IndexVec to allocate block indices is too clever. I'm fine with leaving it alone in this PR, but maybe add a comment explaining this dual purpose?
|
Reminder, once the PR becomes ready for a review, use |
|
☔ The latest upstream changes (presumably #165597) made this pull request unmergeable. Please resolve the merge conflicts by rebasing. |
First step towards replacing
Option<Terminator>withTerminatorinBasicBlockData. See this thread for more info.This is a slight restructuring of the custom mir parser that avoids creating dummy basic blocks that don't have terminators. More details in individual commits.