Skip to content

[MIR] Refactor Drop to be a Terminator #31307

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

Merged
merged 7 commits into from
Feb 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/doc/book/lang-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,17 @@ unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {

p
}

#[lang = "exchange_free"]
unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) {
libc::free(ptr as *mut libc::c_void)
}

#[lang = "box_free"]
unsafe fn box_free<T>(ptr: *mut T) {
deallocate(ptr as *mut u8, ::core::mem::size_of::<T>(), ::core::mem::align_of::<T>());
}

#[start]
fn main(argc: isize, argv: *const *const u8) -> isize {
let x = box 1;
Expand Down
13 changes: 13 additions & 0 deletions src/liballoc/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
issue = "27700")]

use core::{isize, usize};
#[cfg(not(test))]
use core::intrinsics::{size_of, min_align_of};

#[allow(improper_ctypes)]
extern "C" {
Expand Down Expand Up @@ -147,6 +149,17 @@ unsafe fn exchange_free(ptr: *mut u8, old_size: usize, align: usize) {
deallocate(ptr, old_size, align);
}

#[cfg(not(test))]
#[lang = "box_free"]
#[inline]
unsafe fn box_free<T>(ptr: *mut T) {
let size = size_of::<T>();
// We do not allocate for Box<T> when T is ZST, so deallocation is also not necessary.
if size != 0 {
deallocate(ptr as *mut u8, size, min_align_of::<T>());
}
}

#[cfg(test)]
mod tests {
extern crate test;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ impl<'a, 'tcx> DeadVisitor<'a, 'tcx> {
let is_named = node.name().is_some();
let field_type = self.tcx.node_id_to_type(node.id);
let is_marker_field = match field_type.ty_to_def_id() {
Some(def_id) => self.tcx.lang_items.items().any(|(_, item)| *item == Some(def_id)),
Some(def_id) => self.tcx.lang_items.items().iter().any(|item| *item == Some(def_id)),
_ => false
};
is_named
Expand Down
8 changes: 3 additions & 5 deletions src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ use syntax::parse::token::InternedString;
use rustc_front::intravisit::Visitor;
use rustc_front::hir;

use std::iter::Enumerate;
use std::slice;

// The actual lang items defined come at the end of this file in one handy table.
// So you probably just want to nip down to the end.
macro_rules! lets_do_this {
Expand Down Expand Up @@ -69,8 +66,8 @@ impl LanguageItems {
}
}

pub fn items<'a>(&'a self) -> Enumerate<slice::Iter<'a, Option<DefId>>> {
self.items.iter().enumerate()
pub fn items(&self) -> &[Option<DefId>] {
&*self.items
}

pub fn item_name(index: usize) -> &'static str {
Expand Down Expand Up @@ -334,6 +331,7 @@ lets_do_this! {

ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn;
ExchangeFreeFnLangItem, "exchange_free", exchange_free_fn;
BoxFreeFnLangItem, "box_free", box_free_fn;
StrDupUniqFnLangItem, "strdup_uniq", strdup_uniq_fn;

StartFnLangItem, "start", start_fn;
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/reachable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ pub fn find_reachable(tcx: &ty::ctxt,
for (id, _) in &access_levels.map {
reachable_context.worklist.push(*id);
}
for (_, item) in tcx.lang_items.items() {
for item in tcx.lang_items.items().iter() {
if let Some(did) = *item {
if let Some(node_id) = tcx.map.as_local_node_id(did) {
reachable_context.worklist.push(node_id);
Expand Down
171 changes: 60 additions & 111 deletions src/librustc/mir/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,20 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use graphviz::IntoCow;
use middle::const_eval::ConstVal;
use middle::def_id::DefId;
use middle::subst::Substs;
use middle::ty::{self, AdtDef, ClosureSubsts, FnOutput, Region, Ty};
use rustc_back::slice;
use rustc_data_structures::tuple_slice::TupleSlice;
use rustc_front::hir::InlineAsm;
use syntax::ast::{self, Name};
use syntax::codemap::Span;
use graphviz::IntoCow;
use std::ascii;
use std::borrow::Cow;
use std::borrow::{Cow};
use std::fmt::{self, Debug, Formatter, Write};
use std::{iter, u32};
use std::ops::{Index, IndexMut};
use syntax::ast::{self, Name};
use syntax::codemap::Span;

/// Lowered representation of a single function.
#[derive(Clone, RustcEncodable, RustcDecodable)]
Expand Down Expand Up @@ -263,101 +262,63 @@ pub enum Terminator<'tcx> {
/// `END_BLOCK`.
Return,

/// Drop the Lvalue
Drop {
value: Lvalue<'tcx>,
target: BasicBlock,
unwind: Option<BasicBlock>
},

/// Block ends with a call of a converging function
Call {
/// The function that’s being called
func: Operand<'tcx>,
/// Arguments the function is called with
args: Vec<Operand<'tcx>>,
/// The kind of call with associated information
kind: CallKind<'tcx>,
/// Destination for the return value. If some, the call is converging.
destination: Option<(Lvalue<'tcx>, BasicBlock)>,
/// Cleanups to be done if the call unwinds.
cleanup: Option<BasicBlock>
},
}

#[derive(Clone, RustcEncodable, RustcDecodable)]
pub enum CallKind<'tcx> {
/// Diverging function without associated cleanup
Diverging,
/// Diverging function with associated cleanup
DivergingCleanup(BasicBlock),
/// Converging function without associated cleanup
Converging {
/// Destination where the call result is written
destination: Lvalue<'tcx>,
/// Block to branch into on successful return
target: BasicBlock,
},
ConvergingCleanup {
/// Destination where the call result is written
destination: Lvalue<'tcx>,
/// First target is branched to on successful return.
/// Second block contains the cleanups to do on unwind.
targets: (BasicBlock, BasicBlock)
}
}

impl<'tcx> CallKind<'tcx> {
pub fn successors(&self) -> &[BasicBlock] {
match *self {
CallKind::Diverging => &[],
CallKind::DivergingCleanup(ref b) |
CallKind::Converging { target: ref b, .. } => slice::ref_slice(b),
CallKind::ConvergingCleanup { ref targets, .. } => targets.as_slice(),
}
}

pub fn successors_mut(&mut self) -> &mut [BasicBlock] {
match *self {
CallKind::Diverging => &mut [],
CallKind::DivergingCleanup(ref mut b) |
CallKind::Converging { target: ref mut b, .. } => slice::mut_ref_slice(b),
CallKind::ConvergingCleanup { ref mut targets, .. } => targets.as_mut_slice(),
}
}

pub fn destination(&self) -> Option<&Lvalue<'tcx>> {
match *self {
CallKind::Converging { ref destination, .. } |
CallKind::ConvergingCleanup { ref destination, .. } => Some(destination),
CallKind::Diverging |
CallKind::DivergingCleanup(_) => None
}
}

pub fn destination_mut(&mut self) -> Option<&mut Lvalue<'tcx>> {
match *self {
CallKind::Converging { ref mut destination, .. } |
CallKind::ConvergingCleanup { ref mut destination, .. } => Some(destination),
CallKind::Diverging |
CallKind::DivergingCleanup(_) => None
}
}
}

impl<'tcx> Terminator<'tcx> {
pub fn successors(&self) -> &[BasicBlock] {
pub fn successors(&self) -> Cow<[BasicBlock]> {
use self::Terminator::*;
match *self {
Goto { target: ref b } => slice::ref_slice(b),
If { targets: ref b, .. } => b.as_slice(),
Switch { targets: ref b, .. } => b,
SwitchInt { targets: ref b, .. } => b,
Resume => &[],
Return => &[],
Call { ref kind, .. } => kind.successors(),
Goto { target: ref b } => slice::ref_slice(b).into_cow(),
If { targets: (b1, b2), .. } => vec![b1, b2].into_cow(),
Switch { targets: ref b, .. } => b[..].into_cow(),
SwitchInt { targets: ref b, .. } => b[..].into_cow(),
Resume => (&[]).into_cow(),
Return => (&[]).into_cow(),
Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(),
Call { destination: Some((_, ref t)), cleanup: None, .. } =>
slice::ref_slice(t).into_cow(),
Call { destination: None, cleanup: Some(ref c), .. } => slice::ref_slice(c).into_cow(),
Call { destination: None, cleanup: None, .. } => (&[]).into_cow(),
Drop { target, unwind: Some(unwind), .. } => vec![target, unwind].into_cow(),
Drop { ref target, .. } => slice::ref_slice(target).into_cow(),
}
}

pub fn successors_mut(&mut self) -> &mut [BasicBlock] {
// FIXME: no mootable cow. I’m honestly not sure what a “cow” between `&mut [BasicBlock]` and
// `Vec<&mut BasicBlock>` would look like in the first place.
pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> {
use self::Terminator::*;
match *self {
Goto { target: ref mut b } => slice::mut_ref_slice(b),
If { targets: ref mut b, .. } => b.as_mut_slice(),
Switch { targets: ref mut b, .. } => b,
SwitchInt { targets: ref mut b, .. } => b,
Resume => &mut [],
Return => &mut [],
Call { ref mut kind, .. } => kind.successors_mut(),
Goto { target: ref mut b } => vec![b],
If { targets: (ref mut b1, ref mut b2), .. } => vec![b1, b2],
Switch { targets: ref mut b, .. } => b.iter_mut().collect(),
SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(),
Resume => Vec::new(),
Return => Vec::new(),
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c],
Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
Call { destination: None, cleanup: None, .. } => vec![],
Drop { ref mut target, unwind: Some(ref mut unwind), .. } => vec![target, unwind],
Drop { ref mut target, .. } => vec![target]
}
}
}
Expand Down Expand Up @@ -424,8 +385,9 @@ impl<'tcx> Terminator<'tcx> {
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
Return => write!(fmt, "return"),
Resume => write!(fmt, "resume"),
Call { ref kind, ref func, ref args } => {
if let Some(destination) = kind.destination() {
Drop { ref value, .. } => write!(fmt, "drop({:?})", value),
Call { ref func, ref args, ref destination, .. } => {
if let Some((ref destination, _)) = *destination {
try!(write!(fmt, "{:?} = ", destination));
}
try!(write!(fmt, "{:?}(", func));
Expand All @@ -445,34 +407,31 @@ impl<'tcx> Terminator<'tcx> {
use self::Terminator::*;
match *self {
Return | Resume => vec![],
Goto { .. } => vec!["".into_cow()],
If { .. } => vec!["true".into_cow(), "false".into_cow()],
Goto { .. } => vec!["".into()],
If { .. } => vec!["true".into(), "false".into()],
Switch { ref adt_def, .. } => {
adt_def.variants
.iter()
.map(|variant| variant.name.to_string().into_cow())
.map(|variant| variant.name.to_string().into())
.collect()
}
SwitchInt { ref values, .. } => {
values.iter()
.map(|const_val| {
let mut buf = String::new();
fmt_const_val(&mut buf, const_val).unwrap();
buf.into_cow()
buf.into()
})
.chain(iter::once(String::from("otherwise").into_cow()))
.chain(iter::once(String::from("otherwise").into()))
.collect()
}
Call { ref kind, .. } => match *kind {
CallKind::Diverging =>
vec![],
CallKind::DivergingCleanup(..) =>
vec!["unwind".into_cow()],
CallKind::Converging { .. } =>
vec!["return".into_cow()],
CallKind::ConvergingCleanup { .. } =>
vec!["return".into_cow(), "unwind".into_cow()],
},
Call { destination: Some(_), cleanup: Some(_), .. } =>
vec!["return".into_cow(), "unwind".into_cow()],
Call { destination: Some(_), cleanup: None, .. } => vec!["return".into_cow()],
Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into_cow()],
Call { destination: None, cleanup: None, .. } => vec![],
Drop { unwind: None, .. } => vec!["return".into_cow()],
Drop { .. } => vec!["return".into_cow(), "unwind".into_cow()],
}
}
}
Expand All @@ -490,23 +449,13 @@ pub struct Statement<'tcx> {
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum StatementKind<'tcx> {
Assign(Lvalue<'tcx>, Rvalue<'tcx>),
Drop(DropKind, Lvalue<'tcx>),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)]
pub enum DropKind {
/// free a partially constructed box, should go away eventually
Free,
Deep
}

impl<'tcx> Debug for Statement<'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
use self::StatementKind::*;
match self.kind {
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
Drop(DropKind::Free, ref lv) => write!(fmt, "free {:?}", lv),
Drop(DropKind::Deep, ref lv) => write!(fmt, "drop {:?}", lv),
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv)
}
}
}
Expand Down
30 changes: 11 additions & 19 deletions src/librustc/mir/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,6 @@ macro_rules! make_mir_visitor {
ref $($mutability)* rvalue) => {
self.visit_assign(block, lvalue, rvalue);
}
StatementKind::Drop(_, ref $($mutability)* lvalue) => {
self.visit_lvalue(lvalue, LvalueContext::Drop);
}
}
}

Expand Down Expand Up @@ -177,30 +174,25 @@ macro_rules! make_mir_visitor {
Terminator::Return => {
}

Terminator::Drop { ref $($mutability)* value, target, unwind } => {
self.visit_lvalue(value, LvalueContext::Drop);
self.visit_branch(block, target);
unwind.map(|t| self.visit_branch(block, t));
}

Terminator::Call { ref $($mutability)* func,
ref $($mutability)* args,
ref $($mutability)* kind } => {
ref $($mutability)* destination,
cleanup } => {
self.visit_operand(func);
for arg in args {
self.visit_operand(arg);
}
match *kind {
CallKind::Converging {
ref $($mutability)* destination,
..
} |
CallKind::ConvergingCleanup {
ref $($mutability)* destination,
..
} => {
self.visit_lvalue(destination, LvalueContext::Store);
}
CallKind::Diverging |
CallKind::DivergingCleanup(_) => {}
}
for &target in kind.successors() {
if let Some((ref $($mutability)* destination, target)) = *destination {
self.visit_lvalue(destination, LvalueContext::Store);
self.visit_branch(block, target);
}
cleanup.map(|t| self.visit_branch(block, t));
}
}
}
Expand Down
Loading