-
Notifications
You must be signed in to change notification settings - Fork 13k
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
[MIR] Refactor the MIR translator to use LLVM Builder directly #31282
Conversation
270ebcd
to
4b44082
Compare
// Create a fresh builder from the crate context. | ||
let builder = self.ccx().builder(); | ||
// Set the builder's position to this block's end. | ||
builder.position_at_end(self.llbb); |
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.
One of the points (other than getting rid of the unnecessary checks) was to get rid of repositioning the builder for every instruction. This should be not necessary (not in MIR, at least). I think it would be better to have something like
struct BlockBuilder<...> {
builder: Builder<...>
bcx: Block<...>
}
// split out the `Builder` trait from the `Builder` struct in trans/builder, and…
impl<...> Builder for BlockBuilder<...> {
// has all the functions that produce LLVM insns (likely generated by a macro or something)
}
so one could just call
bcx.br(self.llblock(target))
anywhere in the MIR, instead of doing the B(bcx).br(...)
or bcx.build().br(...)
dance every time we emit an instruction.
In such (or similar) grand scheme of things each block would have an associated builder and no repositions would be necessary.
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.
Each crate has only one builder, which BuilderRef
points to. I'm worried the lack of repositioning could introduce bugs. For example, after the call to MirContext.unreachable_block
which may emit an instruction, the builder for bcx must be repositioned (I think).
Also, keeping builder refs alongside blocks in the table would add at least several bytes of overhead for each block. Otherwise, you'd need a different dance every time bcx is passed from MIR to common trans.
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.
Each crate has only one builder, which BuilderRef points to.
You can create LLVM builders at will. The BuilderRef
in LocalCrateContext
is just a cached builder for the purposes of the old translator. No need to restrict ourselves only to the things available to the old translator.
I'm worried the lack of repositioning could introduce bugs.
True, especially when there could be two different builders acting on the same basic block (i.e. bcx is passed to some build::*
function), but the bugs are easily preventable by making bcx
in BlockBuilder
private and adding a method similar to this:
fn with_old_bcx<R, F: FnOnce(b: Block<...>) -> R> (&self, f: F) {
f(self.bcx);
self.builder.position_at_end(self.bcx.llbb);
}
Also, keeping builder refs alongside blocks in the table would add at least several bytes of overhead for each block.
I don’t find that to be a problem. At any given point the translator is dealing with a small amount of blocks. I don’t think there’s any place in MIR trans where it would be necessary to have more than 2 or 3 BlockBuilder
s alive at any given time. If you are worried about allocation overhead, dropped builders could be reused in some way as well.
Otherwise, you'd need a different dance every time bcx is passed from MIR to common trans.
bulder.with_old_bcx(|bcx| some_old_trans_function_wants_bcx(bcx, ...))
is much more trivial than sprinkling a bcx.builder()
everywhere, where MIR trans wants to emit an instruction. Consider, that the old trans will be phased out in the future and we should adapt (once the old trans is gone) the common trans utilities to match the MIR trans model, and not the other way around.
All in all, while this PR indeed makes MIR’s use of LLVM builder more direct, in my opinion there’s much larger refactoring opportunities available. If you are not interested in exploring them, I’ll r+ this provided you change all bcx.build()
to be B(bcx)
(where B
== build::B
), because it does essentially the same thing as this method, but is shorter to type out and already exists.
☔ The latest upstream changes (presumably #30448) made this pull request unmergeable. Please resolve the merge conflicts. |
3646783
to
81f8a28
Compare
} | ||
} | ||
|
||
impl<'blk, 'tcx> Deref for BlockAndBuilder<'blk, 'tcx> { |
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.
Nice!
I really like what this is looking like. I do not find lack of builder recycling to be a big problem or a blocker to land this (rather, that would be a premature optimisation, and builder construction seems to be pretty cheap anyway) I’ll let this stay around for a day or two so @nikomatsakis gets a chance to see it, but otherwise r=me with the two nits (and the tidy failure) fixed. |
81f8a28
to
fb2f547
Compare
Finished. I have some thoughts: almost all methods in the Storing |
@@ -1616,6 +1616,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, | |||
param_substs: param_substs, | |||
span: sp, | |||
block_arena: block_arena, | |||
lpad_arena: TypedArena::new(), |
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.
You might want to leave off this change for a separate PR, honestly (or at least factor it out into a separate commit, so it easier to see related changes).
Code related to this field is very precondition-sensitive, I’m worried changing it like this might invalidate some of those preconditions. E.g. MSVC landing blocks, especially, are on per-block basis and it is likely to be wrong there.
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.
Ah, I see it, you’re still storing a pointer inside a Block
. A few thoughts:
- Size of
BlockS
does not really matter, because we only ever pass aBlock
around, which is apub type Block<'blk, 'tcx> = &'blk BlockS<'blk, 'tcx>;
– i.e. is a reference and there’s barely any performance overhead passing a pointer to larger or smaller struct; - From my experimentation with implementing MSVC LPs in MIR, all that really needs to be stored around for MIR itself is a single ValueRef (a pointer, basically) inside MirContext. All the landing-pad related things in
Block
are for the old trans to use, and I wouldn’t bother much changing it (but since you’ve already changed it, its fine, I guess?).
I’d still like it split up into a separate commit so it is easier to review and these changes do not mix with the Builder refactor.
It doesn’t make much sense, does it? All the methods operate strictly on MIR, don’t they. It certainly might be a case that |
lldest: ValueRef, | ||
operand: OperandRef<'tcx>) | ||
{ | ||
debug!("store_operand: operand={}", operand.repr(bcx)); | ||
bcx.with_block(|bcx| { | ||
self.store_operand_direct(bcx, lldest, operand) |
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.
Why not inline?
☔ The latest upstream changes (presumably #31307) made this pull request unmergeable. Please resolve the merge conflicts. |
83c7825
to
8c19204
Compare
Updated. |
8c19204
to
5949393
Compare
let lltemp = bcx.with_block(|bcx| { | ||
base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)) | ||
}); | ||
let (dataptr, meta) = bcx.with_block(|bcx| { |
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.
Merge into with_block
above and return a (lltemp, dataptr, meta)
?
base::store_ty(bcx, llarg, lltemp, arg_ty); | ||
let lltemp = bcx.with_block(|bcx| { | ||
base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)) | ||
}); |
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.
Similarly.
Please squash the |
5949393
to
5d67d76
Compare
Finished. |
* We don't have SEH-based unwinding yet. For this reason we don't need operand bundles in MIR trans. * Refactored some uses of fcx. * Refactored some calls to `with_block`.
5d67d76
to
38fa06b
Compare
@bors r+ |
📌 Commit 38fa06b has been approved by |
⌛ Testing commit 38fa06b with merge 8b95b0a... |
Closes #31003