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

Various improvements to MIR and LLVM IR Construction #32980

Merged
merged 9 commits into from
Apr 28, 2016

Conversation

Aatch
Copy link
Contributor

@Aatch Aatch commented Apr 15, 2016

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.

@rust-highfive
Copy link
Collaborator

r? @pnkfelix

(rust_highfive has picked a reviewer for you, use r? to override)

@Aatch
Copy link
Contributor Author

Aatch commented Apr 15, 2016

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

@nagisa nagisa Apr 15, 2016

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.

@nagisa
Copy link
Member

nagisa commented Apr 15, 2016

The improvements look very nice. I left some comments, questions and nits inline.

r? @nagisa

@rust-highfive rust-highfive assigned nagisa and unassigned pnkfelix Apr 15, 2016
@arielb1
Copy link
Contributor

arielb1 commented Apr 15, 2016

The original plan was to do these changes in a post-optimization pass.

@nagisa
Copy link
Member

nagisa commented Apr 15, 2016

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.

@Aatch
Copy link
Contributor Author

Aatch commented Apr 16, 2016

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

@nagisa nagisa Apr 16, 2016

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).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, that makes sense.

@Aatch
Copy link
Contributor Author

Aatch commented Apr 17, 2016

Travis is finding more of those codegen-unit failures related to drop-glue i8. I'll fix those a little later. Otherwise I made the change to new_operand, @nagisa

@Aatch
Copy link
Contributor Author

Aatch commented Apr 17, 2016

Codegen-units tests fixed, though I'm still not sure what the significance of drop-glue i8 is.

@nagisa
Copy link
Member

nagisa commented Apr 17, 2016

Codegen-units tests fixed, though I'm still not sure what the significance of drop-glue i8 is.

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.

@nagisa
Copy link
Member

nagisa commented Apr 17, 2016

@bors r+ d574d56

@bors
Copy link
Contributor

bors commented Apr 17, 2016

⌛ Testing commit d574d56 with merge 8d4ef4c...

@bors
Copy link
Contributor

bors commented Apr 17, 2016

💔 Test failed - auto-linux-64-opt-mir

@nagisa
Copy link
Member

nagisa commented Apr 17, 2016

The test failure seems to be non-spurious. This runner likely runs in the -Z orbit mode.

@Aatch
Copy link
Contributor Author

Aatch commented Apr 19, 2016

Ah, the handling of closures caused it to try and translate an operand temp as an lvalue, which triggered the assertion.

@Aatch
Copy link
Contributor Author

Aatch commented Apr 20, 2016

Basically re-wrote trans_arguments_untupled. @eddyb want to double-check my changes there?

}
Immediate(llval) => {
for (n, &ty) in arg_types.iter().enumerate() {
let mut elem = bcx.extract_value(llval, n);
Copy link
Member

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?

Copy link
Contributor Author

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.

@arielb1
Copy link
Contributor

arielb1 commented Apr 20, 2016

@Aatch

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

Choose a reason for hiding this comment

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

NIT: incomplete comment?

@nagisa
Copy link
Member

nagisa commented Apr 20, 2016

@arielb1

The alloca should not really be used in practice, and could be removed with a better const-eval.

What alloca are you referring to? Before this PR, pretty much everything is an alloca-backed Lvalue.

@arielb1
Copy link
Contributor

arielb1 commented Apr 20, 2016

@nagisa

I missed the extractvalue trick, which is required with trans_operand.

@bors
Copy link
Contributor

bors commented Apr 21, 2016

☔ 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.
@Aatch
Copy link
Contributor Author

Aatch commented Apr 28, 2016

Rebased, sorry for the delay on that.

@nagisa
Copy link
Member

nagisa commented Apr 28, 2016

@bors
On Apr 28, 2016 5:10 AM, "James Miller" [email protected] wrote:

Rebased, sorry for the delay on that.


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#32980 (comment)

@Aatch
Copy link
Contributor Author

Aatch commented Apr 28, 2016

@bors r=nagisa

I think something got messed up with your email.

@bors
Copy link
Contributor

bors commented Apr 28, 2016

📌 Commit 5bda576 has been approved by nagisa

@bors
Copy link
Contributor

bors commented Apr 28, 2016

⌛ Testing commit 5bda576 with merge 009a649...

bors added a commit that referenced this pull request Apr 28, 2016
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants