diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index bf6aa800d20f4..bce8024399701 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -178,6 +178,8 @@ enum Value<'tcx> { /// An aggregate value, either tuple/closure/struct/enum. /// This does not contain unions, as we cannot reason with the value. Aggregate(VariantIdx, Vec), + /// 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. @@ -431,6 +433,21 @@ impl<'body, 'tcx> VnState<'body, '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()?; @@ -608,6 +625,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { ProjectionElem::Field(f, _) => { if let Value::Aggregate(_, fields) = self.get(value) { return Some((projection_ty, fields[f.as_usize()])); + } else if let Value::Union(active, field) = *self.get(value) + && active == f + { + return Some((projection_ty, field)); } else if let Value::Projection(outer_value, ProjectionElem::Downcast(_, read_variant)) = self.get(value) && 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 @@ -980,7 +1001,10 @@ impl<'body, 'tcx> VnState<'body, '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(); diff --git a/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff b/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff index 5e843da867923..a4900a1ac72ea 100644 --- a/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff +++ b/tests/mir-opt/const_prop/invalid_constant.main.GVN.diff @@ -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 │ .... } diff --git a/tests/mir-opt/const_prop/transmute.rs b/tests/mir-opt/const_prop/transmute.rs index 33cbefbf053f5..ece6331ade279 100644 --- a/tests/mir-opt/const_prop/transmute.rs +++ b/tests/mir-opt/const_prop/transmute.rs @@ -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: (), diff --git a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff index 2ac9769a0e773..be0450114b1c8 100644 --- a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff +++ b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.32bit.diff @@ -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) { ++ __ __ __ __ │ ░░░░ } diff --git a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff index 2ac9769a0e773..be0450114b1c8 100644 --- a/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff +++ b/tests/mir-opt/const_prop/transmute.undef_union_as_integer.GVN.64bit.diff @@ -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) { ++ __ __ __ __ │ ░░░░ } diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff index ef418798faaf7..f84436fbd2d56 100644 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff +++ b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-abort.diff @@ -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::) { - debug _x => _2; + debug _x => const 1_u32; } } scope 2 (inlined val) { @@ -16,12 +16,16 @@ bb0: { StorageLive(_1); - _1 = Un { us: const 1_u32 }; + nop; StorageLive(_2); - _2 = copy (_1.0: u32); + nop; StorageDead(_2); StorageDead(_1); return; } } + ALLOC0 (size: 4, align: 4) { + 01 00 00 00 │ .... + } + diff --git a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff index ef418798faaf7..f84436fbd2d56 100644 --- a/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff +++ b/tests/mir-opt/dest-prop/union.main.DestinationPropagation.panic-unwind.diff @@ -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::) { - debug _x => _2; + debug _x => const 1_u32; } } scope 2 (inlined val) { @@ -16,12 +16,16 @@ bb0: { StorageLive(_1); - _1 = Un { us: const 1_u32 }; + nop; StorageLive(_2); - _2 = copy (_1.0: u32); + nop; StorageDead(_2); StorageDead(_1); return; } } + ALLOC0 (size: 4, align: 4) { + 01 00 00 00 │ .... + } + diff --git a/tests/mir-opt/dest-prop/union.rs b/tests/mir-opt/dest-prop/union.rs index 85eded0998031..977ab9c57ad7e 100644 --- a/tests/mir-opt/dest-prop/union.rs +++ b/tests/mir-opt/dest-prop/union.rs @@ -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, }