GVN: Support unions.
This commit is contained in:
parent
3d8c1c1fc0
commit
5407eb8ca2
8 changed files with 85 additions and 30 deletions
|
|
@ -213,6 +213,8 @@ enum Value<'a, 'tcx> {
|
|||
/// An aggregate value, either tuple/closure/struct/enum.
|
||||
/// This does not contain unions, as we cannot reason with the value.
|
||||
Aggregate(VariantIdx, &'a [VnIndex]),
|
||||
/// A union aggregate value.
|
||||
Union(FieldIdx, VnIndex),
|
||||
/// A raw pointer aggregate built from a thin pointer and metadata.
|
||||
RawPtr {
|
||||
/// Thin pointer component. This is field 0 in MIR.
|
||||
|
|
@ -600,6 +602,21 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
return None;
|
||||
}
|
||||
}
|
||||
Union(active_field, field) => {
|
||||
let field = self.evaluated[field].as_ref()?;
|
||||
if matches!(ty.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..))
|
||||
{
|
||||
let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?;
|
||||
let field_dest = self.ecx.project_field(&dest, active_field).discard_err()?;
|
||||
self.ecx.copy_op(field, &field_dest).discard_err()?;
|
||||
self.ecx
|
||||
.alloc_mark_immutable(dest.ptr().provenance.unwrap().alloc_id())
|
||||
.discard_err()?;
|
||||
dest.into()
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
RawPtr { pointer, metadata } => {
|
||||
let pointer = self.evaluated[pointer].as_ref()?;
|
||||
let metadata = self.evaluated[metadata].as_ref()?;
|
||||
|
|
@ -802,11 +819,11 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index),
|
||||
ProjectionElem::Field(f, _) => {
|
||||
if let Value::Aggregate(_, fields) = self.get(value) {
|
||||
return Some((projection_ty, fields[f.as_usize()]));
|
||||
} else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value)
|
||||
&& let Value::Aggregate(written_variant, fields) = self.get(outer_value)
|
||||
ProjectionElem::Field(f, _) => match self.get(value) {
|
||||
Value::Aggregate(_, fields) => return Some((projection_ty, fields[f.as_usize()])),
|
||||
Value::Union(active, field) if active == f => return Some((projection_ty, field)),
|
||||
Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant))
|
||||
if let Value::Aggregate(written_variant, fields) = self.get(outer_value)
|
||||
// This pass is not aware of control-flow, so we do not know whether the
|
||||
// replacement we are doing is actually reachable. We could be in any arm of
|
||||
// ```
|
||||
|
|
@ -822,12 +839,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
// accessing the wrong variant is not UB if the enum has repr.
|
||||
// So it's not impossible for a series of MIR opts to generate
|
||||
// a downcast to an inactive variant.
|
||||
&& written_variant == read_variant
|
||||
&& written_variant == read_variant =>
|
||||
{
|
||||
return Some((projection_ty, fields[f.as_usize()]));
|
||||
}
|
||||
ProjectionElem::Field(f, ())
|
||||
}
|
||||
_ => ProjectionElem::Field(f, ()),
|
||||
},
|
||||
ProjectionElem::Index(idx) => {
|
||||
if let Value::Repeat(inner, _) = self.get(value) {
|
||||
return Some((projection_ty, inner));
|
||||
|
|
@ -1167,7 +1184,10 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
|
|||
| AggregateKind::Coroutine(..) => FIRST_VARIANT,
|
||||
AggregateKind::Adt(_, variant_index, _, _, None) => variant_index,
|
||||
// Do not track unions.
|
||||
AggregateKind::Adt(_, _, _, _, Some(_)) => return None,
|
||||
AggregateKind::Adt(_, _, _, _, Some(active_field)) => {
|
||||
let field = *fields.first()?;
|
||||
return Some(self.insert(ty, Value::Union(active_field, field)));
|
||||
}
|
||||
AggregateKind::RawPtr(..) => {
|
||||
assert_eq!(field_ops.len(), 2);
|
||||
let [mut pointer, metadata] = fields.try_into().unwrap();
|
||||
|
|
|
|||
|
|
@ -28,21 +28,27 @@
|
|||
bb0: {
|
||||
StorageLive(_1);
|
||||
StorageLive(_2);
|
||||
_2 = InvalidChar { int: const 1114113_u32 };
|
||||
_1 = copy (_2.1: char);
|
||||
- _2 = InvalidChar { int: const 1114113_u32 };
|
||||
- _1 = copy (_2.1: char);
|
||||
+ _2 = const InvalidChar {{ int: 1114113_u32, chr: {transmute(0x00110001): char} }};
|
||||
+ _1 = const {transmute(0x00110001): char};
|
||||
StorageDead(_2);
|
||||
StorageLive(_3);
|
||||
StorageLive(_4);
|
||||
StorageLive(_5);
|
||||
_5 = InvalidTag { int: const 4_u32 };
|
||||
_4 = copy (_5.1: E);
|
||||
_3 = [move _4];
|
||||
- _5 = InvalidTag { int: const 4_u32 };
|
||||
- _4 = copy (_5.1: E);
|
||||
- _3 = [move _4];
|
||||
+ _5 = const InvalidTag {{ int: 4_u32, e: Scalar(0x00000004): E }};
|
||||
+ _4 = const Scalar(0x00000004): E;
|
||||
+ _3 = [const Scalar(0x00000004): E];
|
||||
StorageDead(_4);
|
||||
StorageDead(_5);
|
||||
nop;
|
||||
nop;
|
||||
StorageLive(_8);
|
||||
_8 = NoVariants { int: const 0_u32 };
|
||||
- _8 = NoVariants { int: const 0_u32 };
|
||||
+ _8 = const NoVariants {{ int: 0_u32, empty: ZeroSized: Empty }};
|
||||
nop;
|
||||
nop;
|
||||
nop;
|
||||
|
|
@ -55,5 +61,17 @@
|
|||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ ALLOC0 (size: 4, align: 4) {
|
||||
+ 00 00 00 00 │ ....
|
||||
+ }
|
||||
+
|
||||
+ ALLOC1 (size: 4, align: 4) {
|
||||
+ 04 00 00 00 │ ....
|
||||
+ }
|
||||
+
|
||||
+ ALLOC2 (size: 4, align: 4) {
|
||||
+ 01 00 11 00 │ ....
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ pub unsafe fn invalid_bool() -> bool {
|
|||
// EMIT_MIR transmute.undef_union_as_integer.GVN.diff
|
||||
pub unsafe fn undef_union_as_integer() -> u32 {
|
||||
// CHECK-LABEL: fn undef_union_as_integer(
|
||||
// CHECK: _1 = Union32 {
|
||||
// CHECK: _0 = move _1 as u32 (Transmute);
|
||||
// CHECK: _1 = const Union32
|
||||
// CHECK: _0 = const {{.*}}: u32;
|
||||
union Union32 {
|
||||
value: u32,
|
||||
unit: (),
|
||||
|
|
|
|||
|
|
@ -12,11 +12,16 @@
|
|||
- _2 = ();
|
||||
- _1 = Union32 { value: move _2 };
|
||||
+ _2 = const ();
|
||||
+ _1 = Union32 { value: const () };
|
||||
+ _1 = const Union32 {{ value: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32, unit: () }};
|
||||
StorageDead(_2);
|
||||
_0 = move _1 as u32 (Transmute);
|
||||
- _0 = move _1 as u32 (Transmute);
|
||||
+ _0 = const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32;
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ ALLOC0 (size: 4, align: 4) {
|
||||
+ __ __ __ __ │ ░░░░
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,16 @@
|
|||
- _2 = ();
|
||||
- _1 = Union32 { value: move _2 };
|
||||
+ _2 = const ();
|
||||
+ _1 = Union32 { value: const () };
|
||||
+ _1 = const Union32 {{ value: Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32, unit: () }};
|
||||
StorageDead(_2);
|
||||
_0 = move _1 as u32 (Transmute);
|
||||
- _0 = move _1 as u32 (Transmute);
|
||||
+ _0 = const Indirect { alloc_id: ALLOC0, offset: Size(0 bytes) }: u32;
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
+ }
|
||||
+
|
||||
+ ALLOC0 (size: 4, align: 4) {
|
||||
+ __ __ __ __ │ ░░░░
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
let _1: main::Un;
|
||||
let mut _2: u32;
|
||||
scope 1 {
|
||||
debug un => _1;
|
||||
debug un => const Un {{ us: 1_u32 }};
|
||||
scope 3 (inlined std::mem::drop::<u32>) {
|
||||
debug _x => _2;
|
||||
debug _x => const 1_u32;
|
||||
}
|
||||
}
|
||||
scope 2 (inlined val) {
|
||||
|
|
@ -16,12 +16,14 @@
|
|||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
_1 = Un { us: const 1_u32 };
|
||||
StorageLive(_2);
|
||||
_2 = copy (_1.0: u32);
|
||||
StorageDead(_2);
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ALLOC0 (size: 4, align: 4) {
|
||||
01 00 00 00 │ ....
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@
|
|||
let _1: main::Un;
|
||||
let mut _2: u32;
|
||||
scope 1 {
|
||||
debug un => _1;
|
||||
debug un => const Un {{ us: 1_u32 }};
|
||||
scope 3 (inlined std::mem::drop::<u32>) {
|
||||
debug _x => _2;
|
||||
debug _x => const 1_u32;
|
||||
}
|
||||
}
|
||||
scope 2 (inlined val) {
|
||||
|
|
@ -16,12 +16,14 @@
|
|||
|
||||
bb0: {
|
||||
StorageLive(_1);
|
||||
_1 = Un { us: const 1_u32 };
|
||||
StorageLive(_2);
|
||||
_2 = copy (_1.0: u32);
|
||||
StorageDead(_2);
|
||||
StorageDead(_1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ALLOC0 (size: 4, align: 4) {
|
||||
01 00 00 00 │ ....
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ fn val() -> u32 {
|
|||
// EMIT_MIR union.main.DestinationPropagation.diff
|
||||
fn main() {
|
||||
// CHECK-LABEL: fn main(
|
||||
// CHECK: {{_.*}} = Un { us: const 1_u32 };
|
||||
// CHECK: debug un => const Un
|
||||
// CHECK: debug _x => const 1_u32;
|
||||
// CHECK: bb0: {
|
||||
// CHECK-NEXT: return;
|
||||
union Un {
|
||||
us: u32,
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue