@@ -86,6 +86,12 @@ pub enum OperandValue<V> {
8686 /// `is_zst` on its `Layout` returns `true`. Note however that
8787 /// these values can still require alignment.
8888 ZeroSized ,
89+ /// A value for which all bytes are entirely uninitialized.
90+ ///
91+ /// Storing this value is a no-op; it propagates through field extraction.
92+ /// Used to avoid emitting memcpys from uninit globals (which LLVM may
93+ /// otherwise materialize as zero-fills) for `const <uninit>` operands.
94+ Uninit ,
8995}
9096
9197impl < V : CodegenObject > OperandValue < V > {
@@ -95,7 +101,7 @@ impl<V: CodegenObject> OperandValue<V> {
95101 match self {
96102 OperandValue :: Immediate ( llptr) => Some ( ( llptr, None ) ) ,
97103 OperandValue :: Pair ( llptr, llextra) => Some ( ( llptr, Some ( llextra) ) ) ,
98- OperandValue :: Ref ( _) | OperandValue :: ZeroSized => None ,
104+ OperandValue :: Ref ( _) | OperandValue :: ZeroSized | OperandValue :: Uninit => None ,
99105 }
100106 }
101107
@@ -123,6 +129,7 @@ impl<V: CodegenObject> OperandValue<V> {
123129 #[ must_use]
124130 pub ( crate ) fn is_expected_variant_for_type < ' tcx > ( & self , ty : TyAndLayout < ' tcx > ) -> bool {
125131 match ( self , ty. backend_repr ) {
132+ ( OperandValue :: Uninit , _) => true ,
126133 ( OperandValue :: ZeroSized , BackendRepr :: Memory { .. } ) => ty. is_zst ( ) ,
127134 ( OperandValue :: Ref ( _) , BackendRepr :: Memory { .. } ) => !ty. is_zst ( ) ,
128135 (
@@ -397,7 +404,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
397404 ) ;
398405 }
399406
400- let val = if field. is_zst ( ) {
407+ let val = if let OperandValue :: Uninit = self . val {
408+ OperandValue :: Uninit
409+ } else if field. is_zst ( ) {
401410 OperandValue :: ZeroSized
402411 } else if field. size == self . layout . size {
403412 assert_eq ! ( offset. bytes( ) , 0 ) ;
@@ -496,6 +505,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
496505 // Read the tag/niche-encoded discriminant from memory.
497506 let tag_op = match self . val {
498507 OperandValue :: ZeroSized => bug ! ( ) ,
508+ OperandValue :: Uninit => return bx. cx ( ) . const_poison ( cast_to) ,
499509 OperandValue :: Immediate ( _) | OperandValue :: Pair ( _, _) => {
500510 self . extract_field ( fx, bx, tag_field. as_usize ( ) )
501511 }
@@ -778,12 +788,15 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> {
778788 field : FieldIdx ,
779789 field_operand : OperandRef < ' tcx , V > ,
780790 ) {
781- if let OperandValue :: ZeroSized = field_operand . val {
791+ if matches ! ( field_operand . val , OperandValue :: ZeroSized | OperandValue :: Uninit ) {
782792 // A ZST never adds any state, so just ignore it.
783793 // This special-casing is worth it because of things like
784794 // `Result<!, !>` where `Ok(never)` is legal to write,
785795 // but the type shows as FieldShape::Primitive so we can't
786796 // actually look at the layout for the field being set.
797+ //
798+ // Likewise, an uninit field does not contribute any value;
799+ // the builder's unset slots will produce `const_undef` in `build()`.
787800 return ;
788801 }
789802
@@ -1019,6 +1032,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
10191032 // Avoid generating stores of zero-sized values, because the only way to have a
10201033 // zero-sized value is through `undef`/`poison`, and the store itself is useless.
10211034 }
1035+ OperandValue :: Uninit => {
1036+ // Storing an entirely uninit value is a no-op: the destination is left
1037+ // uninitialized, which is valid since the value itself is uninit.
1038+ }
10221039 OperandValue :: Ref ( val) => {
10231040 assert ! ( dest. layout. is_sized( ) , "cannot directly store unsized values" ) ;
10241041 if val. llextra . is_some ( ) {
0 commit comments