Skip to content

Commit

Permalink
Do not mark unitinitialized locals as requiring storage
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewjasper committed Oct 2, 2019
1 parent 1dfc3e7 commit 73c0987
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 17 deletions.
38 changes: 30 additions & 8 deletions src/librustc_mir/dataflow/impls/storage_liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,13 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
assert_eq!(1, self.body.arg_count);
}

fn statement_effect(&self,
sets: &mut GenKillSet<Local>,
loc: Location) {
self.check_for_move(sets, loc);
fn before_statement_effect(&self, sets: &mut GenKillSet<Self::Idx>, loc: Location) {
// If we borrow or assign to a place then it needs storage for that
// statement.
self.check_for_borrow(sets, loc);

let stmt = &self.body[loc.block].statements[loc.statement_index];
match stmt.kind {
StatementKind::StorageLive(l) => sets.gen(l),
StatementKind::StorageDead(l) => sets.kill(l),
StatementKind::Assign(box(ref place, _))
| StatementKind::SetDiscriminant { box ref place, .. } => {
Expand All @@ -136,11 +134,35 @@ impl<'mir, 'tcx> BitDenotation<'tcx> for RequiresStorage<'mir, 'tcx> {
}
}

fn terminator_effect(&self,
sets: &mut GenKillSet<Local>,
loc: Location) {
fn statement_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
// If we move from a place then only stops needing storage *after*
// that statement.
self.check_for_move(sets, loc);
}

fn before_terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
self.check_for_borrow(sets, loc);

if let TerminatorKind::Call {
destination: Some((Place { base: PlaceBase::Local(local), .. }, _)),
..
} = self.body[loc.block].terminator().kind {
sets.gen(local);
}
}

fn terminator_effect(&self, sets: &mut GenKillSet<Local>, loc: Location) {
// For call terminators the destination requires storage for the call
// and after the call returns successfully, but not after a panic.
// Since `propagate_call_unwind` doesn't exist, we have to kill the
// destination here, and then gen it again in `propagate_call_return`.
if let TerminatorKind::Call {
destination: Some((Place { base: PlaceBase::Local(local), projection: box [] }, _)),
..
} = self.body[loc.block].terminator().kind {
sets.kill(local);
}
self.check_for_move(sets, loc);
}

fn propagate_call_return(
Expand Down
5 changes: 1 addition & 4 deletions src/librustc_mir/transform/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -508,10 +508,7 @@ fn locals_live_across_suspend_points(
storage_liveness_map.insert(block, storage_liveness.clone());

requires_storage_cursor.seek(loc);
let mut storage_required = requires_storage_cursor.get().clone();

// Mark locals without storage statements as always requiring storage
storage_required.union(&ignored.0);
let storage_required = requires_storage_cursor.get().clone();

// Locals live are live at this point only if they are used across
// suspension points (the `liveness` variable)
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/async-await/async-fn-size-moved-locals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ struct BigFut([u8; BIG_FUT_SIZE]);
impl BigFut {
fn new() -> Self {
BigFut([0; BIG_FUT_SIZE])
} }
}
}

impl Drop for BigFut {
fn drop(&mut self) {}
Expand Down
103 changes: 103 additions & 0 deletions src/test/ui/async-await/async-fn-size-uninit-locals.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Test that we don't store uninitialized locals in futures from `async fn`.
//
// The exact sizes can change by a few bytes (we'd like to know when they do).
// What we don't want to see is the wrong multiple of 1024 (the size of `Big`)
// being reflected in the size.

// ignore-wasm32-bare (sizes don't match)
// run-pass

// edition:2018

#![allow(unused_variables, unused_assignments)]

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

const BIG_FUT_SIZE: usize = 1024;
struct Big([u8; BIG_FUT_SIZE]);

impl Big {
fn new() -> Self {
Big([0; BIG_FUT_SIZE])
}
}

impl Drop for Big {
fn drop(&mut self) {}
}

#[allow(dead_code)]
struct Joiner {
a: Option<Big>,
b: Option<Big>,
c: Option<Big>,
}

impl Future for Joiner {
type Output = ();

fn poll(self: Pin<&mut Self>, _ctx: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(())
}
}

fn noop() {}
async fn fut() {}

async fn single() {
let x;
fut().await;
x = Big::new();
}

async fn single_with_noop() {
let x;
fut().await;
noop();
x = Big::new();
noop();
}

async fn joined() {
let joiner;
let a = Big::new();
let b = Big::new();
let c = Big::new();

fut().await;
noop();
joiner = Joiner { a: Some(a), b: Some(b), c: Some(c) };
noop();
}

async fn joined_with_noop() {
let joiner;
let a = Big::new();
let b = Big::new();
let c = Big::new();

fut().await;
noop();
joiner = Joiner { a: Some(a), b: Some(b), c: Some(c) };
noop();
}

async fn join_retval() -> Joiner {
let a = Big::new();
let b = Big::new();
let c = Big::new();

fut().await;
noop();
Joiner { a: Some(a), b: Some(b), c: Some(c) }
}

fn main() {
assert_eq!(8, std::mem::size_of_val(&single()));
assert_eq!(12, std::mem::size_of_val(&single_with_noop()));
assert_eq!(3084, std::mem::size_of_val(&joined()));
assert_eq!(3084, std::mem::size_of_val(&joined_with_noop()));
assert_eq!(3084, std::mem::size_of_val(&join_retval()));
}
8 changes: 4 additions & 4 deletions src/test/ui/async-await/async-fn-size.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ fn main() {
assert_eq!(8, std::mem::size_of_val(&await1_level1()));
assert_eq!(12, std::mem::size_of_val(&await2_level1()));
assert_eq!(12, std::mem::size_of_val(&await3_level1()));
assert_eq!(20, std::mem::size_of_val(&await3_level2()));
assert_eq!(28, std::mem::size_of_val(&await3_level3()));
assert_eq!(36, std::mem::size_of_val(&await3_level4()));
assert_eq!(44, std::mem::size_of_val(&await3_level5()));
assert_eq!(24, std::mem::size_of_val(&await3_level2()));
assert_eq!(36, std::mem::size_of_val(&await3_level3()));
assert_eq!(48, std::mem::size_of_val(&await3_level4()));
assert_eq!(60, std::mem::size_of_val(&await3_level5()));

assert_eq!(1, wait(base()));
assert_eq!(1, wait(await1_level1()));
Expand Down

0 comments on commit 73c0987

Please sign in to comment.