Roll our own MIR for dropping arrays.

This commit is contained in:
Oliver Schneider 2017-03-22 16:16:23 +01:00
parent 030f00a8a1
commit 7c12ebc78d
2 changed files with 206 additions and 23 deletions

View File

@ -5,6 +5,7 @@ use std::fmt::Write;
use rustc::hir::def_id::DefId;
use rustc::hir::map::definitions::DefPathData;
use rustc::middle::const_val::ConstVal;
use rustc_const_math::{ConstInt, ConstUsize};
use rustc::mir;
use rustc::traits::Reveal;
use rustc::ty::layout::{self, Layout, Size};
@ -44,6 +45,9 @@ pub struct EvalContext<'a, 'tcx: 'a> {
/// This prevents infinite loops and huge computations from freezing up const eval.
/// Remove once halting problem is solved.
pub(crate) steps_remaining: u64,
/// Drop glue for arrays and slices
pub(crate) seq_drop_glue: MirRef<'tcx>,
}
/// A stack frame.
@ -124,6 +128,176 @@ impl Default for ResourceLimits {
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
let source_info = mir::SourceInfo {
span: DUMMY_SP,
scope: mir::ARGUMENT_VISIBILITY_SCOPE
};
// i = 0; len = Len(*a0); goto head;
let start_block = mir::BasicBlockData {
statements: vec![
mir::Statement {
source_info,
kind: mir::StatementKind::Assign(
mir::Lvalue::Local(mir::Local::new(2)),
mir::Rvalue::Use(mir::Operand::Constant(mir::Constant {
span: DUMMY_SP,
ty: tcx.types.usize,
literal: mir::Literal::Value {
value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())),
},
}))
)
},
mir::Statement {
source_info,
kind: mir::StatementKind::Assign(
mir::Lvalue::Local(mir::Local::new(3)),
mir::Rvalue::Len(mir::Lvalue::Projection(Box::new(mir::LvalueProjection {
base: mir::Lvalue::Local(mir::Local::new(1)),
elem: mir::ProjectionElem::Deref,
}))),
)
},
],
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) },
}),
is_cleanup: false
};
// head: done = i == len; switch done { 1 => ret, 0 => loop }
let head = mir::BasicBlockData {
statements: vec![
mir::Statement {
source_info,
kind: mir::StatementKind::Assign(
mir::Lvalue::Local(mir::Local::new(4)),
mir::Rvalue::BinaryOp(
mir::BinOp::Eq,
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))),
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(3))),
)
)
},
],
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::SwitchInt {
targets: vec![
mir::BasicBlock::new(2),
mir::BasicBlock::new(4),
],
discr: mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(4))),
switch_ty: tcx.types.bool,
values: vec![ConstInt::U8(0)].into(),
},
}),
is_cleanup: false
};
// loop: drop (*a0)[i]; goto inc;
let loop_ = mir::BasicBlockData {
statements: Vec::new(),
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::Drop {
target: mir::BasicBlock::new(3),
unwind: None,
location: mir::Lvalue::Projection(Box::new(
mir::LvalueProjection {
base: mir::Lvalue::Projection(Box::new(
mir::LvalueProjection {
base: mir::Lvalue::Local(mir::Local::new(1)),
elem: mir::ProjectionElem::Deref,
}
)),
elem: mir::ProjectionElem::Index(mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2)))),
}
)),
},
}),
is_cleanup: false
};
// inc: i++; goto head;
let inc = mir::BasicBlockData {
statements: vec![
mir::Statement {
source_info,
kind: mir::StatementKind::Assign(
mir::Lvalue::Local(mir::Local::new(2)),
mir::Rvalue::BinaryOp(
mir::BinOp::Add,
mir::Operand::Consume(mir::Lvalue::Local(mir::Local::new(2))),
mir::Operand::Constant(mir::Constant {
span: DUMMY_SP,
ty: tcx.types.usize,
literal: mir::Literal::Value {
value: ConstVal::Integral(ConstInt::Usize(ConstUsize::new(0, tcx.sess.target.uint_type).unwrap())),
},
}),
)
)
},
],
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::Goto { target: mir::BasicBlock::new(1) },
}),
is_cleanup: false
};
// ret: return;
let ret = mir::BasicBlockData {
statements: Vec::new(),
terminator: Some(mir::Terminator {
source_info: source_info,
kind: mir::TerminatorKind::Return,
}),
is_cleanup: false
};
let locals = vec![
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.mk_nil(),
name: None,
source_info: None,
},
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.mk_mut_ptr(tcx.mk_self_type()),
name: None,
source_info: None,
},
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.types.usize,
name: None,
source_info: None,
},
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.types.usize,
name: None,
source_info: None,
},
mir::LocalDecl {
mutability: mir::Mutability::Mut,
ty: tcx.types.bool,
name: None,
source_info: None,
},
];
let seq_drop_glue = mir::Mir::new(
vec![start_block, head, loop_, inc, ret].into_iter().collect(),
Vec::new().into_iter().collect(), // vis scopes
Vec::new().into_iter().collect(), // promoted
tcx.mk_nil(), // return type
locals.into_iter().collect(),
1, // arg_count
Vec::new(), // upvars
DUMMY_SP,
);
let seq_drop_glue = tcx.alloc_mir(seq_drop_glue);
// Perma-borrow MIR from shims to prevent mutation.
::std::mem::forget(seq_drop_glue.borrow());
EvalContext {
tcx,
memory: Memory::new(&tcx.data_layout, limits.memory_size),
@ -131,6 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
stack: Vec::new(),
stack_limit: limits.stack_limit,
steps_remaining: limits.step_limit,
seq_drop_glue: seq_drop_glue.borrow(),
}
}
@ -1958,3 +2133,13 @@ fn fulfill_obligation<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
vtable
})
}
pub fn resolve_drop_in_place<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
ty: Ty<'tcx>,
) -> ty::Instance<'tcx>
{
let def_id = tcx.require_lang_item(::rustc::middle::lang_items::DropInPlaceFnLangItem);
let substs = tcx.intern_substs(&[Kind::from(ty)]);
resolve(tcx, def_id, substs)
}

View File

@ -80,18 +80,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let src_ptr = self.force_allocation(lval)?.to_ptr();
let ty = self.lvalue_ty(location);
let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty);
self.goto_block(target);
let drop_in_place = self.tcx.lang_items.drop_in_place_fn().expect("drop_in_place lang item not available");
let env = self.tcx.empty_parameter_environment();
let def = if self.tcx.type_needs_drop_given_env(ty, &env) {
ty::InstanceDef::DropGlue(drop_in_place, Some(ty))
} else {
ty::InstanceDef::DropGlue(drop_in_place, None)
let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty);
if let ty::InstanceDef::DropGlue(_, None) = instance.def {
// we don't actually need to drop anything
return Ok(());
}
let mir = match ty.sty {
ty::TyDynamic(..) => unimplemented!(),
ty::TyArray(..) | ty::TySlice(..) => ::eval_context::MirRef::clone(&self.seq_drop_glue),
_ => self.load_mir(instance.def)?,
};
let substs = self.substs();
let instance = ty::Instance { substs, def };
let mir = self.load_mir(instance.def)?;
self.push_stack_frame(
instance,
terminator.source_info.span,
@ -162,19 +166,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.dump_local(ret);
Ok(())
},
/*Abi::C => {
let sig = self.erase_lifetimes(&sig);
let ty = sig.output();
let (ret, target) = destination.unwrap();
match instance.def {
ty::InstanceDef::Item(_) => {},
_ => bug!("C abi function must be InstanceDef::Item"),
}
self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?;
self.dump_local(ret);
self.goto_block(target);
Ok(())
},*/
ty::InstanceDef::ClosureOnceShim{..} => {
let mut args = Vec::new();
for arg in arg_operands {
@ -198,7 +189,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
args.push((arg_val, arg_ty));
}
match sig.abi {
Abi::C => unimplemented!(),
Abi::C => {
let ty = sig.output();
let (ret, target) = destination.unwrap();
self.call_c_abi(instance.def_id(), arg_operands, ret, ty)?;
self.dump_local(ret);
self.goto_block(target);
return Ok(());
},
Abi::Rust => {},
Abi::RustCall => self.unpack_fn_args(&mut args)?,
_ => unimplemented!(),