From 162bc513fbc15066d04f865da232702fefc7b923 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 18 May 2017 23:59:39 +0300 Subject: [PATCH] use a pointer-based array drop loop for non-zero-sized types --- src/librustc_mir/util/elaborate_drops.rs | 189 ++++++++++++++++------- 1 file changed, 136 insertions(+), 53 deletions(-) diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index c1d8d087eac..50ebe366387 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -493,13 +493,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let discr_ty = adt.repr.discr_type().to_ty(self.tcx()); let discr = Lvalue::Local(self.new_temp(discr_ty)); let discr_rv = Rvalue::Discriminant(self.lvalue.clone()); - let switch_block = self.elaborator.patch().new_block(BasicBlockData { - statements: vec![ - Statement { - source_info: self.source_info, - kind: StatementKind::Assign(discr.clone(), discr_rv), - } - ], + let switch_block = BasicBlockData { + statements: vec![self.assign(&discr, discr_rv)], terminator: Some(Terminator { source_info: self.source_info, kind: TerminatorKind::SwitchInt { @@ -510,7 +505,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> } }), is_cleanup: unwind.is_cleanup(), - }); + }; + let switch_block = self.elaborator.patch().new_block(switch_block); self.drop_flag_test_block(switch_block, succ, unwind) } @@ -531,14 +527,11 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let ref_lvalue = self.new_temp(ref_ty); let unit_temp = Lvalue::Local(self.new_temp(tcx.mk_nil())); - self.elaborator.patch().new_block(BasicBlockData { - statements: vec![Statement { - source_info: self.source_info, - kind: StatementKind::Assign( - Lvalue::Local(ref_lvalue), - Rvalue::Ref(tcx.types.re_erased, BorrowKind::Mut, self.lvalue.clone()) - ) - }], + let result = BasicBlockData { + statements: vec![self.assign( + &Lvalue::Local(ref_lvalue), + Rvalue::Ref(tcx.types.re_erased, BorrowKind::Mut, self.lvalue.clone()) + )], terminator: Some(Terminator { kind: TerminatorKind::Call { func: Operand::function_handle(tcx, drop_fn.def_id, substs, @@ -550,24 +543,33 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> source_info: self.source_info }), is_cleanup: unwind.is_cleanup(), - }) + }; + self.elaborator.patch().new_block(result) } /// create a loop that drops an array: + /// + /// /// loop-block: - /// can_go = index == len + /// can_go = cur == length_or_end /// if can_go then succ else drop-block /// drop-block: - /// ptr = &mut LV[index] - /// index = index + 1 + /// if ptr_based { + /// ptr = cur + /// cur = cur.offset(1) + /// } else { + /// ptr = &mut LV[cur] + /// cur = cur + 1 + /// } /// drop(ptr) fn drop_loop(&mut self, succ: BasicBlock, - index: &Lvalue<'tcx>, - length: &Lvalue<'tcx>, + cur: &Lvalue<'tcx>, + length_or_end: &Lvalue<'tcx>, ety: Ty<'tcx>, - unwind: Unwind) + unwind: Unwind, + ptr_based: bool) -> BasicBlock { let use_ = |lv: &Lvalue<'tcx>| Operand::Consume(lv.clone()); @@ -581,17 +583,21 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let can_go = &Lvalue::Local(self.new_temp(tcx.types.bool)); let one = self.constant_usize(1); - let drop_block = self.elaborator.patch().new_block(BasicBlockData { + let (ptr_next, cur_next) = if ptr_based { + (Rvalue::Use(use_(cur)), + Rvalue::BinaryOp(BinOp::Offset, use_(cur), one)) + } else { + (Rvalue::Ref( + tcx.types.re_erased, + BorrowKind::Mut, + self.lvalue.clone().index(use_(cur))), + Rvalue::BinaryOp(BinOp::Add, use_(cur), one)) + }; + + let drop_block = BasicBlockData { statements: vec![ - Statement { source_info: self.source_info, kind: StatementKind::Assign( - ptr.clone(), Rvalue::Ref( - tcx.types.re_erased, BorrowKind::Mut, - self.lvalue.clone().index(use_(index)) - ), - )}, - Statement { source_info: self.source_info, kind: StatementKind::Assign( - index.clone(), Rvalue::BinaryOp(BinOp::Add, use_(index), one) - )}, + self.assign(ptr, ptr_next), + self.assign(cur, cur_next) ], is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { @@ -599,20 +605,22 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> // this gets overwritten by drop elaboration. kind: TerminatorKind::Unreachable, }) - }); + }; + let drop_block = self.elaborator.patch().new_block(drop_block); - let loop_block = self.elaborator.patch().new_block(BasicBlockData { + let loop_block = BasicBlockData { statements: vec![ - Statement { source_info: self.source_info, kind: StatementKind::Assign( - can_go.clone(), Rvalue::BinaryOp(BinOp::Eq, use_(index), use_(length)) - )}, + self.assign(can_go, Rvalue::BinaryOp(BinOp::Eq, + use_(cur), + use_(length_or_end))) ], is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { source_info: self.source_info, kind: TerminatorKind::if_(tcx, use_(can_go), succ, drop_block) }) - }); + }; + let loop_block = self.elaborator.patch().new_block(loop_block); self.elaborator.patch().patch_terminator(drop_block, TerminatorKind::Drop { location: ptr.clone().deref(), @@ -625,29 +633,97 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn open_drop_for_array(&mut self, ety: Ty<'tcx>) -> BasicBlock { debug!("open_drop_for_array({:?})", ety); - // FIXME: using an index instead of a pointer to avoid - // special-casing ZSTs. + + // if size_of::() == 0 { + // index_based_loop + // } else { + // ptr_based_loop + // } + let tcx = self.tcx(); - let index = &Lvalue::Local(self.new_temp(tcx.types.usize)); - let length = &Lvalue::Local(self.new_temp(tcx.types.usize)); + + let use_ = |lv: &Lvalue<'tcx>| Operand::Consume(lv.clone()); + let size = &Lvalue::Local(self.new_temp(tcx.types.usize)); + let size_is_zero = &Lvalue::Local(self.new_temp(tcx.types.bool)); + let base_block = BasicBlockData { + statements: vec![ + self.assign(size, Rvalue::NullaryOp(NullOp::SizeOf, ety)), + self.assign(size_is_zero, Rvalue::BinaryOp(BinOp::Eq, + use_(size), + self.constant_usize(0))) + ], + is_cleanup: self.unwind.is_cleanup(), + terminator: Some(Terminator { + source_info: self.source_info, + kind: TerminatorKind::if_( + tcx, + use_(size_is_zero), + self.drop_loop_pair(ety, false), + self.drop_loop_pair(ety, true) + ) + }) + }; + self.elaborator.patch().new_block(base_block) + } + + // create a pair of drop-loops of `lvalue`, which drops its contents + // even in the case of 1 panic. If `ptr_based`, create a pointer loop, + // otherwise create an index loop. + fn drop_loop_pair(&mut self, ety: Ty<'tcx>, ptr_based: bool) -> BasicBlock { + debug!("drop_loop_pair({:?}, {:?})", ety, ptr_based); + let tcx = self.tcx(); + let iter_ty = if ptr_based { + tcx.mk_ptr(ty::TypeAndMut { ty: ety, mutbl: hir::Mutability::MutMutable }) + } else { + tcx.types.usize + }; + + let cur = Lvalue::Local(self.new_temp(iter_ty)); + let length = Lvalue::Local(self.new_temp(tcx.types.usize)); + let length_or_end = if ptr_based { + Lvalue::Local(self.new_temp(iter_ty)) + } else { + length.clone() + }; let unwind = self.unwind.map(|unwind| { - self.drop_loop(unwind, index, length, ety, Unwind::InCleanup) + self.drop_loop(unwind, + &cur, + &length_or_end, + ety, + Unwind::InCleanup, + ptr_based) }); let succ = self.succ; // FIXME(#6393) - let loop_block = self.drop_loop(succ, index, length, ety, unwind); + let loop_block = self.drop_loop( + succ, + &cur, + &length_or_end, + ety, + unwind, + ptr_based); let zero = self.constant_usize(0); + let mut drop_block_stmts = vec![]; + drop_block_stmts.push(self.assign(&length, Rvalue::Len(self.lvalue.clone()))); + if ptr_based { + // cur = &LV[0]; + // end = &LV[len]; + drop_block_stmts.push(self.assign(&cur, Rvalue::Ref( + tcx.types.re_erased, BorrowKind::Mut, + self.lvalue.clone().index(zero.clone()) + ))); + drop_block_stmts.push(self.assign(&length_or_end, Rvalue::Ref( + tcx.types.re_erased, BorrowKind::Mut, + self.lvalue.clone().index(Operand::Consume(length.clone())) + ))); + } else { + // index = 0 (length already pushed) + drop_block_stmts.push(self.assign(&cur, Rvalue::Use(zero))); + } let drop_block = self.elaborator.patch().new_block(BasicBlockData { - statements: vec![ - Statement { source_info: self.source_info, kind: StatementKind::Assign( - length.clone(), Rvalue::Len(self.lvalue.clone()) - )}, - Statement { source_info: self.source_info, kind: StatementKind::Assign( - index.clone(), Rvalue::Use(zero), - )}, - ], + statements: drop_block_stmts, is_cleanup: unwind.is_cleanup(), terminator: Some(Terminator { source_info: self.source_info, @@ -836,4 +912,11 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> literal: Literal::Value { value: ConstVal::Integral(self.tcx().const_usize(val)) } }) } + + fn assign(&self, lhs: &Lvalue<'tcx>, rhs: Rvalue<'tcx>) -> Statement<'tcx> { + Statement { + source_info: self.source_info, + kind: StatementKind::Assign(lhs.clone(), rhs) + } + } }