about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs11
-rw-r--r--compiler/rustc_target/src/callconv/loongarch.rs137
-rw-r--r--library/core/src/num/int_macros.rs117
-rw-r--r--library/core/src/num/uint_macros.rs114
-rw-r--r--library/coretests/tests/floats/f128.rs23
-rw-r--r--library/coretests/tests/floats/f16.rs21
-rw-r--r--library/coretests/tests/floats/f32.rs23
-rw-r--r--library/coretests/tests/floats/f64.rs21
-rw-r--r--library/coretests/tests/floats/mod.rs39
-rw-r--r--library/std/src/sys/stdio/mod.rs10
-rw-r--r--library/std/src/sys/stdio/wasip1.rs (renamed from library/std/src/sys/stdio/wasi.rs)0
-rw-r--r--library/std/src/sys/stdio/wasip2.rs120
-rw-r--r--tests/assembly-llvm/loongarch-float-struct-abi.rs134
-rw-r--r--tests/codegen-llvm/cast-target-abi.rs52
-rw-r--r--tests/codegen-llvm/loongarch-abi/cast-local-large-enough.rs44
-rw-r--r--tests/ui/privacy/suggest-box-new.rs3
-rw-r--r--tests/ui/privacy/suggest-box-new.stderr32
-rw-r--r--tests/ui/privacy/suggest-new-projection-ice.rs19
-rw-r--r--tests/ui/privacy/suggest-new-projection-ice.stderr10
-rw-r--r--tests/ui/suggestions/multi-suggestion.ascii.stderr2
-rw-r--r--tests/ui/suggestions/multi-suggestion.unicode.stderr2
21 files changed, 798 insertions, 136 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index a652e08905a..7adbee7ff28 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -2585,12 +2585,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .filter(|item| item.is_fn() && !item.is_method())
                 .filter_map(|item| {
                     // Only assoc fns that return `Self`
-                    let fn_sig = self.tcx.fn_sig(item.def_id).skip_binder();
-                    let ret_ty = fn_sig.output();
-                    let ret_ty = self.tcx.normalize_erasing_late_bound_regions(
-                        self.typing_env(self.param_env),
-                        ret_ty,
-                    );
+                    let fn_sig = self
+                        .tcx
+                        .fn_sig(item.def_id)
+                        .instantiate(self.tcx, self.fresh_args_for_item(span, item.def_id));
+                    let ret_ty = self.tcx.instantiate_bound_regions_with_erased(fn_sig.output());
                     if !self.can_eq(self.param_env, ret_ty, adt_ty) {
                         return None;
                     }
diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs
index d567ad401bb..9213d73e24e 100644
--- a/compiler/rustc_target/src/callconv/loongarch.rs
+++ b/compiler/rustc_target/src/callconv/loongarch.rs
@@ -8,16 +8,16 @@ use crate::spec::HasTargetSpec;
 
 #[derive(Copy, Clone)]
 enum RegPassKind {
-    Float(Reg),
-    Integer(Reg),
+    Float { offset_from_start: Size, ty: Reg },
+    Integer { offset_from_start: Size, ty: Reg },
     Unknown,
 }
 
 #[derive(Copy, Clone)]
 enum FloatConv {
-    FloatPair(Reg, Reg),
+    FloatPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg },
     Float(Reg),
-    MixedPair(Reg, Reg),
+    MixedPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg },
 }
 
 #[derive(Copy, Clone)]
@@ -37,6 +37,7 @@ fn should_use_fp_conv_helper<'a, Ty, C>(
     flen: u64,
     field1_kind: &mut RegPassKind,
     field2_kind: &mut RegPassKind,
+    offset_from_start: Size,
 ) -> Result<(), CannotUseFpConv>
 where
     Ty: TyAbiInterface<'a, C> + Copy,
@@ -49,16 +50,16 @@ where
                 }
                 match (*field1_kind, *field2_kind) {
                     (RegPassKind::Unknown, _) => {
-                        *field1_kind = RegPassKind::Integer(Reg {
-                            kind: RegKind::Integer,
-                            size: arg_layout.size,
-                        });
+                        *field1_kind = RegPassKind::Integer {
+                            offset_from_start,
+                            ty: Reg { kind: RegKind::Integer, size: arg_layout.size },
+                        };
                     }
-                    (RegPassKind::Float(_), RegPassKind::Unknown) => {
-                        *field2_kind = RegPassKind::Integer(Reg {
-                            kind: RegKind::Integer,
-                            size: arg_layout.size,
-                        });
+                    (RegPassKind::Float { .. }, RegPassKind::Unknown) => {
+                        *field2_kind = RegPassKind::Integer {
+                            offset_from_start,
+                            ty: Reg { kind: RegKind::Integer, size: arg_layout.size },
+                        };
                     }
                     _ => return Err(CannotUseFpConv),
                 }
@@ -69,12 +70,16 @@ where
                 }
                 match (*field1_kind, *field2_kind) {
                     (RegPassKind::Unknown, _) => {
-                        *field1_kind =
-                            RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
+                        *field1_kind = RegPassKind::Float {
+                            offset_from_start,
+                            ty: Reg { kind: RegKind::Float, size: arg_layout.size },
+                        };
                     }
                     (_, RegPassKind::Unknown) => {
-                        *field2_kind =
-                            RegPassKind::Float(Reg { kind: RegKind::Float, size: arg_layout.size });
+                        *field2_kind = RegPassKind::Float {
+                            offset_from_start,
+                            ty: Reg { kind: RegKind::Float, size: arg_layout.size },
+                        };
                     }
                     _ => return Err(CannotUseFpConv),
                 }
@@ -96,13 +101,14 @@ where
                             flen,
                             field1_kind,
                             field2_kind,
+                            offset_from_start,
                         );
                     }
                     return Err(CannotUseFpConv);
                 }
             }
             FieldsShape::Array { count, .. } => {
-                for _ in 0..count {
+                for i in 0..count {
                     let elem_layout = arg_layout.field(cx, 0);
                     should_use_fp_conv_helper(
                         cx,
@@ -111,6 +117,7 @@ where
                         flen,
                         field1_kind,
                         field2_kind,
+                        offset_from_start + elem_layout.size * i,
                     )?;
                 }
             }
@@ -121,7 +128,15 @@ where
                 }
                 for i in arg_layout.fields.index_by_increasing_offset() {
                     let field = arg_layout.field(cx, i);
-                    should_use_fp_conv_helper(cx, &field, xlen, flen, field1_kind, field2_kind)?;
+                    should_use_fp_conv_helper(
+                        cx,
+                        &field,
+                        xlen,
+                        flen,
+                        field1_kind,
+                        field2_kind,
+                        offset_from_start + arg_layout.fields.offset(i),
+                    )?;
                 }
             }
         },
@@ -140,14 +155,52 @@ where
 {
     let mut field1_kind = RegPassKind::Unknown;
     let mut field2_kind = RegPassKind::Unknown;
-    if should_use_fp_conv_helper(cx, arg, xlen, flen, &mut field1_kind, &mut field2_kind).is_err() {
+    if should_use_fp_conv_helper(
+        cx,
+        arg,
+        xlen,
+        flen,
+        &mut field1_kind,
+        &mut field2_kind,
+        Size::ZERO,
+    )
+    .is_err()
+    {
         return None;
     }
     match (field1_kind, field2_kind) {
-        (RegPassKind::Integer(l), RegPassKind::Float(r)) => Some(FloatConv::MixedPair(l, r)),
-        (RegPassKind::Float(l), RegPassKind::Integer(r)) => Some(FloatConv::MixedPair(l, r)),
-        (RegPassKind::Float(l), RegPassKind::Float(r)) => Some(FloatConv::FloatPair(l, r)),
-        (RegPassKind::Float(f), RegPassKind::Unknown) => Some(FloatConv::Float(f)),
+        (
+            RegPassKind::Integer { offset_from_start, .. }
+            | RegPassKind::Float { offset_from_start, .. },
+            _,
+        ) if offset_from_start != Size::ZERO => {
+            panic!("type {:?} has a first field with non-zero offset {offset_from_start:?}", arg.ty)
+        }
+        (
+            RegPassKind::Integer { ty: first_ty, .. },
+            RegPassKind::Float { offset_from_start, ty: second_ty },
+        ) => Some(FloatConv::MixedPair {
+            first_ty,
+            second_ty_offset_from_start: offset_from_start,
+            second_ty,
+        }),
+        (
+            RegPassKind::Float { ty: first_ty, .. },
+            RegPassKind::Integer { offset_from_start, ty: second_ty },
+        ) => Some(FloatConv::MixedPair {
+            first_ty,
+            second_ty_offset_from_start: offset_from_start,
+            second_ty,
+        }),
+        (
+            RegPassKind::Float { ty: first_ty, .. },
+            RegPassKind::Float { offset_from_start, ty: second_ty },
+        ) => Some(FloatConv::FloatPair {
+            first_ty,
+            second_ty_offset_from_start: offset_from_start,
+            second_ty,
+        }),
+        (RegPassKind::Float { ty, .. }, RegPassKind::Unknown) => Some(FloatConv::Float(ty)),
         _ => None,
     }
 }
@@ -165,11 +218,19 @@ where
             FloatConv::Float(f) => {
                 arg.cast_to(f);
             }
-            FloatConv::FloatPair(l, r) => {
-                arg.cast_to(CastTarget::pair(l, r));
+            FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty } => {
+                arg.cast_to(CastTarget::offset_pair(
+                    first_ty,
+                    second_ty_offset_from_start,
+                    second_ty,
+                ));
             }
-            FloatConv::MixedPair(l, r) => {
-                arg.cast_to(CastTarget::pair(l, r));
+            FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty } => {
+                arg.cast_to(CastTarget::offset_pair(
+                    first_ty,
+                    second_ty_offset_from_start,
+                    second_ty,
+                ));
             }
         }
         return false;
@@ -233,15 +294,27 @@ fn classify_arg<'a, Ty, C>(
                 arg.cast_to(f);
                 return;
             }
-            Some(FloatConv::FloatPair(l, r)) if *avail_fprs >= 2 => {
+            Some(FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty })
+                if *avail_fprs >= 2 =>
+            {
                 *avail_fprs -= 2;
-                arg.cast_to(CastTarget::pair(l, r));
+                arg.cast_to(CastTarget::offset_pair(
+                    first_ty,
+                    second_ty_offset_from_start,
+                    second_ty,
+                ));
                 return;
             }
-            Some(FloatConv::MixedPair(l, r)) if *avail_fprs >= 1 && *avail_gprs >= 1 => {
+            Some(FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty })
+                if *avail_fprs >= 1 && *avail_gprs >= 1 =>
+            {
                 *avail_gprs -= 1;
                 *avail_fprs -= 1;
-                arg.cast_to(CastTarget::pair(l, r));
+                arg.cast_to(CastTarget::offset_pair(
+                    first_ty,
+                    second_ty_offset_from_start,
+                    second_ty,
+                ));
                 return;
             }
             _ => (),
diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs
index db70fb65d44..64a3dd3e8bc 100644
--- a/library/core/src/num/int_macros.rs
+++ b/library/core/src/num/int_macros.rs
@@ -1413,6 +1413,66 @@ macro_rules! int_impl {
             }
         }
 
+        /// Exact shift left. Computes `self << rhs` as long as it can be reversed losslessly.
+        ///
+        /// Returns `None` if any bits that would be shifted out differ from the resulting sign bit
+        /// or if `rhs` >=
+        #[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
+        /// Otherwise, returns `Some(self << rhs)`.
+        ///
+        /// # Examples
+        ///
+        /// ```
+        /// #![feature(exact_bitshifts)]
+        ///
+        #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(4), Some(0x10));")]
+        #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(", stringify!($SelfT), "::BITS - 2), Some(1 << ", stringify!($SelfT), "::BITS - 2));")]
+        #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(", stringify!($SelfT), "::BITS - 1), None);")]
+        #[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").exact_shl(", stringify!($SelfT), "::BITS - 2), Some(-0x2 << ", stringify!($SelfT), "::BITS - 2));")]
+        #[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").exact_shl(", stringify!($SelfT), "::BITS - 1), None);")]
+        /// ```
+        #[unstable(feature = "exact_bitshifts", issue = "144336")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> {
+            if rhs < self.leading_zeros() || rhs < self.leading_ones() {
+                // SAFETY: rhs is checked above
+                Some(unsafe { self.unchecked_shl(rhs) })
+            } else {
+                None
+            }
+        }
+
+        /// Unchecked exact shift left. Computes `self << rhs`, assuming the operation can be
+        /// losslessly reversed and `rhs` cannot be larger than
+        #[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
+        ///
+        /// # Safety
+        ///
+        /// This results in undefined behavior when `rhs >= self.leading_zeros() && rhs >=
+        /// self.leading_ones()` i.e. when
+        #[doc = concat!("[`", stringify!($SelfT), "::exact_shl`]")]
+        /// would return `None`.
+        #[unstable(feature = "exact_bitshifts", issue = "144336")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT {
+            assert_unsafe_precondition!(
+                check_language_ub,
+                concat!(stringify!($SelfT), "::unchecked_exact_shl cannot shift out non-zero bits"),
+                (
+                    zeros: u32 = self.leading_zeros(),
+                    ones: u32 = self.leading_ones(),
+                    rhs: u32 = rhs,
+                ) => rhs < zeros || rhs < ones,
+            );
+
+            // SAFETY: this is guaranteed to be safe by the caller
+            unsafe { self.unchecked_shl(rhs) }
+        }
+
         /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is
         /// larger than or equal to the number of bits in `self`.
         ///
@@ -1534,6 +1594,63 @@ macro_rules! int_impl {
             }
         }
 
+        /// Exact shift right. Computes `self >> rhs` as long as it can be reversed losslessly.
+        ///
+        /// Returns `None` if any non-zero bits would be shifted out or if `rhs` >=
+        #[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
+        /// Otherwise, returns `Some(self >> rhs)`.
+        ///
+        /// # Examples
+        ///
+        /// ```
+        /// #![feature(exact_bitshifts)]
+        ///
+        #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(4), Some(0x1));")]
+        #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(5), None);")]
+        /// ```
+        #[unstable(feature = "exact_bitshifts", issue = "144336")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> {
+            if rhs <= self.trailing_zeros() && rhs < <$SelfT>::BITS {
+                // SAFETY: rhs is checked above
+                Some(unsafe { self.unchecked_shr(rhs) })
+            } else {
+                None
+            }
+        }
+
+        /// Unchecked exact shift right. Computes `self >> rhs`, assuming the operation can be
+        /// losslessly reversed and `rhs` cannot be larger than
+        #[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
+        ///
+        /// # Safety
+        ///
+        /// This results in undefined behavior when `rhs > self.trailing_zeros() || rhs >=
+        #[doc = concat!(stringify!($SelfT), "::BITS`")]
+        /// i.e. when
+        #[doc = concat!("[`", stringify!($SelfT), "::exact_shr`]")]
+        /// would return `None`.
+        #[unstable(feature = "exact_bitshifts", issue = "144336")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT {
+            assert_unsafe_precondition!(
+                check_language_ub,
+                concat!(stringify!($SelfT), "::unchecked_exact_shr cannot shift out non-zero bits"),
+                (
+                    zeros: u32 = self.trailing_zeros(),
+                    bits: u32 =  <$SelfT>::BITS,
+                    rhs: u32 = rhs,
+                ) => rhs <= zeros && rhs < bits,
+            );
+
+            // SAFETY: this is guaranteed to be safe by the caller
+            unsafe { self.unchecked_shr(rhs) }
+        }
+
         /// Checked absolute value. Computes `self.abs()`, returning `None` if
         /// `self == MIN`.
         ///
diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs
index 0ce9c259c40..bf72ec83197 100644
--- a/library/core/src/num/uint_macros.rs
+++ b/library/core/src/num/uint_macros.rs
@@ -1821,6 +1821,63 @@ macro_rules! uint_impl {
             }
         }
 
+        /// Exact shift left. Computes `self << rhs` as long as it can be reversed losslessly.
+        ///
+        /// Returns `None` if any non-zero bits would be shifted out or if `rhs` >=
+        #[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
+        /// Otherwise, returns `Some(self << rhs)`.
+        ///
+        /// # Examples
+        ///
+        /// ```
+        /// #![feature(exact_bitshifts)]
+        ///
+        #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(4), Some(0x10));")]
+        #[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(129), None);")]
+        /// ```
+        #[unstable(feature = "exact_bitshifts", issue = "144336")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> {
+            if rhs <= self.leading_zeros() && rhs < <$SelfT>::BITS {
+                // SAFETY: rhs is checked above
+                Some(unsafe { self.unchecked_shl(rhs) })
+            } else {
+                None
+            }
+        }
+
+        /// Unchecked exact shift left. Computes `self << rhs`, assuming the operation can be
+        /// losslessly reversed `rhs` cannot be larger than
+        #[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
+        ///
+        /// # Safety
+        ///
+        /// This results in undefined behavior when `rhs > self.leading_zeros() || rhs >=
+        #[doc = concat!(stringify!($SelfT), "::BITS`")]
+        /// i.e. when
+        #[doc = concat!("[`", stringify!($SelfT), "::exact_shl`]")]
+        /// would return `None`.
+        #[unstable(feature = "exact_bitshifts", issue = "144336")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT {
+            assert_unsafe_precondition!(
+                check_language_ub,
+                concat!(stringify!($SelfT), "::exact_shl_unchecked cannot shift out non-zero bits"),
+                (
+                    zeros: u32 = self.leading_zeros(),
+                    bits: u32 =  <$SelfT>::BITS,
+                    rhs: u32 = rhs,
+                ) => rhs <= zeros && rhs < bits,
+            );
+
+            // SAFETY: this is guaranteed to be safe by the caller
+            unsafe { self.unchecked_shl(rhs) }
+        }
+
         /// Checked shift right. Computes `self >> rhs`, returning `None`
         /// if `rhs` is larger than or equal to the number of bits in `self`.
         ///
@@ -1936,6 +1993,63 @@ macro_rules! uint_impl {
             }
         }
 
+        /// Exact shift right. Computes `self >> rhs` as long as it can be reversed losslessly.
+        ///
+        /// Returns `None` if any non-zero bits would be shifted out or if `rhs` >=
+        #[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
+        /// Otherwise, returns `Some(self >> rhs)`.
+        ///
+        /// # Examples
+        ///
+        /// ```
+        /// #![feature(exact_bitshifts)]
+        ///
+        #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(4), Some(0x1));")]
+        #[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(5), None);")]
+        /// ```
+        #[unstable(feature = "exact_bitshifts", issue = "144336")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> {
+            if rhs <= self.trailing_zeros() && rhs < <$SelfT>::BITS {
+                // SAFETY: rhs is checked above
+                Some(unsafe { self.unchecked_shr(rhs) })
+            } else {
+                None
+            }
+        }
+
+        /// Unchecked exact shift right. Computes `self >> rhs`, assuming the operation can be
+        /// losslessly reversed and `rhs` cannot be larger than
+        #[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
+        ///
+        /// # Safety
+        ///
+        /// This results in undefined behavior when `rhs > self.trailing_zeros() || rhs >=
+        #[doc = concat!(stringify!($SelfT), "::BITS`")]
+        /// i.e. when
+        #[doc = concat!("[`", stringify!($SelfT), "::exact_shr`]")]
+        /// would return `None`.
+        #[unstable(feature = "exact_bitshifts", issue = "144336")]
+        #[must_use = "this returns the result of the operation, \
+                      without modifying the original"]
+        #[inline]
+        pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT {
+            assert_unsafe_precondition!(
+                check_language_ub,
+                concat!(stringify!($SelfT), "::exact_shr_unchecked cannot shift out non-zero bits"),
+                (
+                    zeros: u32 = self.trailing_zeros(),
+                    bits: u32 =  <$SelfT>::BITS,
+                    rhs: u32 = rhs,
+                ) => rhs <= zeros && rhs < bits,
+            );
+
+            // SAFETY: this is guaranteed to be safe by the caller
+            unsafe { self.unchecked_shr(rhs) }
+        }
+
         /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if
         /// overflow occurred.
         ///
diff --git a/library/coretests/tests/floats/f128.rs b/library/coretests/tests/floats/f128.rs
index c173d7f0ae0..4267fef50f8 100644
--- a/library/coretests/tests/floats/f128.rs
+++ b/library/coretests/tests/floats/f128.rs
@@ -1,7 +1,9 @@
 // FIXME(f16_f128): only tested on platforms that have symbols and aren't buggy
 #![cfg(target_has_reliable_f128)]
 
-use super::{assert_approx_eq, assert_biteq};
+#[cfg(any(miri, target_has_reliable_f128_math))]
+use super::assert_approx_eq;
+use super::assert_biteq;
 
 // Note these tolerances make sense around zero, but not for more extreme exponents.
 
@@ -75,25 +77,6 @@ fn test_float_bits_conv() {
 }
 
 #[test]
-fn test_algebraic() {
-    let a: f128 = 123.0;
-    let b: f128 = 456.0;
-
-    // Check that individual operations match their primitive counterparts.
-    //
-    // This is a check of current implementations and does NOT imply any form of
-    // guarantee about future behavior. The compiler reserves the right to make
-    // these operations inexact matches in the future.
-    let eps = if cfg!(miri) { 1e-6 } else { 0.0 };
-
-    assert_approx_eq!(a.algebraic_add(b), a + b, eps);
-    assert_approx_eq!(a.algebraic_sub(b), a - b, eps);
-    assert_approx_eq!(a.algebraic_mul(b), a * b, eps);
-    assert_approx_eq!(a.algebraic_div(b), a / b, eps);
-    assert_approx_eq!(a.algebraic_rem(b), a % b, eps);
-}
-
-#[test]
 fn test_from() {
     assert_biteq!(f128::from(false), 0.0);
     assert_biteq!(f128::from(true), 1.0);
diff --git a/library/coretests/tests/floats/f16.rs b/library/coretests/tests/floats/f16.rs
index c12de7221ba..d2818a6d768 100644
--- a/library/coretests/tests/floats/f16.rs
+++ b/library/coretests/tests/floats/f16.rs
@@ -74,27 +74,6 @@ fn test_float_bits_conv() {
 }
 
 #[test]
-fn test_algebraic() {
-    let a: f16 = 123.0;
-    let b: f16 = 456.0;
-
-    // Check that individual operations match their primitive counterparts.
-    //
-    // This is a check of current implementations and does NOT imply any form of
-    // guarantee about future behavior. The compiler reserves the right to make
-    // these operations inexact matches in the future.
-    let eps_add = if cfg!(miri) { 1e1 } else { 0.0 };
-    let eps_mul = if cfg!(miri) { 1e3 } else { 0.0 };
-    let eps_div = if cfg!(miri) { 1e0 } else { 0.0 };
-
-    assert_approx_eq!(a.algebraic_add(b), a + b, eps_add);
-    assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add);
-    assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul);
-    assert_approx_eq!(a.algebraic_div(b), a / b, eps_div);
-    assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div);
-}
-
-#[test]
 fn test_from() {
     assert_biteq!(f16::from(false), 0.0);
     assert_biteq!(f16::from(true), 1.0);
diff --git a/library/coretests/tests/floats/f32.rs b/library/coretests/tests/floats/f32.rs
index b79295f470d..7b25f354da4 100644
--- a/library/coretests/tests/floats/f32.rs
+++ b/library/coretests/tests/floats/f32.rs
@@ -1,6 +1,6 @@
 use core::f32;
 
-use super::{assert_approx_eq, assert_biteq};
+use super::assert_biteq;
 
 /// First pattern over the mantissa
 const NAN_MASK1: u32 = 0x002a_aaaa;
@@ -47,24 +47,3 @@ fn test_float_bits_conv() {
     assert_eq!(f32::from_bits(masked_nan1).to_bits(), masked_nan1);
     assert_eq!(f32::from_bits(masked_nan2).to_bits(), masked_nan2);
 }
-
-#[test]
-fn test_algebraic() {
-    let a: f32 = 123.0;
-    let b: f32 = 456.0;
-
-    // Check that individual operations match their primitive counterparts.
-    //
-    // This is a check of current implementations and does NOT imply any form of
-    // guarantee about future behavior. The compiler reserves the right to make
-    // these operations inexact matches in the future.
-    let eps_add = if cfg!(miri) { 1e-3 } else { 0.0 };
-    let eps_mul = if cfg!(miri) { 1e-1 } else { 0.0 };
-    let eps_div = if cfg!(miri) { 1e-4 } else { 0.0 };
-
-    assert_approx_eq!(a.algebraic_add(b), a + b, eps_add);
-    assert_approx_eq!(a.algebraic_sub(b), a - b, eps_add);
-    assert_approx_eq!(a.algebraic_mul(b), a * b, eps_mul);
-    assert_approx_eq!(a.algebraic_div(b), a / b, eps_div);
-    assert_approx_eq!(a.algebraic_rem(b), a % b, eps_div);
-}
diff --git a/library/coretests/tests/floats/f64.rs b/library/coretests/tests/floats/f64.rs
index a2540586328..099d85627a3 100644
--- a/library/coretests/tests/floats/f64.rs
+++ b/library/coretests/tests/floats/f64.rs
@@ -1,6 +1,6 @@
 use core::f64;
 
-use super::{assert_approx_eq, assert_biteq};
+use super::assert_biteq;
 
 /// First pattern over the mantissa
 const NAN_MASK1: u64 = 0x000a_aaaa_aaaa_aaaa;
@@ -46,22 +46,3 @@ fn test_float_bits_conv() {
     assert_eq!(f64::from_bits(masked_nan1).to_bits(), masked_nan1);
     assert_eq!(f64::from_bits(masked_nan2).to_bits(), masked_nan2);
 }
-
-#[test]
-fn test_algebraic() {
-    let a: f64 = 123.0;
-    let b: f64 = 456.0;
-
-    // Check that individual operations match their primitive counterparts.
-    //
-    // This is a check of current implementations and does NOT imply any form of
-    // guarantee about future behavior. The compiler reserves the right to make
-    // these operations inexact matches in the future.
-    let eps = if cfg!(miri) { 1e-6 } else { 0.0 };
-
-    assert_approx_eq!(a.algebraic_add(b), a + b, eps);
-    assert_approx_eq!(a.algebraic_sub(b), a - b, eps);
-    assert_approx_eq!(a.algebraic_mul(b), a * b, eps);
-    assert_approx_eq!(a.algebraic_div(b), a / b, eps);
-    assert_approx_eq!(a.algebraic_rem(b), a % b, eps);
-}
diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/floats/mod.rs
index 5f59cb9cce3..c0439845a4a 100644
--- a/library/coretests/tests/floats/mod.rs
+++ b/library/coretests/tests/floats/mod.rs
@@ -27,6 +27,9 @@ trait TestableFloat: Sized {
     const NAN_MASK1: Self::Int;
     /// Second pattern over the mantissa
     const NAN_MASK2: Self::Int;
+    const EPS_ADD: Self;
+    const EPS_MUL: Self;
+    const EPS_DIV: Self;
 }
 
 impl TestableFloat for f16 {
@@ -44,6 +47,9 @@ impl TestableFloat for f16 {
     const MAX_DOWN: Self = Self::from_bits(0x7bfe);
     const NAN_MASK1: Self::Int = 0x02aa;
     const NAN_MASK2: Self::Int = 0x0155;
+    const EPS_ADD: Self = if cfg!(miri) { 1e1 } else { 0.0 };
+    const EPS_MUL: Self = if cfg!(miri) { 1e3 } else { 0.0 };
+    const EPS_DIV: Self = if cfg!(miri) { 1e0 } else { 0.0 };
 }
 
 impl TestableFloat for f32 {
@@ -63,6 +69,9 @@ impl TestableFloat for f32 {
     const MAX_DOWN: Self = Self::from_bits(0x7f7f_fffe);
     const NAN_MASK1: Self::Int = 0x002a_aaaa;
     const NAN_MASK2: Self::Int = 0x0055_5555;
+    const EPS_ADD: Self = if cfg!(miri) { 1e-3 } else { 0.0 };
+    const EPS_MUL: Self = if cfg!(miri) { 1e-1 } else { 0.0 };
+    const EPS_DIV: Self = if cfg!(miri) { 1e-4 } else { 0.0 };
 }
 
 impl TestableFloat for f64 {
@@ -78,6 +87,9 @@ impl TestableFloat for f64 {
     const MAX_DOWN: Self = Self::from_bits(0x7fef_ffff_ffff_fffe);
     const NAN_MASK1: Self::Int = 0x000a_aaaa_aaaa_aaaa;
     const NAN_MASK2: Self::Int = 0x0005_5555_5555_5555;
+    const EPS_ADD: Self = if cfg!(miri) { 1e-6 } else { 0.0 };
+    const EPS_MUL: Self = if cfg!(miri) { 1e-6 } else { 0.0 };
+    const EPS_DIV: Self = if cfg!(miri) { 1e-6 } else { 0.0 };
 }
 
 impl TestableFloat for f128 {
@@ -93,6 +105,9 @@ impl TestableFloat for f128 {
     const MAX_DOWN: Self = Self::from_bits(0x7ffefffffffffffffffffffffffffffe);
     const NAN_MASK1: Self::Int = 0x0000aaaaaaaaaaaaaaaaaaaaaaaaaaaa;
     const NAN_MASK2: Self::Int = 0x00005555555555555555555555555555;
+    const EPS_ADD: Self = if cfg!(miri) { 1e-6 } else { 0.0 };
+    const EPS_MUL: Self = if cfg!(miri) { 1e-6 } else { 0.0 };
+    const EPS_DIV: Self = if cfg!(miri) { 1e-6 } else { 0.0 };
 }
 
 /// Determine the tolerance for values of the argument type.
@@ -1440,3 +1455,27 @@ float_test! {
         assert_biteq!(neg_inf.to_radians(), neg_inf);
     }
 }
+
+float_test! {
+    name: to_algebraic,
+    attrs: {
+        f16: #[cfg(target_has_reliable_f16)],
+        f128: #[cfg(target_has_reliable_f128)],
+    },
+    test<Float> {
+        let a: Float = 123.0;
+        let b: Float = 456.0;
+
+        // Check that individual operations match their primitive counterparts.
+        //
+        // This is a check of current implementations and does NOT imply any form of
+        // guarantee about future behavior. The compiler reserves the right to make
+        // these operations inexact matches in the future.
+
+        assert_approx_eq!(a.algebraic_add(b), a + b, Float::EPS_ADD);
+        assert_approx_eq!(a.algebraic_sub(b), a - b, Float::EPS_ADD);
+        assert_approx_eq!(a.algebraic_mul(b), a * b, Float::EPS_MUL);
+        assert_approx_eq!(a.algebraic_div(b), a / b, Float::EPS_DIV);
+        assert_approx_eq!(a.algebraic_rem(b), a % b, Float::EPS_DIV);
+    }
+}
diff --git a/library/std/src/sys/stdio/mod.rs b/library/std/src/sys/stdio/mod.rs
index 314f226f07b..7436e4d9de4 100644
--- a/library/std/src/sys/stdio/mod.rs
+++ b/library/std/src/sys/stdio/mod.rs
@@ -29,9 +29,13 @@ cfg_select! {
         mod uefi;
         pub use uefi::*;
     }
-    target_os = "wasi" => {
-        mod wasi;
-        pub use wasi::*;
+    all(target_os = "wasi", target_env = "p1") => {
+        mod wasip1;
+        pub use wasip1::*;
+    }
+    all(target_os = "wasi", target_env = "p2") => {
+        mod wasip2;
+        pub use wasip2::*;
     }
     target_os = "xous" => {
         mod xous;
diff --git a/library/std/src/sys/stdio/wasi.rs b/library/std/src/sys/stdio/wasip1.rs
index b70efd026f9..b70efd026f9 100644
--- a/library/std/src/sys/stdio/wasi.rs
+++ b/library/std/src/sys/stdio/wasip1.rs
diff --git a/library/std/src/sys/stdio/wasip2.rs b/library/std/src/sys/stdio/wasip2.rs
new file mode 100644
index 00000000000..1fcb49a083d
--- /dev/null
+++ b/library/std/src/sys/stdio/wasip2.rs
@@ -0,0 +1,120 @@
+use wasip2::cli;
+use wasip2::io::streams::{Error, InputStream, OutputStream, StreamError};
+
+use crate::io::{self, BorrowedBuf, BorrowedCursor};
+
+pub struct Stdin(Option<InputStream>);
+pub struct Stdout(Option<OutputStream>);
+pub struct Stderr(Option<OutputStream>);
+
+fn error_to_io(err: Error) -> io::Error {
+    // There exists a function in `wasi:filesystem` to optionally acquire an
+    // error code from an error, but the streams in use in this module are
+    // exclusively used with stdio meaning that a filesystem error is not
+    // possible here.
+    //
+    // In lieu of an error code, which WASIp2 does not specify, this instead
+    // carries along the `to_debug_string` implementation that the host
+    // supplies. If this becomes too expensive in the future this could also
+    // become `io::Error::from_raw_os_error(libc::EIO)` or similar.
+    io::Error::new(io::ErrorKind::Other, err.to_debug_string())
+}
+
+impl Stdin {
+    pub const fn new() -> Stdin {
+        Stdin(None)
+    }
+
+    fn stream(&mut self) -> &InputStream {
+        self.0.get_or_insert_with(cli::stdin::get_stdin)
+    }
+}
+
+impl io::Read for Stdin {
+    fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
+        let mut buf = BorrowedBuf::from(data);
+        self.read_buf(buf.unfilled())?;
+        Ok(buf.len())
+    }
+
+    fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> io::Result<()> {
+        match self.stream().blocking_read(u64::try_from(buf.capacity()).unwrap()) {
+            Ok(result) => {
+                buf.append(&result);
+                Ok(())
+            }
+            Err(StreamError::Closed) => Ok(()),
+            Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)),
+        }
+    }
+}
+
+impl Stdout {
+    pub const fn new() -> Stdout {
+        Stdout(None)
+    }
+
+    fn stream(&mut self) -> &OutputStream {
+        self.0.get_or_insert_with(cli::stdout::get_stdout)
+    }
+}
+
+fn write(stream: &OutputStream, buf: &[u8]) -> io::Result<usize> {
+    // WASIp2's `blocking_write_and_flush` function is defined as accepting no
+    // more than 4096 bytes. Larger writes can be issued by manually using
+    // `check_write`, `write`, and `blocking_flush`, but for now just go ahead
+    // and use `blocking_write_and_flush` and report a short write and let a
+    // higher level loop over the result.
+    const MAX: usize = 4096;
+    let buf = &buf[..buf.len().min(MAX)];
+    match stream.blocking_write_and_flush(buf) {
+        Ok(()) => Ok(buf.len()),
+        Err(StreamError::Closed) => Ok(0),
+        Err(StreamError::LastOperationFailed(e)) => Err(error_to_io(e)),
+    }
+}
+
+impl io::Write for Stdout {
+    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+        write(self.stream(), data)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        // Note that `OutputStream` has a `flush` function but for stdio all
+        // writes are accompanied with a flush which means that this flush
+        // doesn't need to do anything.
+        Ok(())
+    }
+}
+
+impl Stderr {
+    pub const fn new() -> Stderr {
+        Stderr(None)
+    }
+
+    fn stream(&mut self) -> &OutputStream {
+        self.0.get_or_insert_with(cli::stderr::get_stderr)
+    }
+}
+
+impl io::Write for Stderr {
+    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+        write(self.stream(), data)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        // See `Stdout::flush` for why this is a noop.
+        Ok(())
+    }
+}
+
+pub const STDIN_BUF_SIZE: usize = crate::sys::io::DEFAULT_BUF_SIZE;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+    // WASIp2 stdio streams are always available so ebadf never shows up.
+    false
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+    Some(Stderr::new())
+}
diff --git a/tests/assembly-llvm/loongarch-float-struct-abi.rs b/tests/assembly-llvm/loongarch-float-struct-abi.rs
new file mode 100644
index 00000000000..4991004fc05
--- /dev/null
+++ b/tests/assembly-llvm/loongarch-float-struct-abi.rs
@@ -0,0 +1,134 @@
+//@ add-core-stubs
+//@ assembly-output: emit-asm
+//@ compile-flags: -Copt-level=3 --target loongarch64-unknown-linux-gnu
+//@ needs-llvm-components: loongarch
+
+#![feature(no_core, lang_items)]
+#![no_std]
+#![no_core]
+#![crate_type = "lib"]
+
+extern crate minicore;
+use minicore::*;
+
+#[repr(C, align(64))]
+struct Aligned(f64);
+
+#[repr(C)]
+struct Padded(u8, Aligned);
+
+#[repr(C, packed)]
+struct Packed(u8, f32);
+
+impl Copy for Aligned {}
+impl Copy for Padded {}
+impl Copy for Packed {}
+
+extern "C" {
+    fn take_padded(x: Padded);
+    fn get_padded() -> Padded;
+    fn take_packed(x: Packed);
+    fn get_packed() -> Packed;
+}
+
+// CHECK-LABEL: pass_padded
+#[unsafe(no_mangle)]
+extern "C" fn pass_padded(out: &mut Padded, x: Padded) {
+    // CHECK: st.b $a1, $a0, 0
+    // CHECK-NEXT: fst.d $fa0, $a0, 64
+    // CHECK-NEXT: ret
+    *out = x;
+}
+
+// CHECK-LABEL: ret_padded
+#[unsafe(no_mangle)]
+extern "C" fn ret_padded(x: &Padded) -> Padded {
+    // CHECK: fld.d $fa0, $a0, 64
+    // CHECK-NEXT: ld.b $a0, $a0, 0
+    // CHECK-NEXT: ret
+    *x
+}
+
+#[unsafe(no_mangle)]
+extern "C" fn call_padded(x: &Padded) {
+    // CHECK: fld.d $fa0, $a0, 64
+    // CHECK-NEXT: ld.b $a0, $a0, 0
+    // CHECK-NEXT: pcaddu18i $t8, %call36(take_padded)
+    // CHECK-NEXT: jr $t8
+    unsafe {
+        take_padded(*x);
+    }
+}
+
+#[unsafe(no_mangle)]
+extern "C" fn receive_padded(out: &mut Padded) {
+    // CHECK: addi.d $sp, $sp, -16
+    // CHECK-NEXT: .cfi_def_cfa_offset 16
+    // CHECK-NEXT: st.d $ra, $sp, [[#%d,RA_SPILL:]]
+    // CHECK-NEXT: st.d [[TEMP:.*]], $sp, [[#%d,TEMP_SPILL:]]
+    // CHECK-NEXT: .cfi_offset 1, [[#%d,RA_SPILL - 16]]
+    // CHECK-NEXT: .cfi_offset [[#%d,TEMP_NUM:]], [[#%d,TEMP_SPILL - 16]]
+    // CHECK-NEXT: move [[TEMP]], $a0
+    // CHECK-NEXT: pcaddu18i $ra, %call36(get_padded)
+    // CHECK-NEXT: jirl $ra, $ra, 0
+    // CHECK-NEXT: st.b $a0, [[TEMP]], 0
+    // CHECK-NEXT: fst.d $fa0, [[TEMP]], 64
+    // CHECK-NEXT: ld.d [[TEMP]], $sp, [[#%d,TEMP_SPILL]]
+    // CHECK-NEXT: ld.d $ra, $sp, [[#%d,RA_SPILL]]
+    // CHECK: addi.d $sp, $sp, 16
+    // CHECK: ret
+    unsafe {
+        *out = get_padded();
+    }
+}
+
+// CHECK-LABEL: pass_packed
+#[unsafe(no_mangle)]
+extern "C" fn pass_packed(out: &mut Packed, x: Packed) {
+    // CHECK: st.b $a1, $a0, 0
+    // CHECK-NEXT: fst.s $fa0, $a0, 1
+    // CHECK-NEXT: ret
+    *out = x;
+}
+
+// CHECK-LABEL: ret_packed
+#[unsafe(no_mangle)]
+extern "C" fn ret_packed(x: &Packed) -> Packed {
+    // CHECK: fld.s $fa0, $a0, 1
+    // CHECK-NEXT: ld.b $a0, $a0, 0
+    // CHECK-NEXT: ret
+    *x
+}
+
+#[unsafe(no_mangle)]
+extern "C" fn call_packed(x: &Packed) {
+    // CHECK: fld.s $fa0, $a0, 1
+    // CHECK-NEXT: ld.b $a0, $a0, 0
+    // CHECK-NEXT: pcaddu18i $t8, %call36(take_packed)
+    // CHECK-NEXT: jr $t8
+    unsafe {
+        take_packed(*x);
+    }
+}
+
+#[unsafe(no_mangle)]
+extern "C" fn receive_packed(out: &mut Packed) {
+    // CHECK: addi.d $sp, $sp, -16
+    // CHECK-NEXT: .cfi_def_cfa_offset 16
+    // CHECK-NEXT: st.d $ra, $sp, [[#%d,RA_SPILL:]]
+    // CHECK-NEXT: st.d [[TEMP:.*]], $sp, [[#%d,TEMP_SPILL:]]
+    // CHECK-NEXT: .cfi_offset 1, [[#%d,RA_SPILL - 16]]
+    // CHECK-NEXT: .cfi_offset [[#%d,TEMP_NUM:]], [[#%d,TEMP_SPILL - 16]]
+    // CHECK-NEXT: move [[TEMP]], $a0
+    // CHECK-NEXT: pcaddu18i $ra, %call36(get_packed)
+    // CHECK-NEXT: jirl $ra, $ra, 0
+    // CHECK-NEXT: st.b $a0, [[TEMP]], 0
+    // CHECK-NEXT: fst.s $fa0, [[TEMP]], 1
+    // CHECK-NEXT: ld.d [[TEMP]], $sp, [[#%d,TEMP_SPILL]]
+    // CHECK-NEXT: ld.d $ra, $sp, [[#%d,RA_SPILL]]
+    // CHECK: addi.d $sp, $sp, 16
+    // CHECK: ret
+    unsafe {
+        *out = get_packed();
+    }
+}
diff --git a/tests/codegen-llvm/cast-target-abi.rs b/tests/codegen-llvm/cast-target-abi.rs
index 28d3ad5f61c..090de00df93 100644
--- a/tests/codegen-llvm/cast-target-abi.rs
+++ b/tests/codegen-llvm/cast-target-abi.rs
@@ -171,7 +171,15 @@ pub extern "C" fn receives_doubledouble(x: DoubleDouble) {
 
     // CHECK: [[RUST_ALLOCA:%.+]] = alloca [16 x i8], align [[RUST_ALIGN:8]]
 
-    // CHECK: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // aarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_0:%.+]] = extractvalue [[ABI_TYPE]] [[ABI_VALUE:%.+]], 0
+    // loongarch64: [[ABI_VALUE_1:%.+]] = extractvalue [[ABI_TYPE]] [[ABI_VALUE:%.+]], 1
+    // loongarch64: store double [[ABI_VALUE_0]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8
+    // loongarch64: store double [[ABI_VALUE_1]], ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]]
+    // powerpc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // sparc64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // x86_64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
 
     // CHECK: call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false)
 }
@@ -190,7 +198,11 @@ pub extern "C" fn returns_doubledouble() -> DoubleDouble {
     // x86_64:      [[ABI_ALLOCA:%.+]] = alloca [16 x i8], align [[ABI_ALIGN:8]]
 
     // aarch64:     [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x double\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
-    // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_0:%.+]] = load double, ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8
+    // loongarch64: [[ABI_VALUE_1:%.+]] = load double, ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_2:%.+]] = insertvalue [[ABI_TYPE:{ double, double }]] poison, double [[ABI_VALUE_0]], 0
+    // loongarch64: [[ABI_VALUE:%.+]] = insertvalue { double, double } [[ABI_VALUE_2]], double [[ABI_VALUE_1]], 1
     // sparc64:     [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
     // x86_64:      [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
 
@@ -269,7 +281,11 @@ pub extern "C" fn receives_doublefloat(x: DoubleFloat) {
     // x86_64:      [[RUST_ALLOCA:%.+]] = alloca [16 x i8], align [[RUST_ALIGN:8]]
 
     // aarch64:     store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
-    // loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_0:%.+]] = extractvalue { double, float } [[ABI_VALUE]], 0
+    // loongarch64: [[ABI_VALUE_1:%.+]] = extractvalue { double, float } [[ABI_VALUE]], 1
+    // loongarch64: store double [[ABI_VALUE_0]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8
+    // loongarch64: store float [[ABI_VALUE_1]], ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]]
     // powerpc64:   store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
     // x86_64:      store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
 
@@ -297,7 +313,11 @@ pub extern "C" fn returns_doublefloat() -> DoubleFloat {
     // x86_64:      [[ABI_ALLOCA:%.+]] = alloca [16 x i8], align [[ABI_ALIGN:8]]
 
     // aarch64:     [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
-    // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, float }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_0:%.+]] = load double, ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8
+    // loongarch64: [[ABI_VALUE_1:%.+]] = load float, ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_2:%.+]] = insertvalue [[ABI_TYPE:{ double, float }]] poison, double [[ABI_VALUE_0]], 0
+    // loongarch64: [[ABI_VALUE:%.+]] = insertvalue { double, float } [[ABI_VALUE_2]], float [[ABI_VALUE_1]], 1
     // x86_64:      [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
 
     // aarch64:     ret [[ABI_TYPE]] [[ABI_VALUE]]
@@ -429,7 +449,11 @@ pub fn call_doubledouble() {
     // CHECK: call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 16, i1 false)
 
     // aarch64:     [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x double\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
-    // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_0:%.+]] = load double, ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8
+    // loongarch64: [[ABI_VALUE_1:%.+]] = load double, ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_2:%.+]] = insertvalue [[ABI_TYPE:{ double, double }]] poison, double [[ABI_VALUE_0]], 0
+    // loongarch64: [[ABI_VALUE:%.+]] = insertvalue { double, double } [[ABI_VALUE_2]], double [[ABI_VALUE_1]], 1
     // powerpc64:   [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
     // sparc64:     [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
     // x86_64:      [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
@@ -465,7 +489,11 @@ pub fn return_doubledouble() -> DoubleDouble {
     // x86_64:      [[ABI_VALUE:%.+]] = call [[ABI_TYPE:{ double, double }]] @returns_doubledouble()
 
     // aarch64:     store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
-    // loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_0:%.+]] = extractvalue { double, double } [[ABI_VALUE]], 0
+    // loongarch64: [[ABI_VALUE_1:%.+]] = extractvalue { double, double } [[ABI_VALUE]], 1
+    // loongarch64: store double [[ABI_VALUE_0]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8
+    // loongarch64: store double [[ABI_VALUE_1]], ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]]
     // sparc64:     store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
     // x86_64:      store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
 
@@ -500,7 +528,11 @@ pub fn call_doublefloat() {
     // x86_64:      call void @llvm.memcpy.{{.+}}(ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], i64 16, i1 false)
 
     // aarch64:     [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
-    // loongarch64: [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, float }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_0:%.+]] = load double, ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8
+    // loongarch64: [[ABI_VALUE_1:%.+]] = load float, ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_2:%.+]] = insertvalue [[ABI_TYPE:{ double, float }]] poison, double [[ABI_VALUE_0]], 0
+    // loongarch64: [[ABI_VALUE:%.+]] = insertvalue { double, float } [[ABI_VALUE_2]], float [[ABI_VALUE_1]], 1
     // powerpc64:   [[ABI_VALUE:%.+]] = load [[ABI_TYPE:\[2 x i64\]]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
     // x86_64:      [[ABI_VALUE:%.+]] = load [[ABI_TYPE:{ double, double }]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
 
@@ -540,7 +572,11 @@ pub fn return_doublefloat() -> DoubleFloat {
     // x86_64:      [[ABI_VALUE:%.+]] = call [[ABI_TYPE:{ double, double }]] @returns_doublefloat()
 
     // aarch64:     store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
-    // loongarch64: store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_VALUE_0:%.+]] = extractvalue { double, float } [[ABI_VALUE]], 0
+    // loongarch64: [[ABI_VALUE_1:%.+]] = extractvalue { double, float } [[ABI_VALUE]], 1
+    // loongarch64: store double [[ABI_VALUE_0]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
+    // loongarch64: [[ABI_ALLOCA_1:%.+]] = getelementptr inbounds i8, ptr [[ABI_ALLOCA]], i64 8
+    // loongarch64: store float [[ABI_VALUE_1]], ptr [[ABI_ALLOCA_1]], align [[ABI_ALIGN]]
     // x86_64:      store [[ABI_TYPE]] [[ABI_VALUE]], ptr [[ABI_ALLOCA]], align [[ABI_ALIGN]]
 
     // aarch64:     call void @llvm.memcpy.{{.+}}(ptr align [[RUST_ALIGN]] [[RUST_ALLOCA]], ptr align [[ABI_ALIGN]] [[ABI_ALLOCA]], i64 16, i1 false)
diff --git a/tests/codegen-llvm/loongarch-abi/cast-local-large-enough.rs b/tests/codegen-llvm/loongarch-abi/cast-local-large-enough.rs
new file mode 100644
index 00000000000..e5a0e4cd3a2
--- /dev/null
+++ b/tests/codegen-llvm/loongarch-abi/cast-local-large-enough.rs
@@ -0,0 +1,44 @@
+//@ add-core-stubs
+//@ compile-flags: -Copt-level=0 -Cdebuginfo=0 --target loongarch64-unknown-linux-gnu
+//@ needs-llvm-components: loongarch
+
+#![feature(no_core, lang_items)]
+#![no_std]
+#![no_core]
+#![crate_type = "lib"]
+
+extern crate minicore;
+use minicore::*;
+
+#[repr(C, align(64))]
+struct Aligned(f64);
+
+#[repr(C, align(64))]
+struct AlignedPair(f32, f64);
+
+impl Copy for Aligned {}
+impl Copy for AlignedPair {}
+
+// CHECK-LABEL: define double @read_aligned
+#[unsafe(no_mangle)]
+pub extern "C" fn read_aligned(x: &Aligned) -> Aligned {
+    // CHECK: %[[TEMP:.*]] = alloca [64 x i8], align 64
+    // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 64 %[[TEMP]], ptr align 64 %[[PTR:.*]], i64 64, i1 false)
+    // CHECK-NEXT: %[[RES:.*]] = load double, ptr %[[TEMP]], align 64
+    // CHECK-NEXT: ret double %[[RES]]
+    *x
+}
+
+// CHECK-LABEL: define { float, double } @read_aligned_pair
+#[unsafe(no_mangle)]
+pub extern "C" fn read_aligned_pair(x: &AlignedPair) -> AlignedPair {
+    // CHECK: %[[TEMP:.*]] = alloca [64 x i8], align 64
+    // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 64 %[[TEMP]], ptr align 64 %[[PTR:.*]], i64 64, i1 false)
+    // CHECK-NEXT: %[[FIRST:.*]] = load float, ptr %[[TEMP]], align 64
+    // CHECK-NEXT: %[[SECOND_PTR:.*]] = getelementptr inbounds i8, ptr %[[TEMP]], i64 8
+    // CHECK-NEXT: %[[SECOND:.*]] = load double, ptr %[[SECOND_PTR]], align 8
+    // CHECK-NEXT: %[[RES1:.*]] = insertvalue { float, double } poison, float %[[FIRST]], 0
+    // CHECK-NEXT: %[[RES2:.*]] = insertvalue { float, double } %[[RES1]], double %[[SECOND]], 1
+    // CHECK-NEXT: ret { float, double } %[[RES2]]
+    *x
+}
diff --git a/tests/ui/privacy/suggest-box-new.rs b/tests/ui/privacy/suggest-box-new.rs
index 7125285fc77..ff585387020 100644
--- a/tests/ui/privacy/suggest-box-new.rs
+++ b/tests/ui/privacy/suggest-box-new.rs
@@ -16,4 +16,7 @@ fn main() {
     let _ = std::collections::HashMap {};
     //~^ ERROR cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields
     let _ = Box {}; //~ ERROR cannot construct `Box<_, _>` with struct literal syntax due to private fields
+
+    // test that we properly instantiate the parameter of `Box::<T>::new` with an inference variable
+    let _ = Box::<i32> {}; //~ ERROR cannot construct `Box<i32>` with struct literal syntax due to private fields
 }
diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr
index 2b48e9046bf..37b2989dcc1 100644
--- a/tests/ui/privacy/suggest-box-new.stderr
+++ b/tests/ui/privacy/suggest-box-new.stderr
@@ -118,13 +118,41 @@ LL +     let _ = Box::new_zeroed();
 LL -     let _ = Box {};
 LL +     let _ = Box::new_in(_, _);
    |
-   = and 12 other candidates
+   = and 13 other candidates
 help: consider using the `Default` trait
    |
 LL -     let _ = Box {};
 LL +     let _ = <Box as std::default::Default>::default();
    |
 
-error: aborting due to 4 previous errors
+error: cannot construct `Box<i32>` with struct literal syntax due to private fields
+  --> $DIR/suggest-box-new.rs:21:13
+   |
+LL |     let _ = Box::<i32> {};
+   |             ^^^^^^^^^^
+   |
+   = note: private fields `0` and `1` that were not provided
+help: you might have meant to use an associated function to build this type
+   |
+LL -     let _ = Box::<i32> {};
+LL +     let _ = Box::<i32>::new(_);
+   |
+LL -     let _ = Box::<i32> {};
+LL +     let _ = Box::<i32>::new_in(_, _);
+   |
+LL -     let _ = Box::<i32> {};
+LL +     let _ = Box::<i32>::into_inner(_);
+   |
+LL -     let _ = Box::<i32> {};
+LL +     let _ = Box::<i32>::write(_, _);
+   |
+   = and 4 other candidates
+help: consider using the `Default` trait
+   |
+LL -     let _ = Box::<i32> {};
+LL +     let _ = <Box::<i32> as std::default::Default>::default();
+   |
+
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0423`.
diff --git a/tests/ui/privacy/suggest-new-projection-ice.rs b/tests/ui/privacy/suggest-new-projection-ice.rs
new file mode 100644
index 00000000000..41ac27508e8
--- /dev/null
+++ b/tests/ui/privacy/suggest-new-projection-ice.rs
@@ -0,0 +1,19 @@
+//! Regression test for <https://github.com/rust-lang/rust/issues/146174>.
+//! Ensure that we don't ICE when an associated function returns an associated type.
+
+mod m {
+    pub trait Project {
+        type Assoc;
+    }
+    pub struct Foo {
+        _priv: (),
+    }
+    impl Foo {
+        fn new<T: Project>() -> T::Assoc {
+            todo!()
+        }
+    }
+}
+fn main() {
+    let _ = m::Foo {}; //~ ERROR: cannot construct `Foo`
+}
diff --git a/tests/ui/privacy/suggest-new-projection-ice.stderr b/tests/ui/privacy/suggest-new-projection-ice.stderr
new file mode 100644
index 00000000000..4700772096b
--- /dev/null
+++ b/tests/ui/privacy/suggest-new-projection-ice.stderr
@@ -0,0 +1,10 @@
+error: cannot construct `Foo` with struct literal syntax due to private fields
+  --> $DIR/suggest-new-projection-ice.rs:18:13
+   |
+LL |     let _ = m::Foo {};
+   |             ^^^^^^
+   |
+   = note: private field `_priv` that was not provided
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/suggestions/multi-suggestion.ascii.stderr b/tests/ui/suggestions/multi-suggestion.ascii.stderr
index 1744162e6ce..9c8867a1771 100644
--- a/tests/ui/suggestions/multi-suggestion.ascii.stderr
+++ b/tests/ui/suggestions/multi-suggestion.ascii.stderr
@@ -118,7 +118,7 @@ LL +     let _ = Box::new_zeroed();
 LL -     let _ = Box {};
 LL +     let _ = Box::new_in(_, _);
    |
-   = and 12 other candidates
+   = and 13 other candidates
 help: consider using the `Default` trait
    |
 LL -     let _ = Box {};
diff --git a/tests/ui/suggestions/multi-suggestion.unicode.stderr b/tests/ui/suggestions/multi-suggestion.unicode.stderr
index 4835c263f19..4fdab51493e 100644
--- a/tests/ui/suggestions/multi-suggestion.unicode.stderr
+++ b/tests/ui/suggestions/multi-suggestion.unicode.stderr
@@ -118,7 +118,7 @@ LL +     let _ = Box::new_zeroed();
 LL -     let _ = Box {};
 LL +     let _ = Box::new_in(_, _);

-   ╰ and 12 other candidates
+   ╰ and 13 other candidates
 help: consider using the `Default` trait
    ╭╴
 LL -     let _ = Box {};