Skip to content

[MIR] implement zero-on-move #31449

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

Closed
wants to merge 3 commits into from
Closed
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
25 changes: 16 additions & 9 deletions src/librustc_trans/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1298,22 +1298,29 @@ pub fn init_zero_mem<'blk, 'tcx>(cx: Block<'blk, 'tcx>, llptr: ValueRef, t: Ty<'
fn memfill<'a, 'tcx>(b: &Builder<'a, 'tcx>, llptr: ValueRef, ty: Ty<'tcx>, byte: u8) {
let _icx = push_ctxt("memfill");
let ccx = b.ccx;

let llty = type_of::type_of(ccx, ty);
let ptr_width = &ccx.sess().target.target.target_pointer_width[..];
let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width);

let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key);
let llptr = b.pointercast(llptr, Type::i8(ccx).ptr_to());
let llzeroval = C_u8(ccx, byte);
let size = machine::llsize_of(ccx, llty);
let align = C_i32(ccx, type_of::align_of(ccx, ty) as i32);
let volatile = C_bool(ccx, false);
b.call(llintrinsicfn,
&[llptr, llzeroval, size, align, volatile],
None, None);
call_memset(b, llptr, llzeroval, size, align, false);
}

pub fn call_memset<'bcx, 'tcx>(b: &Builder<'bcx, 'tcx>,
ptr: ValueRef,
fill_byte: ValueRef,
size: ValueRef,
align: ValueRef,
volatile: bool) {
let ccx = b.ccx;
let ptr_width = &ccx.sess().target.target.target_pointer_width[..];
let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width);
let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key);
let volatile = C_bool(ccx, volatile);
b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None, None);
}


/// In general, when we create an scratch value in an alloca, the
/// creator may not know if the block (that initializes the scratch
/// with the desired value) actually dominates the cleanup associated
Expand Down
37 changes: 4 additions & 33 deletions src/librustc_trans/trans/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use trans::common::{self, Block, LandingPad};
use trans::debuginfo::DebugLoc;
use trans::Disr;
use trans::foreign;
use trans::glue;
use trans::type_of;
use trans::type_::Type;

Expand Down Expand Up @@ -96,35 +95,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}

mir::Terminator::Drop { ref value, target, unwind } => {
let lvalue = self.trans_lvalue(bcx, value);
let ty = lvalue.ty.to_ty(bcx.tcx());
// Double check for necessity to drop
if !glue::type_needs_drop(bcx.tcx(), ty) {
build::Br(bcx, self.llblock(target), DebugLoc::None);
return;
}
let drop_fn = glue::get_drop_glue(bcx.ccx(), ty);
let drop_ty = glue::get_drop_glue_type(bcx.ccx(), ty);
let llvalue = if drop_ty != ty {
build::PointerCast(bcx, lvalue.llval,
type_of::type_of(bcx.ccx(), drop_ty).ptr_to())
} else {
lvalue.llval
};
if let Some(unwind) = unwind {
let uwbcx = self.bcx(unwind);
let unwind = self.make_landing_pad(uwbcx);
build::Invoke(bcx,
drop_fn,
&[llvalue],
self.llblock(target),
unwind.llbb,
None,
DebugLoc::None);
} else {
build::Call(bcx, drop_fn, &[llvalue], None, DebugLoc::None);
build::Br(bcx, self.llblock(target), DebugLoc::None);
}
self.trans_drop(bcx, value, target, unwind);
}

mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => {
Expand Down Expand Up @@ -287,7 +258,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
}

fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> {
pub fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> {
let bcx = cleanup.fcx.new_block("cleanup", None);
// FIXME(#30941) this doesn't handle msvc-style exceptions
*bcx.lpad.borrow_mut() = Some(LandingPad::gnu());
Expand All @@ -314,11 +285,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
}

fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> {
pub fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> {
self.blocks[bb.index()]
}

fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
pub fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef {
self.blocks[bb.index()].llbb
}
}
104 changes: 104 additions & 0 deletions src/librustc_trans/trans/mir/drop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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.

use llvm::ValueRef;
use rustc::middle::ty::Ty;
use rustc::mir::repr as mir;
use trans::adt;
use trans::base;
use trans::build;
use trans::common::{self, Block};
use trans::debuginfo::DebugLoc;
use trans::glue;
use trans::machine;
use trans::type_of;
use trans::type_::Type;

use super::{MirContext, TempRef};

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
pub fn trans_drop(&mut self,
bcx: Block<'bcx, 'tcx>,
value: &mir::Lvalue<'tcx>,
target: mir::BasicBlock,
unwind: Option<mir::BasicBlock>) {
let lvalue = self.trans_lvalue(bcx, value);
let ty = lvalue.ty.to_ty(bcx.tcx());
// Double check for necessity to drop
if !glue::type_needs_drop(bcx.tcx(), ty) {
build::Br(bcx, self.llblock(target), DebugLoc::None);
return;
}
let drop_fn = glue::get_drop_glue(bcx.ccx(), ty);
let drop_ty = glue::get_drop_glue_type(bcx.ccx(), ty);
let llvalue = if drop_ty != ty {
build::PointerCast(bcx, lvalue.llval,
type_of::type_of(bcx.ccx(), drop_ty).ptr_to())
} else {
lvalue.llval
};
if let Some(unwind) = unwind {
// block cannot be cleanup in this case, so a regular block is fine
let intermediate_bcx = bcx.fcx.new_block("", None);
let uwbcx = self.bcx(unwind);
let unwind = self.make_landing_pad(uwbcx);
// FIXME: it could be possible to do zeroing before invoking here if the drop glue
// didn’t code in the checks inside itself.
build::Invoke(bcx,
drop_fn,
&[llvalue],
intermediate_bcx.llbb,
unwind.llbb,
None,
DebugLoc::None);
// FIXME: perhaps we also should fill inside failed branch? We do not want to re-drop a
// failed drop again by mistake. (conflicts with MSVC SEH if we don’t want to introduce
// a heap of hacks)
self.drop_fill(intermediate_bcx, lvalue.llval, ty);
build::Br(intermediate_bcx, self.llblock(target), DebugLoc::None);
} else {
build::Call(bcx, drop_fn, &[llvalue], None, DebugLoc::None);
self.drop_fill(bcx, lvalue.llval, ty);
build::Br(bcx, self.llblock(target), DebugLoc::None);
}
}

pub fn drop_fill(&mut self, bcx: Block<'bcx, 'tcx>, value: ValueRef, ty: Ty<'tcx>) {
let llty = type_of::type_of(bcx.ccx(), ty);
let llptr = build::PointerCast(bcx, value, Type::i8(bcx.ccx()).ptr_to());
let filling = common::C_u8(bcx.ccx(), adt::DTOR_DONE);
let size = machine::llsize_of(bcx.ccx(), llty);
let align = common::C_u32(bcx.ccx(), machine::llalign_of_min(bcx.ccx(), llty));
base::call_memset(&build::B(bcx), llptr, filling, size, align, false);
}

pub fn set_operand_dropped(&mut self,
Copy link
Member Author

Choose a reason for hiding this comment

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

This also feels seriously hacky to me, but I don’t see an obvious way to dropflag-fill a OperandValue::Immediate.

Copy link
Contributor

Choose a reason for hiding this comment

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

You could use an out-of-line drop flag for these

bcx: Block<'bcx, 'tcx>,
operand: &mir::Operand<'tcx>) {
match *operand {
mir::Operand::Constant(_) => return,
mir::Operand::Consume(ref lvalue) => {
if let mir::Lvalue::Temp(idx) = *lvalue {
if let TempRef::Operand(..) = self.temps[idx as usize] {
return // we do not handle these, should we?
}
}
let lvalue = self.trans_lvalue(bcx, lvalue);
let ty = lvalue.ty.to_ty(bcx.tcx());
if !glue::type_needs_drop(bcx.tcx(), ty) ||
common::type_is_fat_ptr(bcx.tcx(), ty) {
return
} else {
self.drop_fill(bcx, lvalue.llval, ty);
}
}
}
}
}
1 change: 1 addition & 0 deletions src/librustc_trans/trans/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ mod analyze;
mod block;
mod constant;
mod did;
mod drop;
mod lvalue;
mod operand;
mod rvalue;
Expand Down
16 changes: 0 additions & 16 deletions src/librustc_trans/trans/mir/operand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,22 +147,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
}
}

pub fn trans_operand_into(&mut self,
bcx: Block<'bcx, 'tcx>,
lldest: ValueRef,
operand: &mir::Operand<'tcx>)
{
debug!("trans_operand_into(lldest={}, operand={:?})",
bcx.val_to_string(lldest),
operand);

// FIXME: consider not copying constants through the
// stack.

let o = self.trans_operand(bcx, operand);
self.store_operand(bcx, lldest, o);
}

pub fn store_operand(&mut self,
bcx: Block<'bcx, 'tcx>,
lldest: ValueRef,
Expand Down
16 changes: 9 additions & 7 deletions src/librustc_trans/trans/mir/rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {

match *rvalue {
mir::Rvalue::Use(ref operand) => {
self.trans_operand_into(bcx, dest.llval, operand);
// FIXME: consider not copying constants through stack. (fixable by translating
// constants into OperandValue::Ref, why don’t we do that yet if we don’t?)
let tr_operand = self.trans_operand(bcx, operand);
self.store_operand(bcx, dest.llval, tr_operand);
self.set_operand_dropped(bcx, operand);
bcx
}

Expand Down Expand Up @@ -167,11 +171,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);

match *rvalue {
mir::Rvalue::Use(ref operand) => {
let operand = self.trans_operand(bcx, operand);
(bcx, operand)
}

mir::Rvalue::Cast(ref kind, ref operand, cast_ty) => {
let operand = self.trans_operand(bcx, operand);
debug!("cast operand is {}", operand.repr(bcx));
Expand Down Expand Up @@ -401,6 +400,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
})
}

mir::Rvalue::Use(..) |
mir::Rvalue::Repeat(..) |
mir::Rvalue::Aggregate(..) |
mir::Rvalue::Slice { .. } |
Expand Down Expand Up @@ -511,14 +511,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {

pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool {
match *rvalue {
mir::Rvalue::Use(..) | // (*)
mir::Rvalue::Ref(..) |
mir::Rvalue::Len(..) |
mir::Rvalue::Cast(..) | // (*)
mir::Rvalue::BinaryOp(..) |
mir::Rvalue::UnaryOp(..) |
mir::Rvalue::Box(..) =>
true,
mir::Rvalue::Use(..) | // (**)
mir::Rvalue::Repeat(..) |
mir::Rvalue::Aggregate(..) |
mir::Rvalue::Slice { .. } |
Expand All @@ -527,4 +527,6 @@ pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool {
}

// (*) this is only true if the type is suitable
// (**) we need to zero-out the old value before moving, so we are restricted to either
// ensuring all users of `Use` set it themselves or not allowing to “create” operand for it.
}
43 changes: 43 additions & 0 deletions src/test/run-fail/mir_dynamic_drops_1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2016 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.
#![feature(rustc_attrs)]
// error-pattern:drop 1
// error-pattern:drop 2
use std::io::{self, Write};


/// Structure which will not allow to be dropped twice.
struct Droppable<'a>(&'a mut bool, u32);
impl<'a> Drop for Droppable<'a> {
fn drop(&mut self) {
if *self.0 {
writeln!(io::stderr(), "{} dropped twice", self.1);
::std::process::exit(1);
}
writeln!(io::stderr(), "drop {}", self.1);
*self.0 = true;
}
}

#[rustc_mir]
fn mir(){
let mut b1 = false;
let mut b2 = false;
let x = Droppable(&mut b1, 1);
let y = Droppable(&mut b2, 2);
let mut z = x;
let k = y;
z = k;
}

fn main() {
mir();
panic!();
}
40 changes: 40 additions & 0 deletions src/test/run-fail/mir_dynamic_drops_2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2016 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.
#![feature(rustc_attrs)]
// error-pattern:drop 1
use std::io::{self, Write};


/// Structure which will not allow to be dropped twice.
struct Droppable<'a>(&'a mut bool, u32);
impl<'a> Drop for Droppable<'a> {
fn drop(&mut self) {
if *self.0 {
writeln!(io::stderr(), "{} dropped twice", self.1);
::std::process::exit(1);
}
writeln!(io::stderr(), "drop {}", self.1);
*self.0 = true;
}
}

#[rustc_mir]
fn mir(d: Droppable){
loop {
let x = d;
break;
}
}

fn main() {
let mut d1 = false;
mir(Droppable(&mut d1, 1));
panic!();
}
Loading