2015-11-03 05:35:09 -06:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
//! An analysis to determine which temporaries require allocas and
|
|
|
|
//! which do not.
|
|
|
|
|
2016-01-21 10:57:43 -06:00
|
|
|
use rustc_data_structures::bitvec::BitVector;
|
2015-11-19 09:37:34 -06:00
|
|
|
use rustc::mir::repr as mir;
|
2016-05-29 14:01:06 -05:00
|
|
|
use rustc::mir::repr::TerminatorKind;
|
2015-11-24 07:35:34 -06:00
|
|
|
use rustc::mir::visit::{Visitor, LvalueContext};
|
2016-05-29 14:01:06 -05:00
|
|
|
use rustc_mir::traversal;
|
2016-04-16 00:38:18 -05:00
|
|
|
use common::{self, Block, BlockAndBuilder};
|
2015-11-03 05:35:09 -06:00
|
|
|
use super::rvalue;
|
|
|
|
|
|
|
|
pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>,
|
2016-04-16 00:38:18 -05:00
|
|
|
mir: &mir::Mir<'tcx>) -> BitVector {
|
|
|
|
let bcx = bcx.build();
|
|
|
|
let mut analyzer = TempAnalyzer::new(mir, &bcx, mir.temp_decls.len());
|
2015-11-03 05:35:09 -06:00
|
|
|
|
|
|
|
analyzer.visit_mir(mir);
|
|
|
|
|
|
|
|
for (index, temp_decl) in mir.temp_decls.iter().enumerate() {
|
|
|
|
let ty = bcx.monomorphize(&temp_decl.ty);
|
|
|
|
debug!("temp {:?} has type {:?}", index, ty);
|
2015-11-10 14:05:11 -06:00
|
|
|
if ty.is_scalar() ||
|
|
|
|
ty.is_unique() ||
|
|
|
|
ty.is_region_ptr() ||
|
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.
2016-04-14 19:36:16 -05:00
|
|
|
ty.is_simd() ||
|
|
|
|
common::type_is_zero_size(bcx.ccx(), ty)
|
2015-11-03 05:35:09 -06:00
|
|
|
{
|
|
|
|
// These sorts of types are immediates that we can store
|
|
|
|
// in an ValueRef without an alloca.
|
2015-11-10 14:05:11 -06:00
|
|
|
assert!(common::type_is_immediate(bcx.ccx(), ty) ||
|
|
|
|
common::type_is_fat_ptr(bcx.tcx(), ty));
|
2015-11-03 05:35:09 -06:00
|
|
|
} else {
|
|
|
|
// These sorts of types require an alloca. Note that
|
|
|
|
// type_is_immediate() may *still* be true, particularly
|
|
|
|
// for newtypes, but we currently force some types
|
|
|
|
// (e.g. structs) into an alloca unconditionally, just so
|
|
|
|
// that we don't have to deal with having two pathways
|
2015-11-03 14:50:04 -06:00
|
|
|
// (gep vs extractvalue etc).
|
2015-11-03 05:35:09 -06:00
|
|
|
analyzer.mark_as_lvalue(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
analyzer.lvalue_temps
|
|
|
|
}
|
|
|
|
|
2016-04-16 00:38:18 -05:00
|
|
|
struct TempAnalyzer<'mir, 'bcx: 'mir, 'tcx: 'bcx> {
|
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.
2016-04-14 19:36:16 -05:00
|
|
|
mir: &'mir mir::Mir<'tcx>,
|
2016-04-16 00:38:18 -05:00
|
|
|
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
|
2016-01-21 10:57:43 -06:00
|
|
|
lvalue_temps: BitVector,
|
|
|
|
seen_assigned: BitVector
|
2015-11-03 05:35:09 -06:00
|
|
|
}
|
|
|
|
|
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.
2016-04-14 19:36:16 -05:00
|
|
|
impl<'mir, 'bcx, 'tcx> TempAnalyzer<'mir, 'bcx, 'tcx> {
|
|
|
|
fn new(mir: &'mir mir::Mir<'tcx>,
|
2016-04-16 00:38:18 -05:00
|
|
|
bcx: &'mir BlockAndBuilder<'bcx, 'tcx>,
|
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.
2016-04-14 19:36:16 -05:00
|
|
|
temp_count: usize) -> TempAnalyzer<'mir, 'bcx, 'tcx> {
|
2016-01-21 10:57:43 -06:00
|
|
|
TempAnalyzer {
|
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.
2016-04-14 19:36:16 -05:00
|
|
|
mir: mir,
|
|
|
|
bcx: bcx,
|
2016-01-21 10:57:43 -06:00
|
|
|
lvalue_temps: BitVector::new(temp_count),
|
|
|
|
seen_assigned: BitVector::new(temp_count)
|
|
|
|
}
|
2015-11-03 05:35:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
fn mark_as_lvalue(&mut self, temp: usize) {
|
|
|
|
debug!("marking temp {} as lvalue", temp);
|
|
|
|
self.lvalue_temps.insert(temp);
|
|
|
|
}
|
2016-01-21 10:57:43 -06:00
|
|
|
|
|
|
|
fn mark_assigned(&mut self, temp: usize) {
|
|
|
|
if !self.seen_assigned.insert(temp) {
|
|
|
|
self.mark_as_lvalue(temp);
|
|
|
|
}
|
|
|
|
}
|
2015-11-03 05:35:09 -06:00
|
|
|
}
|
|
|
|
|
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.
2016-04-14 19:36:16 -05:00
|
|
|
impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
|
2015-11-03 05:35:09 -06:00
|
|
|
fn visit_assign(&mut self,
|
|
|
|
block: mir::BasicBlock,
|
|
|
|
lvalue: &mir::Lvalue<'tcx>,
|
|
|
|
rvalue: &mir::Rvalue<'tcx>) {
|
|
|
|
debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue);
|
|
|
|
|
|
|
|
match *lvalue {
|
|
|
|
mir::Lvalue::Temp(index) => {
|
2016-01-21 10:57:43 -06:00
|
|
|
self.mark_assigned(index as usize);
|
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.
2016-04-14 19:36:16 -05:00
|
|
|
if !rvalue::rvalue_creates_operand(self.mir, self.bcx, rvalue) {
|
2015-11-03 05:35:09 -06:00
|
|
|
self.mark_as_lvalue(index as usize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.visit_lvalue(lvalue, LvalueContext::Store);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.visit_rvalue(rvalue);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_lvalue(&mut self,
|
|
|
|
lvalue: &mir::Lvalue<'tcx>,
|
|
|
|
context: LvalueContext) {
|
|
|
|
debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context);
|
|
|
|
|
|
|
|
match *lvalue {
|
|
|
|
mir::Lvalue::Temp(index) => {
|
|
|
|
match context {
|
2016-04-07 22:37:56 -05:00
|
|
|
LvalueContext::Call => {
|
|
|
|
self.mark_assigned(index as usize);
|
|
|
|
}
|
2015-11-03 05:35:09 -06:00
|
|
|
LvalueContext::Consume => {
|
|
|
|
}
|
|
|
|
LvalueContext::Store |
|
|
|
|
LvalueContext::Drop |
|
|
|
|
LvalueContext::Inspect |
|
|
|
|
LvalueContext::Borrow { .. } |
|
|
|
|
LvalueContext::Slice { .. } |
|
|
|
|
LvalueContext::Projection => {
|
|
|
|
self.mark_as_lvalue(index as usize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.super_lvalue(lvalue, context);
|
|
|
|
}
|
|
|
|
}
|
2016-05-29 14:01:06 -05:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub enum CleanupKind {
|
|
|
|
NotCleanup,
|
|
|
|
Funclet,
|
|
|
|
Internal { funclet: mir::BasicBlock }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
|
|
|
|
mir: &mir::Mir<'tcx>)
|
|
|
|
-> Vec<CleanupKind>
|
|
|
|
{
|
|
|
|
fn discover_masters<'tcx>(result: &mut [CleanupKind], mir: &mir::Mir<'tcx>) {
|
|
|
|
for bb in mir.all_basic_blocks() {
|
|
|
|
let data = mir.basic_block_data(bb);
|
|
|
|
match data.terminator().kind {
|
|
|
|
TerminatorKind::Goto { .. } |
|
|
|
|
TerminatorKind::Resume |
|
|
|
|
TerminatorKind::Return |
|
|
|
|
TerminatorKind::If { .. } |
|
|
|
|
TerminatorKind::Switch { .. } |
|
|
|
|
TerminatorKind::SwitchInt { .. } => {
|
|
|
|
/* nothing to do */
|
|
|
|
}
|
|
|
|
TerminatorKind::Call { cleanup: unwind, .. } |
|
2016-05-25 00:39:32 -05:00
|
|
|
TerminatorKind::Assert { cleanup: unwind, .. } |
|
2016-05-29 14:01:06 -05:00
|
|
|
TerminatorKind::DropAndReplace { unwind, .. } |
|
|
|
|
TerminatorKind::Drop { unwind, .. } => {
|
|
|
|
if let Some(unwind) = unwind {
|
|
|
|
debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
|
|
|
|
bb, data, unwind);
|
|
|
|
result[unwind.index()] = CleanupKind::Funclet;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn propagate<'tcx>(result: &mut [CleanupKind], mir: &mir::Mir<'tcx>) {
|
|
|
|
let mut funclet_succs : Vec<_> =
|
|
|
|
mir.all_basic_blocks().iter().map(|_| None).collect();
|
|
|
|
|
|
|
|
let mut set_successor = |funclet: mir::BasicBlock, succ| {
|
|
|
|
match funclet_succs[funclet.index()] {
|
|
|
|
ref mut s @ None => {
|
|
|
|
debug!("set_successor: updating successor of {:?} to {:?}",
|
|
|
|
funclet, succ);
|
|
|
|
*s = Some(succ);
|
|
|
|
},
|
|
|
|
Some(s) => if s != succ {
|
|
|
|
span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
|
|
|
|
funclet, s, succ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
for (bb, data) in traversal::reverse_postorder(mir) {
|
|
|
|
let funclet = match result[bb.index()] {
|
|
|
|
CleanupKind::NotCleanup => continue,
|
|
|
|
CleanupKind::Funclet => bb,
|
|
|
|
CleanupKind::Internal { funclet } => funclet,
|
|
|
|
};
|
|
|
|
|
|
|
|
debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
|
|
|
|
bb, data, result[bb.index()], funclet);
|
|
|
|
|
|
|
|
for &succ in data.terminator().successors().iter() {
|
|
|
|
let kind = result[succ.index()];
|
|
|
|
debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
|
|
|
|
funclet, succ, kind);
|
|
|
|
match kind {
|
|
|
|
CleanupKind::NotCleanup => {
|
|
|
|
result[succ.index()] = CleanupKind::Internal { funclet: funclet };
|
|
|
|
}
|
|
|
|
CleanupKind::Funclet => {
|
|
|
|
set_successor(funclet, succ);
|
|
|
|
}
|
|
|
|
CleanupKind::Internal { funclet: succ_funclet } => {
|
|
|
|
if funclet != succ_funclet {
|
|
|
|
// `succ` has 2 different funclet going into it, so it must
|
|
|
|
// be a funclet by itself.
|
|
|
|
|
|
|
|
debug!("promoting {:?} to a funclet and updating {:?}", succ,
|
|
|
|
succ_funclet);
|
|
|
|
result[succ.index()] = CleanupKind::Funclet;
|
|
|
|
set_successor(succ_funclet, succ);
|
|
|
|
set_successor(funclet, succ);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut result : Vec<_> =
|
|
|
|
mir.all_basic_blocks().iter().map(|_| CleanupKind::NotCleanup).collect();
|
|
|
|
|
|
|
|
discover_masters(&mut result, mir);
|
|
|
|
propagate(&mut result, mir);
|
|
|
|
debug!("cleanup_kinds: result={:?}", result);
|
|
|
|
result
|
|
|
|
}
|