From 7c12ebc78dbe86896c66e8cfb4e6923184f89e24 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 22 Mar 2017 16:16:23 +0100 Subject: [PATCH] Roll our own MIR for dropping arrays. --- src/eval_context.rs | 185 ++++++++++++++++++++++++++++++++++++++++++ src/terminator/mod.rs | 44 +++++----- 2 files changed, 206 insertions(+), 23 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 1ec690e6204..5fe66ee50c7 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -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) +} diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 53c22926438..8258395c402 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -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!(),