-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Various improvements to MIR and LLVM IR Construction #32980
Conversation
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
A comparison of the MIR and LLVM IR output before and after these changes: https://gist.github.com/Aatch/5257da7c18fb7a86595080c0851fabd5. Which is for this code: pub fn foo() -> i32 {
let mut a = 0;
while a < 100 {
a += 1;
a = a * 2;
}
a * a
} I haven't made any measurements regarding compile times. |
@@ -599,7 +608,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { | |||
} | |||
} | |||
|
|||
pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { | |||
pub fn rvalue_creates_operand<'bcx, 'tcx>(mir: &mir::Mir<'tcx>, bcx: Block<'bcx, '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.
Pass a BlockAndBuilder
here and use its tcx
and monomorphize
methods instead. bcx.with_block
does work to make sure blocks are in suitable condition for old-trans and the method should be avoided at all costs unless you’re calling back into old-trans machinery.
The improvements look very nice. I left some comments, questions and nits inline. r? @nagisa |
The original plan was to do these changes in a post-optimization pass. |
Some of them, yes. We will still end up having a pass removing unused temps/vars etc for example, so the passes wouldn’t need to bother with them much, but feeding cleaner MIR to the passes in the first place is also a good idea, especially, when its such a low-hanging fruit (in terms of diff) as it is in this case. |
47a3ad1
to
ad91f9f
Compare
Comments addressed and a bug fixed I found locally. |
@@ -108,6 +108,16 @@ enum TempRef<'tcx> { | |||
Operand(Option<OperandRef<'tcx>>), | |||
} | |||
|
|||
impl<'tcx> TempRef<'tcx> { | |||
fn new_operand(val: OperandValue, ty: ty::Ty<'tcx>) -> TempRef<'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.
I was thinking that it would check for ty
being zero-sized and returned either the wrapped C_nil
or None
, like the whole else-if-else
block did in the original commit, because that’s the kinda of an important assumption for code elsewhere to work (EDIT: namely, the changes in trans/mir/statement
).
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, that makes sense.
820d1ef
to
82d933e
Compare
Travis is finding more of those codegen-unit failures related to |
Codegen-units tests fixed, though I'm still not sure what the significance of |
These are generated from depgraph which collects data from MIR. It seems correct to me that some of these items disappear, because we do emit less drops (and thus sometimes drop glue becomes unnecessary) now. |
@bors r+ d574d56 |
⌛ Testing commit d574d56 with merge 8d4ef4c... |
💔 Test failed - auto-linux-64-opt-mir |
The test failure seems to be non-spurious. This runner likely runs in the |
Ah, the handling of closures caused it to try and translate an operand temp as an lvalue, which triggered the assertion. |
Basically re-wrote |
} | ||
Immediate(llval) => { | ||
for (n, &ty) in arg_types.iter().enumerate() { | ||
let mut elem = bcx.extract_value(llval, n); |
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.
Couldn't this be abstracted away in a single place?
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.
Probably, I'd like to rework most of how we deal with ADTs though, especially now that #32939 has landed.
What was the problem with the old version? The alloca should not really be used in practice, and could be removed with a better const-eval. |
Ref(ptr) | ||
}; | ||
self.trans_argument(bcx, val, llargs, fn_ty, next_idx, callee); | ||
// Handle both by-ref and immediate tuples. This gives us the option of |
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.
NIT: incomplete comment?
What alloca are you referring to? Before this PR, pretty much everything is an alloca-backed Lvalue. |
I missed the |
☔ The latest upstream changes (presumably #33030) made this pull request unmergeable. Please resolve the merge conflicts. |
Primarily affects the MIR construction, which indirectly improves LLVM IR generation, but some LLVM IR changes have been made too. * Handle "statement expressions" more intelligently. These are expressions that always evaluate to `()`. Previously a temporary would be generated as a destination to translate into, which is unnecessary. This affects assignment, augmented assignment, `return`, `break` and `continue`. * Avoid inserting drops for non-drop types in more places. Scheduled drops were already skipped for types that we knew wouldn't need dropping at construction time. However manually-inserted drops like those for `x` in `x = y;` were still generated. `build_drop` now takes a type parameter like its `schedule_drop` counterpart and checks to see if the type needs dropping. * Avoid generating an extra temporary for an assignment where the types involved don't need dropping. Previously an expression like `a = b + 1;` would result in a temporary for `b + 1`. This is so the RHS can be evaluated, then the LHS evaluated and dropped and have everything work correctly. However, this isn't necessary if the `LHS` doesn't need a drop, as we can just overwrite the existing value. * Improves lvalue analysis to allow treating an `Rvalue::Use` as an operand in certain conditions. The reason for it never being an operand is so it can be zeroed/drop-filled, but this is only true for types that need dropping. The first two changes result in significantly fewer MIR blocks being generated, as previously almost every statement would end up generating a new block due to the drop of the `()` temporary being generated.
Moves `stmt_expr` into its own module, `expr::stmt`.
The drop glue for `i8` is no longer generated as a trans item
In code like `let x = y = z;`, `y = z` goes through `as_rvalue`, which didn't handle it. Now it translates the assignment and produces `()` directly.
`new_operand` now checks the type it's given and either creates the nil value itself, or produces an empty operand.
I'm not sure what the signficance of `drop-glue i8` is, nor why one of the tests had it appear while the others had it disappear. Either way it doesn't seem like the presence or absense of it is the focus of the tests.
Use either getelementptr or extractvalue depending on whether or not the tuple is immediate or not.
LLVM's assertion doesn't provide much insight as to what the problem was. We were already checking `call` instructions ourselves, so this brings the checks from there to `invoke`. Both the `invoke` and `call` checking is controlled by `debug_assertions`.
The logic for checking `call` and `invoke` instructions was duplicated between them, so factor it out to a helper method.
71665e7
to
5bda576
Compare
Rebased, sorry for the delay on that. |
@bors r±
|
@bors r=nagisa I think something got messed up with your email. |
📌 Commit 5bda576 has been approved by |
Various improvements to MIR and LLVM IR Construction Primarily affects the MIR construction, which indirectly improves LLVM IR generation, but some LLVM IR changes have been made too. * Handle "statement expressions" more intelligently. These are expressions that always evaluate to `()`. Previously a temporary would be generated as a destination to translate into, which is unnecessary. This affects assignment, augmented assignment, `return`, `break` and `continue`. * Avoid inserting drops for non-drop types in more places. Scheduled drops were already skipped for types that we knew wouldn't need dropping at construction time. However manually-inserted drops like those for `x` in `x = y;` were still generated. `build_drop` now takes a type parameter like its `schedule_drop` counterpart and checks to see if the type needs dropping. * Avoid generating an extra temporary for an assignment where the types involved don't need dropping. Previously an expression like `a = b + 1;` would result in a temporary for `b + 1`. This is so the RHS can be evaluated, then the LHS evaluated and dropped and have everything work correctly. However, this isn't necessary if the `LHS` doesn't need a drop, as we can just overwrite the existing value. * Improves lvalue analysis to allow treating an `Rvalue::Use` as an operand in certain conditions. The reason for it never being an operand is so it can be zeroed/drop-filled, but this is only true for types that need dropping. The first two changes result in significantly fewer MIR blocks being generated, as previously almost every statement would end up generating a new block due to the drop of the `()` temporary being generated.
Primarily affects the MIR construction, which indirectly improves LLVM
IR generation, but some LLVM IR changes have been made too.
Handle "statement expressions" more intelligently. These are
expressions that always evaluate to
()
. Previously a temporary wouldbe generated as a destination to translate into, which is unnecessary.
This affects assignment, augmented assignment,
return
,break
andcontinue
.Avoid inserting drops for non-drop types in more places. Scheduled
drops were already skipped for types that we knew wouldn't need
dropping at construction time. However manually-inserted drops like
those for
x
inx = y;
were still generated.build_drop
now takesa type parameter like its
schedule_drop
counterpart and checks tosee if the type needs dropping.
Avoid generating an extra temporary for an assignment where the types
involved don't need dropping. Previously an expression like
a = b + 1;
would result in a temporary forb + 1
. This is so theRHS can be evaluated, then the LHS evaluated and dropped and have
everything work correctly. However, this isn't necessary if the
LHS
doesn't need a drop, as we can just overwrite the existing value.
Improves lvalue analysis to allow treating an
Rvalue::Use
as anoperand in certain conditions. The reason for it never being an
operand is so it can be zeroed/drop-filled, but this is only true for
types that need dropping.
The first two changes result in significantly fewer MIR blocks being
generated, as previously almost every statement would end up generating
a new block due to the drop of the
()
temporary being generated.