summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_transform/src/shim.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_transform/src/shim.rs')
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs131
1 files changed, 96 insertions, 35 deletions
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 3620e94be..882136200 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::*;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::{InternalSubsts, Subst};
-use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
+use rustc_middle::ty::{self, EarlyBinder, GeneratorSubsts, Ty, TyCtxt};
use rustc_target::abi::VariantIdx;
use rustc_index::vec::{Idx, IndexVec};
@@ -17,8 +17,8 @@ use std::iter;
use crate::util::expand_aggregate;
use crate::{
- abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, marker, pass_manager as pm,
- remove_noop_landing_pads, simplify,
+ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, marker,
+ pass_manager as pm, remove_noop_landing_pads, simplify,
};
use rustc_middle::mir::patch::MirPatch;
use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
@@ -92,11 +92,12 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
&mut result,
&[
&add_moves_for_packed_drops::AddMovesForPackedDrops,
+ &deref_separator::Derefer,
&remove_noop_landing_pads::RemoveNoopLandingPads,
&simplify::SimplifyCfg::new("make_shim"),
&add_call_guards::CriticalCallEdges,
&abort_unwinding_calls::AbortUnwindingCalls,
- &marker::PhaseChange(MirPhase::Const),
+ &marker::PhaseChange(MirPhase::Runtime(RuntimePhase::Optimized)),
],
);
@@ -322,6 +323,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
builder.tuple_like_shim(dest, src, substs.as_closure().upvar_tys())
}
ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
+ ty::Generator(gen_def_id, substs, hir::Movability::Movable) => {
+ builder.generator_shim(dest, src, *gen_def_id, substs.as_generator())
+ }
_ => bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty),
};
@@ -387,7 +391,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
/// offset=0 will give you the index of the next BasicBlock,
/// offset=1 will give the index of the next-to-next block,
/// offset=-1 will give you the index of the last-created block
- fn block_index_offset(&mut self, offset: usize) -> BasicBlock {
+ fn block_index_offset(&self, offset: usize) -> BasicBlock {
BasicBlock::new(self.blocks.len() + offset)
}
@@ -460,49 +464,106 @@ impl<'tcx> CloneShimBuilder<'tcx> {
);
}
- fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
+ fn clone_fields<I>(
+ &mut self,
+ dest: Place<'tcx>,
+ src: Place<'tcx>,
+ target: BasicBlock,
+ mut unwind: BasicBlock,
+ tys: I,
+ ) -> BasicBlock
where
I: IntoIterator<Item = Ty<'tcx>>,
{
- let mut previous_field = None;
+ // For an iterator of length n, create 2*n + 1 blocks.
for (i, ity) in tys.into_iter().enumerate() {
+ // Each iteration creates two blocks, referred to here as block 2*i and block 2*i + 1.
+ //
+ // Block 2*i attempts to clone the field. If successful it branches to 2*i + 2 (the
+ // next clone block). If unsuccessful it branches to the previous unwind block, which
+ // is initially the `unwind` argument passed to this function.
+ //
+ // Block 2*i + 1 is the unwind block for this iteration. It drops the cloned value
+ // created by block 2*i. We store this block in `unwind` so that the next clone block
+ // will unwind to it if cloning fails.
+
let field = Field::new(i);
let src_field = self.tcx.mk_place_field(src, field, ity);
let dest_field = self.tcx.mk_place_field(dest, field, ity);
- // #(2i + 1) is the cleanup block for the previous clone operation
- let cleanup_block = self.block_index_offset(1);
- // #(2i + 2) is the next cloning block
- // (or the Return terminator if this is the last block)
+ let next_unwind = self.block_index_offset(1);
let next_block = self.block_index_offset(2);
+ self.make_clone_call(dest_field, src_field, ity, next_block, unwind);
+ self.block(
+ vec![],
+ TerminatorKind::Drop { place: dest_field, target: unwind, unwind: None },
+ true,
+ );
+ unwind = next_unwind;
+ }
+ // If all clones succeed then we end up here.
+ self.block(vec![], TerminatorKind::Goto { target }, false);
+ unwind
+ }
- // BB #(2i)
- // `dest.i = Clone::clone(&src.i);`
- // Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens.
- self.make_clone_call(dest_field, src_field, ity, next_block, cleanup_block);
-
- // BB #(2i + 1) (cleanup)
- if let Some((previous_field, previous_cleanup)) = previous_field.take() {
- // Drop previous field and goto previous cleanup block.
- self.block(
- vec![],
- TerminatorKind::Drop {
- place: previous_field,
- target: previous_cleanup,
- unwind: None,
- },
- true,
- );
- } else {
- // Nothing to drop, just resume.
- self.block(vec![], TerminatorKind::Resume, true);
- }
+ fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
+ where
+ I: IntoIterator<Item = Ty<'tcx>>,
+ {
+ self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
+ let unwind = self.block(vec![], TerminatorKind::Resume, true);
+ let target = self.block(vec![], TerminatorKind::Return, false);
- previous_field = Some((dest_field, cleanup_block));
- }
+ let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, tys);
+ }
- self.block(vec![], TerminatorKind::Return, false);
+ fn generator_shim(
+ &mut self,
+ dest: Place<'tcx>,
+ src: Place<'tcx>,
+ gen_def_id: DefId,
+ substs: GeneratorSubsts<'tcx>,
+ ) {
+ self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
+ let unwind = self.block(vec![], TerminatorKind::Resume, true);
+ // This will get overwritten with a switch once we know the target blocks
+ let switch = self.block(vec![], TerminatorKind::Unreachable, false);
+ let unwind = self.clone_fields(dest, src, switch, unwind, substs.upvar_tys());
+ let target = self.block(vec![], TerminatorKind::Return, false);
+ let unreachable = self.block(vec![], TerminatorKind::Unreachable, false);
+ let mut cases = Vec::with_capacity(substs.state_tys(gen_def_id, self.tcx).count());
+ for (index, state_tys) in substs.state_tys(gen_def_id, self.tcx).enumerate() {
+ let variant_index = VariantIdx::new(index);
+ let dest = self.tcx.mk_place_downcast_unnamed(dest, variant_index);
+ let src = self.tcx.mk_place_downcast_unnamed(src, variant_index);
+ let clone_block = self.block_index_offset(1);
+ let start_block = self.block(
+ vec![self.make_statement(StatementKind::SetDiscriminant {
+ place: Box::new(Place::return_place()),
+ variant_index,
+ })],
+ TerminatorKind::Goto { target: clone_block },
+ false,
+ );
+ cases.push((index as u128, start_block));
+ let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, state_tys);
+ }
+ let discr_ty = substs.discr_ty(self.tcx);
+ let temp = self.make_place(Mutability::Mut, discr_ty);
+ let rvalue = Rvalue::Discriminant(src);
+ let statement = self.make_statement(StatementKind::Assign(Box::new((temp, rvalue))));
+ match &mut self.blocks[switch] {
+ BasicBlockData { statements, terminator: Some(Terminator { kind, .. }), .. } => {
+ statements.push(statement);
+ *kind = TerminatorKind::SwitchInt {
+ discr: Operand::Move(temp),
+ switch_ty: discr_ty,
+ targets: SwitchTargets::new(cases.into_iter(), unreachable),
+ };
+ }
+ BasicBlockData { terminator: None, .. } => unreachable!(),
+ }
}
}