|
1 | 1 | use std::{fmt, iter}; |
2 | 2 |
|
3 | 3 | use rustc_abi::{ |
4 | | - AddressSpace, Align, BackendRepr, CanonAbi, ExternAbi, HasDataLayout, Primitive, Reg, RegKind, |
5 | | - Scalar, Size, TyAbiInterface, TyAndLayout, |
| 4 | + AddressSpace, Align, BackendRepr, CanonAbi, ExternAbi, FieldsShape, HasDataLayout, Primitive, |
| 5 | + Reg, RegKind, Scalar, Size, TyAbiInterface, TyAndLayout, Variants, |
6 | 6 | }; |
7 | 7 | use rustc_macros::HashStable_Generic; |
8 | 8 |
|
@@ -514,6 +514,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> { |
514 | 514 | self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false }; |
515 | 515 | } |
516 | 516 |
|
| 517 | + pub fn cast_to_with_attrs<T: Into<CastTarget>>(&mut self, target: T, attrs: ArgAttributes) { |
| 518 | + self.mode = |
| 519 | + PassMode::Cast { cast: Box::new(target.into().with_attrs(attrs)), pad_i32: false }; |
| 520 | + } |
| 521 | + |
517 | 522 | pub fn cast_to_and_pad_i32<T: Into<CastTarget>>(&mut self, target: T, pad_i32: bool) { |
518 | 523 | self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 }; |
519 | 524 | } |
@@ -801,7 +806,12 @@ impl<'a, Ty> FnAbi<'a, Ty> { |
801 | 806 | // We want to pass small aggregates as immediates, but using |
802 | 807 | // an LLVM aggregate type for this leads to bad optimizations, |
803 | 808 | // so we pick an appropriately sized integer type instead. |
804 | | - arg.cast_to(Reg { kind: RegKind::Integer, size }); |
| 809 | + let attr = if layout_is_noundef(arg.layout, cx) { |
| 810 | + ArgAttribute::NoUndef |
| 811 | + } else { |
| 812 | + ArgAttribute::default() |
| 813 | + }; |
| 814 | + arg.cast_to_with_attrs(Reg { kind: RegKind::Integer, size }, attr.into()); |
805 | 815 | } |
806 | 816 | } |
807 | 817 |
|
@@ -836,6 +846,66 @@ impl<'a, Ty> FnAbi<'a, Ty> { |
836 | 846 | } |
837 | 847 | } |
838 | 848 |
|
| 849 | +/// Determines whether `layout` contains no uninit bytes (no padding, no unions), |
| 850 | +/// using only the computed layout. |
| 851 | +/// |
| 852 | +/// Conservative: returns `false` for anything it cannot prove fully initialized, |
| 853 | +/// including multi-variant enums and SIMD vectors. |
| 854 | +// FIXME: extend to multi-variant enums (per-variant padding analysis needed). |
| 855 | +fn layout_is_noundef<'a, Ty, C>(layout: TyAndLayout<'a, Ty>, cx: &C) -> bool |
| 856 | +where |
| 857 | + Ty: TyAbiInterface<'a, C> + Copy, |
| 858 | + C: HasDataLayout, |
| 859 | +{ |
| 860 | + match layout.backend_repr { |
| 861 | + BackendRepr::Scalar(scalar) => !scalar.is_uninit_valid(), |
| 862 | + BackendRepr::ScalarPair(s1, s2) => { |
| 863 | + !s1.is_uninit_valid() |
| 864 | + && !s2.is_uninit_valid() |
| 865 | + // Ensure there is no padding. |
| 866 | + && s1.size(cx) + s2.size(cx) == layout.size |
| 867 | + } |
| 868 | + BackendRepr::Memory { .. } => match layout.fields { |
| 869 | + FieldsShape::Primitive | FieldsShape::Union(_) => false, |
| 870 | + // Array elements are at stride offsets with no inter-element gaps. |
| 871 | + FieldsShape::Array { stride: _, count } => { |
| 872 | + count == 0 || layout_is_noundef(layout.field(cx, 0), cx) |
| 873 | + } |
| 874 | + FieldsShape::Arbitrary { .. } => { |
| 875 | + // With `Variants::Multiple`, `layout.fields` only covers shared |
| 876 | + // bytes (niche/discriminant); per-variant data is absent, so |
| 877 | + // full coverage cannot be proven. |
| 878 | + matches!(layout.variants, Variants::Single { .. }) && fields_are_noundef(layout, cx) |
| 879 | + } |
| 880 | + }, |
| 881 | + BackendRepr::SimdVector { .. } | BackendRepr::ScalableVector { .. } => false, |
| 882 | + } |
| 883 | +} |
| 884 | + |
| 885 | +/// Returns `true` if the fields of `layout` contiguously cover bytes `0..layout.size` |
| 886 | +/// with no padding gaps and each field is recursively `layout_is_noundef`. |
| 887 | +fn fields_are_noundef<'a, Ty, C>(layout: TyAndLayout<'a, Ty>, cx: &C) -> bool |
| 888 | +where |
| 889 | + Ty: TyAbiInterface<'a, C> + Copy, |
| 890 | + C: HasDataLayout, |
| 891 | +{ |
| 892 | + let mut cursor = Size::ZERO; |
| 893 | + for i in layout.fields.index_by_increasing_offset() { |
| 894 | + let field = layout.field(cx, i); |
| 895 | + if field.size == Size::ZERO { |
| 896 | + continue; |
| 897 | + } |
| 898 | + if layout.fields.offset(i) != cursor { |
| 899 | + return false; |
| 900 | + } |
| 901 | + if !layout_is_noundef(field, cx) { |
| 902 | + return false; |
| 903 | + } |
| 904 | + cursor += field.size; |
| 905 | + } |
| 906 | + cursor == layout.size |
| 907 | +} |
| 908 | + |
839 | 909 | // Some types are used a lot. Make sure they don't unintentionally get bigger. |
840 | 910 | #[cfg(target_pointer_width = "64")] |
841 | 911 | mod size_asserts { |
|
0 commit comments