Rm allocation in candidate

Instead of storing an extra array for discriminant values, create an allocation there and store
those in an allocation immediately.
This commit is contained in:
kadmin 2021-06-16 05:46:56 +00:00
parent 3e97cef7e5
commit 5d9f5145ac
14 changed files with 746 additions and 281 deletions

View File

@ -1,278 +0,0 @@
use crate::transform::MirPass;
use rustc_data_structures::stable_map::FxHashMap;
use rustc_middle::mir::*;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
use rustc_span::def_id::DefId;
use rustc_target::abi::{HasDataLayout, Size, TagEncoding, Variants};
use std::array::IntoIter;
/// A pass that seeks to optimize unnecessary moves of large enum types, if there is a large
/// enough discrepanc between them
pub struct EnumSizeOpt<const DISCREPANCY: u64>;
impl<'tcx, const D: u64> MirPass<'tcx> for EnumSizeOpt<D> {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
self.optim(tcx, body);
}
}
impl<const D: u64> EnumSizeOpt<D> {
fn candidate<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
body_did: DefId,
) -> Option<(u64, Vec<Size>)> {
match ty.kind() {
ty::Adt(adt_def, _substs) if adt_def.is_enum() => {
let p_e = tcx.param_env(body_did);
let layout =
if let Ok(layout) = tcx.layout_of(p_e.and(ty)) { layout } else { return None };
let variants = &layout.variants;
match variants {
Variants::Single { .. } => None,
Variants::Multiple { variants, .. } if variants.len() <= 1 => None,
Variants::Multiple { tag_encoding, .. }
if matches!(tag_encoding, TagEncoding::Niche { .. }) =>
{
None
}
Variants::Multiple { variants, .. } => {
let min = variants.iter().map(|v| v.size).min().unwrap();
let max = variants.iter().map(|v| v.size).max().unwrap();
if max.bytes() - min.bytes() < D {
return None;
}
let mut discr_sizes = vec![Size::ZERO; adt_def.discriminants(tcx).count()];
for (var_idx, layout) in variants.iter_enumerated() {
let disc_idx =
adt_def.discriminant_for_variant(tcx, var_idx).val as usize;
assert_eq!(discr_sizes[disc_idx], Size::ZERO);
discr_sizes[disc_idx] = layout.size;
}
Some((variants.len() as u64, discr_sizes))
}
}
}
_ => None,
}
}
fn optim(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let mut alloc_cache = FxHashMap::default();
let body_did = body.source.def_id();
let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut();
for bb in bbs {
bb.expand_statements(|st| {
match &st.kind {
StatementKind::Assign(box (
lhs,
Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)),
)) => {
let ty = lhs.ty(local_decls, tcx).ty;
let source_info = st.source_info;
let span = source_info.span;
let (num_variants, sizes) =
if let Some(cand) = Self::candidate(tcx, ty, body_did) {
cand
} else {
return None;
};
let adt_def = ty.ty_adt_def().unwrap();
let alloc = if let Some(alloc) = alloc_cache.get(ty) {
alloc
} else {
let data_layout = tcx.data_layout();
let ptr_sized_int = data_layout.ptr_sized_integer();
let target_bytes = ptr_sized_int.size().bytes() as usize;
let mut data = vec![0; target_bytes * num_variants as usize];
let mut curr = 0;
macro_rules! encode_store {
($endian: expr, $bytes: expr) => {
let bytes = match $endian {
rustc_target::abi::Endian::Little => $bytes.to_le_bytes(),
rustc_target::abi::Endian::Big => $bytes.to_be_bytes(),
};
for b in bytes {
data[curr] = b;
curr += 1;
}
};
}
for sz in sizes {
match ptr_sized_int {
rustc_target::abi::Integer::I32 => {
encode_store!(data_layout.endian, sz.bytes() as u32);
}
rustc_target::abi::Integer::I64 => {
encode_store!(data_layout.endian, sz.bytes());
}
_ => unreachable!(),
};
}
let alloc = interpret::Allocation::from_bytes(
data,
tcx.data_layout.ptr_sized_integer().align(&tcx.data_layout).abi,
Mutability::Not,
);
let alloc = tcx.intern_const_alloc(alloc);
alloc_cache.insert(ty, alloc);
// FIXME(jknodt) use entry API
alloc_cache.get(ty).unwrap()
};
let tmp_ty = tcx.mk_ty(ty::Array(
tcx.types.usize,
Const::from_usize(tcx, num_variants),
));
let size_array_local = local_decls.push(LocalDecl::new(tmp_ty, span));
let store_live = Statement {
source_info,
kind: StatementKind::StorageLive(size_array_local),
};
let place = Place::from(size_array_local);
let constant_vals = Constant {
span,
user_ty: None,
literal: ConstantKind::Val(
interpret::ConstValue::ByRef { alloc, offset: Size::ZERO },
tmp_ty,
),
};
let rval = Rvalue::Use(Operand::Constant(box (constant_vals)));
let const_assign = Statement {
source_info,
kind: StatementKind::Assign(box (place, rval)),
};
let discr_place = Place::from(
local_decls
.push(LocalDecl::new(adt_def.repr.discr_type().to_ty(tcx), span)),
);
let store_discr = Statement {
source_info,
kind: StatementKind::Assign(box (
discr_place,
Rvalue::Discriminant(*rhs),
)),
};
let discr_cast_place =
Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span)));
let cast_discr = Statement {
source_info,
kind: StatementKind::Assign(box (
discr_cast_place,
Rvalue::Cast(
CastKind::Misc,
Operand::Copy(discr_place),
tcx.types.usize,
),
)),
};
let size_place =
Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span)));
let store_size = Statement {
source_info,
kind: StatementKind::Assign(box (
size_place,
Rvalue::Use(Operand::Copy(Place {
local: size_array_local,
projection: tcx.intern_place_elems(&[PlaceElem::Index(
discr_cast_place.local,
)]),
})),
)),
};
let dst =
Place::from(local_decls.push(LocalDecl::new(tcx.mk_mut_ptr(ty), span)));
let dst_ptr = Statement {
source_info,
kind: StatementKind::Assign(box (
dst,
Rvalue::AddressOf(Mutability::Mut, *lhs),
)),
};
let dst_cast_ty = tcx.mk_mut_ptr(tcx.types.u8);
let dst_cast_place =
Place::from(local_decls.push(LocalDecl::new(dst_cast_ty, span)));
let dst_cast = Statement {
source_info,
kind: StatementKind::Assign(box (
dst_cast_place,
Rvalue::Cast(CastKind::Misc, Operand::Copy(dst), dst_cast_ty),
)),
};
let src =
Place::from(local_decls.push(LocalDecl::new(tcx.mk_imm_ptr(ty), span)));
let src_ptr = Statement {
source_info,
kind: StatementKind::Assign(box (
src,
Rvalue::AddressOf(Mutability::Not, *rhs),
)),
};
let src_cast_ty = tcx.mk_imm_ptr(tcx.types.u8);
let src_cast_place =
Place::from(local_decls.push(LocalDecl::new(src_cast_ty, span)));
let src_cast = Statement {
source_info,
kind: StatementKind::Assign(box (
src_cast_place,
Rvalue::Cast(CastKind::Misc, Operand::Copy(src), src_cast_ty),
)),
};
let copy_bytes = Statement {
source_info,
kind: StatementKind::CopyNonOverlapping(box CopyNonOverlapping {
src: Operand::Copy(src_cast_place),
dst: Operand::Copy(dst_cast_place),
count: Operand::Copy(size_place),
}),
};
let store_dead = Statement {
source_info,
kind: StatementKind::StorageDead(size_array_local),
};
let iter = IntoIter::new([
store_live,
const_assign,
store_discr,
cast_discr,
store_size,
dst_ptr,
dst_cast,
src_ptr,
src_cast,
copy_bytes,
store_dead,
]);
st.make_nop();
Some(iter)
}
_ => return None,
}
});
}
}
}

View File

@ -0,0 +1,283 @@
use crate::rustc_middle::ty::util::IntTypeExt;
use crate::MirPass;
use rustc_data_structures::stable_map::FxHashMap;
use rustc_middle::mir::interpret::AllocId;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, AdtDef, Const, ParamEnv, Ty, TyCtxt};
use rustc_target::abi::{HasDataLayout, Size, TagEncoding, Variants};
/// A pass that seeks to optimize unnecessary moves of large enum types, if there is a large
/// enough discrepancy between them.
///
/// i.e. If there is are two variants:
/// ```
/// enum Example {
/// Small,
/// Large([u32; 1024]),
/// }
/// ```
/// Instead of emitting moves of the large variant,
/// Perform a memcpy instead.
/// Based off of [this HackMD](https://hackmd.io/@ft4bxUsFT5CEUBmRKYHr7w/rJM8BBPzD).
pub struct EnumSizeOpt {
pub(crate) discrepancy: u64,
}
impl<'tcx> MirPass<'tcx> for EnumSizeOpt {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let sess = tcx.sess;
if (!sess.opts.debugging_opts.unsound_mir_opts) || sess.mir_opt_level() < 3 {
return;
}
self.optim(tcx, body);
}
}
impl EnumSizeOpt {
fn candidate<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
ty: Ty<'tcx>,
alloc_cache: &mut FxHashMap<Ty<'tcx>, AllocId>,
) -> Option<(AdtDef<'tcx>, usize, AllocId)> {
let adt_def = match ty.kind() {
ty::Adt(adt_def, _substs) if adt_def.is_enum() => adt_def,
_ => return None,
};
let layout = tcx.layout_of(param_env.and(ty)).ok()?;
let variants = match &layout.variants {
Variants::Single { .. } => return None,
Variants::Multiple { tag_encoding, .. }
if matches!(tag_encoding, TagEncoding::Niche { .. }) =>
{
return None;
}
Variants::Multiple { variants, .. } if variants.len() <= 1 => return None,
Variants::Multiple { variants, .. } => variants,
};
let min = variants.iter().map(|v| v.size()).min().unwrap();
let max = variants.iter().map(|v| v.size()).max().unwrap();
if max.bytes() - min.bytes() < self.discrepancy {
return None;
}
let num_discrs = adt_def.discriminants(tcx).count();
if variants.iter_enumerated().any(|(var_idx, _)| {
let discr_for_var = adt_def.discriminant_for_variant(tcx, var_idx).val;
(discr_for_var > usize::MAX as u128) || (discr_for_var as usize >= num_discrs)
}) {
return None;
}
if let Some(alloc_id) = alloc_cache.get(&ty) {
return Some((*adt_def, num_discrs, *alloc_id));
}
let data_layout = tcx.data_layout();
let ptr_sized_int = data_layout.ptr_sized_integer();
let target_bytes = ptr_sized_int.size().bytes() as usize;
let mut data = vec![0; target_bytes * num_discrs];
macro_rules! encode_store {
($curr_idx: expr, $endian: expr, $bytes: expr) => {
let bytes = match $endian {
rustc_target::abi::Endian::Little => $bytes.to_le_bytes(),
rustc_target::abi::Endian::Big => $bytes.to_be_bytes(),
};
for (i, b) in bytes.into_iter().enumerate() {
data[$curr_idx + i] = b;
}
};
}
for (var_idx, layout) in variants.iter_enumerated() {
let curr_idx =
target_bytes * adt_def.discriminant_for_variant(tcx, var_idx).val as usize;
let sz = layout.size();
match ptr_sized_int {
rustc_target::abi::Integer::I32 => {
encode_store!(curr_idx, data_layout.endian, sz.bytes() as u32);
}
rustc_target::abi::Integer::I64 => {
encode_store!(curr_idx, data_layout.endian, sz.bytes());
}
_ => unreachable!(),
};
}
let alloc = interpret::Allocation::from_bytes(
data,
tcx.data_layout.ptr_sized_integer().align(&tcx.data_layout).abi,
Mutability::Not,
);
let alloc = tcx.create_memory_alloc(tcx.intern_const_alloc(alloc));
Some((*adt_def, num_discrs, *alloc_cache.entry(ty).or_insert(alloc)))
}
fn optim<'tcx>(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let mut alloc_cache = FxHashMap::default();
let body_did = body.source.def_id();
let param_env = tcx.param_env(body_did);
let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut();
for bb in bbs {
bb.expand_statements(|st| {
if let StatementKind::Assign(box (
lhs,
Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)),
)) = &st.kind
{
let ty = lhs.ty(local_decls, tcx).ty;
let source_info = st.source_info;
let span = source_info.span;
let (adt_def, num_variants, alloc_id) =
self.candidate(tcx, param_env, ty, &mut alloc_cache)?;
let alloc = tcx.global_alloc(alloc_id).unwrap_memory();
let tmp_ty = tcx.mk_ty(ty::Array(
tcx.types.usize,
Const::from_usize(tcx, num_variants as u64),
));
let size_array_local = local_decls.push(LocalDecl::new(tmp_ty, span));
let store_live = Statement {
source_info,
kind: StatementKind::StorageLive(size_array_local),
};
let place = Place::from(size_array_local);
let constant_vals = Constant {
span,
user_ty: None,
literal: ConstantKind::Val(
interpret::ConstValue::ByRef { alloc, offset: Size::ZERO },
tmp_ty,
),
};
let rval = Rvalue::Use(Operand::Constant(box (constant_vals)));
let const_assign =
Statement { source_info, kind: StatementKind::Assign(box (place, rval)) };
let discr_place = Place::from(
local_decls
.push(LocalDecl::new(adt_def.repr().discr_type().to_ty(tcx), span)),
);
let store_discr = Statement {
source_info,
kind: StatementKind::Assign(box (discr_place, Rvalue::Discriminant(*rhs))),
};
let discr_cast_place =
Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span)));
let cast_discr = Statement {
source_info,
kind: StatementKind::Assign(box (
discr_cast_place,
Rvalue::Cast(
CastKind::Misc,
Operand::Copy(discr_place),
tcx.types.usize,
),
)),
};
let size_place =
Place::from(local_decls.push(LocalDecl::new(tcx.types.usize, span)));
let store_size = Statement {
source_info,
kind: StatementKind::Assign(box (
size_place,
Rvalue::Use(Operand::Copy(Place {
local: size_array_local,
projection: tcx.intern_place_elems(&[PlaceElem::Index(
discr_cast_place.local,
)]),
})),
)),
};
let dst =
Place::from(local_decls.push(LocalDecl::new(tcx.mk_mut_ptr(ty), span)));
let dst_ptr = Statement {
source_info,
kind: StatementKind::Assign(box (
dst,
Rvalue::AddressOf(Mutability::Mut, *lhs),
)),
};
let dst_cast_ty = tcx.mk_mut_ptr(tcx.types.u8);
let dst_cast_place =
Place::from(local_decls.push(LocalDecl::new(dst_cast_ty, span)));
let dst_cast = Statement {
source_info,
kind: StatementKind::Assign(box (
dst_cast_place,
Rvalue::Cast(CastKind::Misc, Operand::Copy(dst), dst_cast_ty),
)),
};
let src =
Place::from(local_decls.push(LocalDecl::new(tcx.mk_imm_ptr(ty), span)));
let src_ptr = Statement {
source_info,
kind: StatementKind::Assign(box (
src,
Rvalue::AddressOf(Mutability::Not, *rhs),
)),
};
let src_cast_ty = tcx.mk_imm_ptr(tcx.types.u8);
let src_cast_place =
Place::from(local_decls.push(LocalDecl::new(src_cast_ty, span)));
let src_cast = Statement {
source_info,
kind: StatementKind::Assign(box (
src_cast_place,
Rvalue::Cast(CastKind::Misc, Operand::Copy(src), src_cast_ty),
)),
};
let copy_bytes = Statement {
source_info,
kind: StatementKind::CopyNonOverlapping(box CopyNonOverlapping {
src: Operand::Copy(src_cast_place),
dst: Operand::Copy(dst_cast_place),
count: Operand::Copy(size_place),
}),
};
let store_dead = Statement {
source_info,
kind: StatementKind::StorageDead(size_array_local),
};
let iter = [
store_live,
const_assign,
store_discr,
cast_discr,
store_size,
dst_ptr,
dst_cast,
src_ptr,
src_cast,
copy_bytes,
store_dead,
]
.into_iter();
st.make_nop();
Some(iter)
} else {
None
}
});
}
}
}

View File

@ -3,7 +3,6 @@
#![feature(drain_filter)]
#![feature(let_chains)]
#![feature(let_else)]
#![feature(entry_insert)]
#![feature(map_try_insert)]
#![feature(min_specialization)]
#![feature(never_type)]
@ -75,6 +74,7 @@ mod function_item_references;
mod generator;
mod inline;
mod instcombine;
mod large_enums;
mod lower_intrinsics;
mod lower_slice_len;
mod match_branches;
@ -547,6 +547,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
tcx,
body,
&[
&large_enums::EnumSizeOpt { discrepancy: 128 },
&reveal_all::RevealAll, // has to be done before inlining, since inlined code is in RevealAll mode.
&lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first
&unreachable_prop::UnreachablePropagation,

View File

@ -1,4 +1,4 @@
// compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0
// compile-flags: -C no-prepopulate-passes
// min-llvm-version: 14.0
#![crate_type = "lib"]

View File

@ -1,4 +1,4 @@
// compile-flags: -O -C no-prepopulate-passes -Zmir-opt-level=0
// compile-flags: -O -C no-prepopulate-passes
#![crate_type = "lib"]

View File

@ -0,0 +1,55 @@
- // MIR for `cand` before EnumSizeOpt
+ // MIR for `cand` after EnumSizeOpt
fn cand() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum_opt.rs:49:15: 49:15
let mut _1: Candidate; // in scope 0 at $DIR/enum_opt.rs:50:7: 50:12
let mut _2: Candidate; // in scope 0 at $DIR/enum_opt.rs:51:7: 51:34
let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:51:24: 51:33
+ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _8: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _10: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
scope 1 {
debug a => _1; // in scope 1 at $DIR/enum_opt.rs:50:7: 50:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:50:7: 50:12
Deinit(_1); // scope 0 at $DIR/enum_opt.rs:50:15: 50:34
((_1 as Small).0: u8) = const 1_u8; // scope 0 at $DIR/enum_opt.rs:50:15: 50:34
discriminant(_1) = 0; // scope 0 at $DIR/enum_opt.rs:50:15: 50:34
StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:51:7: 51:34
StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:51:24: 51:33
_3 = [const 1_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:51:24: 51:33
Deinit(_2); // scope 1 at $DIR/enum_opt.rs:51:7: 51:34
((_2 as Large).0: [u64; 1024]) = move _3; // scope 1 at $DIR/enum_opt.rs:51:7: 51:34
discriminant(_2) = 1; // scope 1 at $DIR/enum_opt.rs:51:7: 51:34
StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:51:33: 51:34
- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _4 = const [2_usize, 8196_usize]; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _6 = _5 as usize (Misc); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _9 = _8 as *mut u8 (Misc); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _11 = _10 as *const u8 (Misc); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ copy_nonoverlapping(src=_11, dst=_9, count=_7); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:51:33: 51:34
_0 = const (); // scope 0 at $DIR/enum_opt.rs:49:15: 52:2
StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:52:1: 52:2
return; // scope 0 at $DIR/enum_opt.rs:52:2: 52:2
}
bb1 (cleanup): {
resume; // scope 0 at $DIR/enum_opt.rs:49:1: 52:2
}
}

View File

@ -0,0 +1,55 @@
- // MIR for `cand` before EnumSizeOpt
+ // MIR for `cand` after EnumSizeOpt
fn cand() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum_opt.rs:49:15: 49:15
let mut _1: Candidate; // in scope 0 at $DIR/enum_opt.rs:50:7: 50:12
let mut _2: Candidate; // in scope 0 at $DIR/enum_opt.rs:51:7: 51:34
let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:51:24: 51:33
+ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _8: *mut Candidate; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _10: *const Candidate; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
+ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:51:3: 51:34
scope 1 {
debug a => _1; // in scope 1 at $DIR/enum_opt.rs:50:7: 50:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:50:7: 50:12
Deinit(_1); // scope 0 at $DIR/enum_opt.rs:50:15: 50:34
((_1 as Small).0: u8) = const 1_u8; // scope 0 at $DIR/enum_opt.rs:50:15: 50:34
discriminant(_1) = 0; // scope 0 at $DIR/enum_opt.rs:50:15: 50:34
StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:51:7: 51:34
StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:51:24: 51:33
_3 = [const 1_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:51:24: 51:33
Deinit(_2); // scope 1 at $DIR/enum_opt.rs:51:7: 51:34
((_2 as Large).0: [u64; 1024]) = move _3; // scope 1 at $DIR/enum_opt.rs:51:7: 51:34
discriminant(_2) = 1; // scope 1 at $DIR/enum_opt.rs:51:7: 51:34
StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:51:33: 51:34
- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _4 = const [2_usize, 8200_usize]; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _6 = _5 as usize (Misc); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _9 = _8 as *mut u8 (Misc); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ _11 = _10 as *const u8 (Misc); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ copy_nonoverlapping(src=_11, dst=_9, count=_7); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
+ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:51:3: 51:34
StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:51:33: 51:34
_0 = const (); // scope 0 at $DIR/enum_opt.rs:49:15: 52:2
StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:52:1: 52:2
return; // scope 0 at $DIR/enum_opt.rs:52:2: 52:2
}
bb1 (cleanup): {
resume; // scope 0 at $DIR/enum_opt.rs:49:1: 52:2
}
}

View File

@ -0,0 +1,35 @@
- // MIR for `invalid` before EnumSizeOpt
+ // MIR for `invalid` after EnumSizeOpt
fn invalid() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum_opt.rs:55:18: 55:18
let mut _1: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:56:7: 56:12
let mut _2: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:57:7: 57:36
let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:57:26: 57:35
scope 1 {
debug a => _1; // in scope 1 at $DIR/enum_opt.rs:56:7: 56:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:56:7: 56:12
Deinit(_1); // scope 0 at $DIR/enum_opt.rs:56:15: 56:29
discriminant(_1) = 0; // scope 0 at $DIR/enum_opt.rs:56:15: 56:29
StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:57:7: 57:36
StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:57:26: 57:35
_3 = [const 0_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:57:26: 57:35
Deinit(_2); // scope 1 at $DIR/enum_opt.rs:57:7: 57:36
((_2 as Large).0: [u64; 1024]) = move _3; // scope 1 at $DIR/enum_opt.rs:57:7: 57:36
discriminant(_2) = 1; // scope 1 at $DIR/enum_opt.rs:57:7: 57:36
StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:57:35: 57:36
_1 = move _2; // scope 1 at $DIR/enum_opt.rs:57:3: 57:36
StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:57:35: 57:36
_0 = const (); // scope 0 at $DIR/enum_opt.rs:55:18: 58:2
StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:58:1: 58:2
return; // scope 0 at $DIR/enum_opt.rs:58:2: 58:2
}
bb1 (cleanup): {
resume; // scope 0 at $DIR/enum_opt.rs:55:1: 58:2
}
}

View File

@ -0,0 +1,35 @@
- // MIR for `invalid` before EnumSizeOpt
+ // MIR for `invalid` after EnumSizeOpt
fn invalid() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum_opt.rs:55:18: 55:18
let mut _1: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:56:7: 56:12
let mut _2: InvalidIdxs; // in scope 0 at $DIR/enum_opt.rs:57:7: 57:36
let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:57:26: 57:35
scope 1 {
debug a => _1; // in scope 1 at $DIR/enum_opt.rs:56:7: 56:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:56:7: 56:12
Deinit(_1); // scope 0 at $DIR/enum_opt.rs:56:15: 56:29
discriminant(_1) = 0; // scope 0 at $DIR/enum_opt.rs:56:15: 56:29
StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:57:7: 57:36
StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:57:26: 57:35
_3 = [const 0_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:57:26: 57:35
Deinit(_2); // scope 1 at $DIR/enum_opt.rs:57:7: 57:36
((_2 as Large).0: [u64; 1024]) = move _3; // scope 1 at $DIR/enum_opt.rs:57:7: 57:36
discriminant(_2) = 1; // scope 1 at $DIR/enum_opt.rs:57:7: 57:36
StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:57:35: 57:36
_1 = move _2; // scope 1 at $DIR/enum_opt.rs:57:3: 57:36
StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:57:35: 57:36
_0 = const (); // scope 0 at $DIR/enum_opt.rs:55:18: 58:2
StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:58:1: 58:2
return; // scope 0 at $DIR/enum_opt.rs:58:2: 58:2
}
bb1 (cleanup): {
resume; // scope 0 at $DIR/enum_opt.rs:55:1: 58:2
}
}

79
tests/mir-opt/enum_opt.rs Normal file
View File

@ -0,0 +1,79 @@
// EMIT_MIR_FOR_EACH_BIT_WIDTH
// compile-flags: -Zunsound-mir-opts -Zmir-opt-level=3
#![feature(arbitrary_enum_discriminant, repr128)]
// Tests that an enum with a variant with no data gets correctly transformed.
pub enum NoData {
None,
Large([u64; 1024]),
}
// Tests that an enum with a variant with data that is a valid candidate gets transformed.
pub enum Candidate {
Small(u8),
Large([u64; 1024]),
}
// Tests that an enum which has a discriminant much higher than the variant does not get
// tformed.
#[repr(u32)]
pub enum InvalidIdxs {
A = 302,
Large([u64; 1024]),
}
// Tests that an enum with too high of a discriminant index (not in bounds of usize) does not
// get tformed.
#[repr(u128)]
pub enum Truncatable {
A = 0,
B([u8; 1024]) = 1,
C([u8; 4096]) = 0x10000000000000001,
}
// Tests that an enum with discriminants in random order still gets tformed correctly.
#[repr(u32)]
pub enum RandOrderDiscr {
A = 13,
B([u8; 1024]) = 5,
C = 7,
}
// EMIT_MIR enum_opt.unin.EnumSizeOpt.diff
pub fn unin() {
let mut a = NoData::None;
a = NoData::Large([1; 1024]);
}
// EMIT_MIR enum_opt.cand.EnumSizeOpt.diff
pub fn cand() {
let mut a = Candidate::Small(1);
a = Candidate::Large([1; 1024]);
}
// EMIT_MIR enum_opt.invalid.EnumSizeOpt.diff
pub fn invalid() {
let mut a = InvalidIdxs::A;
a = InvalidIdxs::Large([0; 1024]);
}
// EMIT_MIR enum_opt.trunc.EnumSizeOpt.diff
pub fn trunc() {
let mut a = Truncatable::A;
a = Truncatable::B([0; 1024]);
a = Truncatable::C([0; 4096]);
}
pub fn rand_order() {
let mut a = RandOrderDiscr::A;
a = RandOrderDiscr::B([0; 1024]);
a = RandOrderDiscr::C;
}
pub fn main() {
unin();
cand();
invalid();
trunc();
rand_order();
}

View File

@ -0,0 +1,46 @@
- // MIR for `trunc` before EnumSizeOpt
+ // MIR for `trunc` after EnumSizeOpt
fn trunc() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum_opt.rs:61:16: 61:16
let mut _1: Truncatable; // in scope 0 at $DIR/enum_opt.rs:62:7: 62:12
let mut _2: Truncatable; // in scope 0 at $DIR/enum_opt.rs:63:7: 63:32
let mut _3: [u8; 1024]; // in scope 0 at $DIR/enum_opt.rs:63:22: 63:31
let mut _4: Truncatable; // in scope 0 at $DIR/enum_opt.rs:64:7: 64:32
let mut _5: [u8; 4096]; // in scope 0 at $DIR/enum_opt.rs:64:22: 64:31
scope 1 {
debug a => _1; // in scope 1 at $DIR/enum_opt.rs:62:7: 62:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:62:7: 62:12
Deinit(_1); // scope 0 at $DIR/enum_opt.rs:62:15: 62:29
discriminant(_1) = 0; // scope 0 at $DIR/enum_opt.rs:62:15: 62:29
StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:63:7: 63:32
StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:63:22: 63:31
_3 = [const 0_u8; 1024]; // scope 1 at $DIR/enum_opt.rs:63:22: 63:31
Deinit(_2); // scope 1 at $DIR/enum_opt.rs:63:7: 63:32
((_2 as B).0: [u8; 1024]) = move _3; // scope 1 at $DIR/enum_opt.rs:63:7: 63:32
discriminant(_2) = 1; // scope 1 at $DIR/enum_opt.rs:63:7: 63:32
StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:63:31: 63:32
_1 = move _2; // scope 1 at $DIR/enum_opt.rs:63:3: 63:32
StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:63:31: 63:32
StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:64:7: 64:32
StorageLive(_5); // scope 1 at $DIR/enum_opt.rs:64:22: 64:31
_5 = [const 0_u8; 4096]; // scope 1 at $DIR/enum_opt.rs:64:22: 64:31
Deinit(_4); // scope 1 at $DIR/enum_opt.rs:64:7: 64:32
((_4 as C).0: [u8; 4096]) = move _5; // scope 1 at $DIR/enum_opt.rs:64:7: 64:32
discriminant(_4) = 2; // scope 1 at $DIR/enum_opt.rs:64:7: 64:32
StorageDead(_5); // scope 1 at $DIR/enum_opt.rs:64:31: 64:32
_1 = move _4; // scope 1 at $DIR/enum_opt.rs:64:3: 64:32
StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:64:31: 64:32
_0 = const (); // scope 0 at $DIR/enum_opt.rs:61:16: 65:2
StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:65:1: 65:2
return; // scope 0 at $DIR/enum_opt.rs:65:2: 65:2
}
bb1 (cleanup): {
resume; // scope 0 at $DIR/enum_opt.rs:61:1: 65:2
}
}

View File

@ -0,0 +1,46 @@
- // MIR for `trunc` before EnumSizeOpt
+ // MIR for `trunc` after EnumSizeOpt
fn trunc() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum_opt.rs:61:16: 61:16
let mut _1: Truncatable; // in scope 0 at $DIR/enum_opt.rs:62:7: 62:12
let mut _2: Truncatable; // in scope 0 at $DIR/enum_opt.rs:63:7: 63:32
let mut _3: [u8; 1024]; // in scope 0 at $DIR/enum_opt.rs:63:22: 63:31
let mut _4: Truncatable; // in scope 0 at $DIR/enum_opt.rs:64:7: 64:32
let mut _5: [u8; 4096]; // in scope 0 at $DIR/enum_opt.rs:64:22: 64:31
scope 1 {
debug a => _1; // in scope 1 at $DIR/enum_opt.rs:62:7: 62:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:62:7: 62:12
Deinit(_1); // scope 0 at $DIR/enum_opt.rs:62:15: 62:29
discriminant(_1) = 0; // scope 0 at $DIR/enum_opt.rs:62:15: 62:29
StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:63:7: 63:32
StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:63:22: 63:31
_3 = [const 0_u8; 1024]; // scope 1 at $DIR/enum_opt.rs:63:22: 63:31
Deinit(_2); // scope 1 at $DIR/enum_opt.rs:63:7: 63:32
((_2 as B).0: [u8; 1024]) = move _3; // scope 1 at $DIR/enum_opt.rs:63:7: 63:32
discriminant(_2) = 1; // scope 1 at $DIR/enum_opt.rs:63:7: 63:32
StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:63:31: 63:32
_1 = move _2; // scope 1 at $DIR/enum_opt.rs:63:3: 63:32
StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:63:31: 63:32
StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:64:7: 64:32
StorageLive(_5); // scope 1 at $DIR/enum_opt.rs:64:22: 64:31
_5 = [const 0_u8; 4096]; // scope 1 at $DIR/enum_opt.rs:64:22: 64:31
Deinit(_4); // scope 1 at $DIR/enum_opt.rs:64:7: 64:32
((_4 as C).0: [u8; 4096]) = move _5; // scope 1 at $DIR/enum_opt.rs:64:7: 64:32
discriminant(_4) = 2; // scope 1 at $DIR/enum_opt.rs:64:7: 64:32
StorageDead(_5); // scope 1 at $DIR/enum_opt.rs:64:31: 64:32
_1 = move _4; // scope 1 at $DIR/enum_opt.rs:64:3: 64:32
StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:64:31: 64:32
_0 = const (); // scope 0 at $DIR/enum_opt.rs:61:16: 65:2
StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:65:1: 65:2
return; // scope 0 at $DIR/enum_opt.rs:65:2: 65:2
}
bb1 (cleanup): {
resume; // scope 0 at $DIR/enum_opt.rs:61:1: 65:2
}
}

View File

@ -0,0 +1,54 @@
- // MIR for `unin` before EnumSizeOpt
+ // MIR for `unin` after EnumSizeOpt
fn unin() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum_opt.rs:43:15: 43:15
let mut _1: NoData; // in scope 0 at $DIR/enum_opt.rs:44:7: 44:12
let mut _2: NoData; // in scope 0 at $DIR/enum_opt.rs:45:7: 45:31
let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:45:21: 45:30
+ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _8: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _10: *const NoData; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
scope 1 {
debug a => _1; // in scope 1 at $DIR/enum_opt.rs:44:7: 44:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:44:7: 44:12
Deinit(_1); // scope 0 at $DIR/enum_opt.rs:44:15: 44:27
discriminant(_1) = 0; // scope 0 at $DIR/enum_opt.rs:44:15: 44:27
StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:45:7: 45:31
StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:45:21: 45:30
_3 = [const 1_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:45:21: 45:30
Deinit(_2); // scope 1 at $DIR/enum_opt.rs:45:7: 45:31
((_2 as Large).0: [u64; 1024]) = move _3; // scope 1 at $DIR/enum_opt.rs:45:7: 45:31
discriminant(_2) = 1; // scope 1 at $DIR/enum_opt.rs:45:7: 45:31
StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:45:30: 45:31
- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _4 = const [4_usize, 8196_usize]; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _6 = _5 as usize (Misc); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _9 = _8 as *mut u8 (Misc); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _11 = _10 as *const u8 (Misc); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ copy_nonoverlapping(src=_11, dst=_9, count=_7); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:45:30: 45:31
_0 = const (); // scope 0 at $DIR/enum_opt.rs:43:15: 46:2
StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:46:1: 46:2
return; // scope 0 at $DIR/enum_opt.rs:46:2: 46:2
}
bb1 (cleanup): {
resume; // scope 0 at $DIR/enum_opt.rs:43:1: 46:2
}
}

View File

@ -0,0 +1,54 @@
- // MIR for `unin` before EnumSizeOpt
+ // MIR for `unin` after EnumSizeOpt
fn unin() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum_opt.rs:43:15: 43:15
let mut _1: NoData; // in scope 0 at $DIR/enum_opt.rs:44:7: 44:12
let mut _2: NoData; // in scope 0 at $DIR/enum_opt.rs:45:7: 45:31
let mut _3: [u64; 1024]; // in scope 0 at $DIR/enum_opt.rs:45:21: 45:30
+ let mut _4: [usize; 2]; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _5: isize; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _6: usize; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _7: usize; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _8: *mut NoData; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _9: *mut u8; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _10: *const NoData; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
+ let mut _11: *const u8; // in scope 0 at $DIR/enum_opt.rs:45:3: 45:31
scope 1 {
debug a => _1; // in scope 1 at $DIR/enum_opt.rs:44:7: 44:12
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum_opt.rs:44:7: 44:12
Deinit(_1); // scope 0 at $DIR/enum_opt.rs:44:15: 44:27
discriminant(_1) = 0; // scope 0 at $DIR/enum_opt.rs:44:15: 44:27
StorageLive(_2); // scope 1 at $DIR/enum_opt.rs:45:7: 45:31
StorageLive(_3); // scope 1 at $DIR/enum_opt.rs:45:21: 45:30
_3 = [const 1_u64; 1024]; // scope 1 at $DIR/enum_opt.rs:45:21: 45:30
Deinit(_2); // scope 1 at $DIR/enum_opt.rs:45:7: 45:31
((_2 as Large).0: [u64; 1024]) = move _3; // scope 1 at $DIR/enum_opt.rs:45:7: 45:31
discriminant(_2) = 1; // scope 1 at $DIR/enum_opt.rs:45:7: 45:31
StorageDead(_3); // scope 1 at $DIR/enum_opt.rs:45:30: 45:31
- _1 = move _2; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ StorageLive(_4); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _4 = const [8_usize, 8200_usize]; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _5 = discriminant(_2); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _6 = _5 as usize (Misc); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _7 = _4[_6]; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _8 = &raw mut _1; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _9 = _8 as *mut u8 (Misc); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _10 = &raw const _2; // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ _11 = _10 as *const u8 (Misc); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ copy_nonoverlapping(src=_11, dst=_9, count=_7); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
+ StorageDead(_4); // scope 1 at $DIR/enum_opt.rs:45:3: 45:31
StorageDead(_2); // scope 1 at $DIR/enum_opt.rs:45:30: 45:31
_0 = const (); // scope 0 at $DIR/enum_opt.rs:43:15: 46:2
StorageDead(_1); // scope 0 at $DIR/enum_opt.rs:46:1: 46:2
return; // scope 0 at $DIR/enum_opt.rs:46:2: 46:2
}
bb1 (cleanup): {
resume; // scope 0 at $DIR/enum_opt.rs:43:1: 46:2
}
}