From bdc08aeb91626171a7dc5bfbc1e0d109770ee457 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Thu, 6 Jun 2024 12:49:31 -0700 Subject: [PATCH 1/8] Reword the caveats on `array::map` Thanks to 107634 and some improvements in LLVM (particularly `dead_on_unwind`), the method actually optimizes reasonably well now. So focus the discussion on the fundamental ordering differences where the optimizer might never be able to fix it because of the different behaviour, and encouraging `Iterator::map` where an array wasn't actually ever needed. --- library/core/src/array/mod.rs | 55 ++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 6cca2e6358b63..4a2cf15277441 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -516,20 +516,47 @@ impl [T; N] { /// /// # Note on performance and stack usage /// - /// Unfortunately, usages of this method are currently not always optimized - /// as well as they could be. This mainly concerns large arrays, as mapping - /// over small arrays seem to be optimized just fine. Also note that in - /// debug mode (i.e. without any optimizations), this method can use a lot - /// of stack space (a few times the size of the array or more). - /// - /// Therefore, in performance-critical code, try to avoid using this method - /// on large arrays or check the emitted code. Also try to avoid chained - /// maps (e.g. `arr.map(...).map(...)`). - /// - /// In many cases, you can instead use [`Iterator::map`] by calling `.iter()` - /// or `.into_iter()` on your array. `[T; N]::map` is only necessary if you - /// really need a new array of the same size as the result. Rust's lazy - /// iterators tend to get optimized very well. + /// Note that this method is *eager*. It evaluates `f` all `N` times before + /// returning the new array. + /// + /// That means that `arr.map(f).map(g)` is, in general, *not* equivalent to + /// `array.map(|x| g(f(x)))`, as the former calls `f` 4 times then `g` 4 times, + /// whereas the latter interleaves the calls (`fgfgfgfg`). + /// + /// A consequence of this is that it can have fairly-high stack usage, especially + /// in debug mode or for long arrays. The backend may be able to optimize it + /// away, but especially for complicated mappings it might not be able to. + /// + /// If you're doing a one-step `map` and really want an array as the result, + /// then absolutely use this method. Its implementation uses a bunch of tricks + /// to help the optimizer handle it well. Particularly for simple arrays, + /// like `[u8; 3]` or `[f32; 4]`, there's nothing to be concerned about. + /// + /// However, if you don't actually need an *array* of the results specifically, + /// just to process them, then you likely want [`Iterator::map`] instead. + /// + /// For example, rather than doing an array-to-array map of all the elements + /// in the array up-front and only iterating after that completes, + /// + /// ``` + /// # let my_array = [1, 2, 3]; + /// # let f = |x: i32| x + 1; + /// for x in my_array.map(f) { + /// // ... + /// } + /// ``` + /// + /// It's often better to use an iterator along the lines of + /// + /// ``` + /// # let my_array = [1, 2, 3]; + /// # let f = |x: i32| x + 1; + /// for x in my_array.into_iter().map(f) { + /// // ... + /// } + /// ``` + /// + /// as that's more likely to avoid large temporaries. /// /// /// # Examples From ef7a7809c743baff98aaa7dcea49e1fafdde190b Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 30 Jan 2026 17:50:52 +0100 Subject: [PATCH 2/8] add test for simd from array repeat codegen --- tests/codegen-llvm/simd/array-repeat.rs | 40 +++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/codegen-llvm/simd/array-repeat.rs diff --git a/tests/codegen-llvm/simd/array-repeat.rs b/tests/codegen-llvm/simd/array-repeat.rs new file mode 100644 index 0000000000000..691167f866626 --- /dev/null +++ b/tests/codegen-llvm/simd/array-repeat.rs @@ -0,0 +1,40 @@ +//@ add-minicore +//@ revisions: X86 AARCH64 RISCV S390X +//@ [X86] compile-flags: -Copt-level=3 --target=x86_64-unknown-linux-gnu +//@ [X86] needs-llvm-components: x86 +//@ [AARCH64] compile-flags: -Copt-level=3 --target=aarch64-unknown-linux-gnu +//@ [AARCH64] needs-llvm-components: aarch64 +//@ [RISCV] compile-flags: -Copt-level=3 --target riscv64gc-unknown-linux-gnu -Ctarget-feature=+v +//@ [RISCV] needs-llvm-components: riscv +//@ [S390X] compile-flags: -Copt-level=3 --target s390x-unknown-linux-gnu -Ctarget-feature=+vector +//@ [S390X] needs-llvm-components: systemz +#![crate_type = "lib"] +#![feature(repr_simd)] +#![feature(no_core)] +#![no_std] +#![no_core] +extern crate minicore; +use minicore::*; + +#[repr(simd)] +pub struct Simd(pub [T; N]); + +pub type u8x16 = Simd; + +// Regression test for https://github.com/rust-lang/rust/issues/97804. + +#[unsafe(no_mangle)] +fn foo(v: u16, p: &mut [u8; 16]) { + // An array repeat transmuted into a SIMD type should emit a canonical LLVM splat sequence: + // + // CHECK-LABEL: foo + // CHECK: start + // CHECK-NEXT: %0 = insertelement <8 x i16> poison, i16 %v, i64 0 + // CHECK-NEXT: %1 = shufflevector <8 x i16> %0, <8 x i16> poison, <8 x i32> zeroinitializer + // CHECK-NEXT: store <8 x i16> %1, ptr %p, align 1 + // CHECK-NEXT: ret void + unsafe { + let v: u8x16 = mem::transmute([v; 8]); + *p = mem::transmute(v); + } +} From cdd0ede64037938b5ef89da2b5ac3c77ea7c6375 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 3 Feb 2026 16:09:21 -0800 Subject: [PATCH 3/8] bootstrap: always propagate `CARGO_TARGET_{host}_LINKER` We were already setting `CARGO_TARGET_{target}_LINKER` when there is a setting in `bootstrap.toml`, and when the host and target are the same, this is also used for build scripts and proc-macros. However, the host value wasn't set when building for any other target, and Cargo would see that as a fingerprint change for those build artifacts, rebuilding them. If we always set the `CARGO_TARGET_{host}_LINKER`, then those build scripts will keep a consistent Cargo fingerprint, so they'll remain cached no matter how we're alternating targets. --- src/bootstrap/src/core/builder/cargo.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 7150b2b0d59f2..488125863ee0d 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -10,7 +10,7 @@ use crate::core::build_steps::tool::SourceType; use crate::core::config::SplitDebuginfo; use crate::core::config::flags::Color; use crate::utils::build_stamp; -use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags}; +use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_flags}; use crate::{ BootstrapCommand, CLang, Compiler, Config, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t, @@ -310,7 +310,15 @@ impl Cargo { } } - for arg in linker_args(builder, compiler.host, LldThreads::Yes) { + // We need to set host linker flags for compiling build scripts and proc-macros. + // This is done the same way as the target linker flags below, so cargo won't see + // any fingerprint difference between host==target versus cross-compiled targets + // when it comes to those host build artifacts. + if let Some(host_linker) = builder.linker(compiler.host) { + let host = crate::envify(&compiler.host.triple); + self.command.env(format!("CARGO_TARGET_{host}_LINKER"), host_linker); + } + for arg in linker_flags(builder, compiler.host, LldThreads::Yes) { self.hostflags.arg(&arg); } @@ -319,11 +327,11 @@ impl Cargo { self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker); } // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not - // `linker_args` here. + // `linker_args` here. Cargo will pass that to both rustc and rustdoc invocations. for flag in linker_flags(builder, target, LldThreads::Yes) { self.rustflags.arg(&flag); } - for arg in linker_args(builder, target, LldThreads::Yes) { + for arg in linker_flags(builder, target, LldThreads::Yes) { self.rustdocflags.arg(&arg); } From 6b1ed212b70be56296d3aadba518028e6049eb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 8 Feb 2026 09:16:31 +0100 Subject: [PATCH 4/8] Remove types adhoc group --- triagebot.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 4a4054391ce73..43db49873c4d8 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1536,13 +1536,6 @@ mir-opt = [ "@wesleywiser", "@saethlin", ] -types = [ - "@jackh726", - "@lcnr", - "@oli-obk", - "@spastorino", - "@BoxyUwU", -] borrowck = [ "@davidtwco", "@matthewjasper" From c19c780d4557f4d1e2ee285c08184e9c37070c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 8 Feb 2026 09:22:24 +0100 Subject: [PATCH 5/8] Remove project-const-traits adhoc group --- triagebot.toml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 43db49873c4d8..d6106024a1fd7 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1555,11 +1555,6 @@ style-team = [ "@joshtriplett", "@traviscross", ] -project-const-traits = [ - "@fee1-dead", - "@fmease", - "@oli-obk", -] project-stable-mir = [ "@celinval", "@oli-obk", From 5e33ea7b926a8ed4f6228588564e036fba792897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 8 Feb 2026 09:23:04 +0100 Subject: [PATCH 6/8] Remove project-stable-mir adhoc group --- triagebot.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index d6106024a1fd7..f2e69b625a816 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1555,12 +1555,6 @@ style-team = [ "@joshtriplett", "@traviscross", ] -project-stable-mir = [ - "@celinval", - "@oli-obk", - "@scottmcm", - "@makai410", -] project-exploit-mitigations = [ "@cuviper", "@rcvalle", From f46032089a94f7185823b0b519529d50311ff489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 8 Feb 2026 09:24:38 +0100 Subject: [PATCH 7/8] Remove project-exploit-mitigations adhoc group --- triagebot.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index f2e69b625a816..98e88f4e2e9b6 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1555,10 +1555,6 @@ style-team = [ "@joshtriplett", "@traviscross", ] -project-exploit-mitigations = [ - "@cuviper", - "@rcvalle", -] compiletest = [ "@jieyouxu", ] From 4a979d546bc779f97e6224e8ab3d26f8bd69a6e9 Mon Sep 17 00:00:00 2001 From: Mark Rousskov Date: Sun, 8 Feb 2026 19:54:03 +0000 Subject: [PATCH 8/8] Stop having two different alignment constants * Stop having two different alignment constants * Update library/core/src/alloc/global.rs --- library/core/src/alloc/global.rs | 7 ++++--- library/core/src/mem/mod.rs | 5 ++++- library/core/src/ptr/alignment.rs | 3 +-- .../default_boxed_slice.main.GVN.32bit.panic-abort.diff | 2 +- .../default_boxed_slice.main.GVN.32bit.panic-unwind.diff | 2 +- .../default_boxed_slice.main.GVN.64bit.panic-abort.diff | 2 +- .../default_boxed_slice.main.GVN.64bit.panic-unwind.diff | 2 +- tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff | 2 +- 8 files changed, 14 insertions(+), 11 deletions(-) diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index d18e1f525d106..bf3d3e0a5aca7 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -284,9 +284,10 @@ pub unsafe trait GlobalAlloc { /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: the caller must ensure that the `new_size` does not overflow. - // `layout.align()` comes from a `Layout` and is thus guaranteed to be valid. - let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) }; + let alignment = layout.alignment(); + // SAFETY: the caller must ensure that the `new_size` does not overflow + // when rounded up to the next multiple of `alignment`. + let new_layout = unsafe { Layout::from_size_alignment_unchecked(new_size, alignment) }; // SAFETY: the caller must ensure that `new_layout` is greater than zero. let new_ptr = unsafe { self.alloc(new_layout) }; if !new_ptr.is_null() { diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 7c486875a8268..eb6f8f9757215 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1260,7 +1260,10 @@ pub trait SizedTypeProperties: Sized { #[doc(hidden)] #[unstable(feature = "ptr_alignment_type", issue = "102070")] - const ALIGNMENT: Alignment = Alignment::of::(); + const ALIGNMENT: Alignment = { + // This can't panic since type alignment is always a power of two. + Alignment::new(Self::ALIGN).unwrap() + }; /// `true` if this type requires no storage. /// `false` if its [size](size_of) is greater than zero. diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 7c34b026e14be..b27930de4e666 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -52,8 +52,7 @@ impl Alignment { #[inline] #[must_use] pub const fn of() -> Self { - // This can't actually panic since type alignment is always a power of two. - const { Alignment::new(align_of::()).unwrap() } + ::ALIGNMENT } /// Returns the [ABI]-required minimum alignment of the type of the value that `val` points to. diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index 7d66d31291155..308f19ea759dd 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -44,7 +44,7 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); -- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute); +- _6 = const <[bool; 0] as std::mem::SizedTypeProperties>::ALIGNMENT as *const [bool; 0] (Transmute); - _5 = NonNull::<[bool; 0]> { pointer: copy _6 }; + _6 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index cc00bd300a3c1..819ad6054df80 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -44,7 +44,7 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); -- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute); +- _6 = const <[bool; 0] as std::mem::SizedTypeProperties>::ALIGNMENT as *const [bool; 0] (Transmute); - _5 = NonNull::<[bool; 0]> { pointer: copy _6 }; + _6 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index 9380cdd6ccb4c..7029e02a857ac 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -44,7 +44,7 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); -- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute); +- _6 = const <[bool; 0] as std::mem::SizedTypeProperties>::ALIGNMENT as *const [bool; 0] (Transmute); - _5 = NonNull::<[bool; 0]> { pointer: copy _6 }; + _6 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index bea5643762747..23a134f3666bb 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -44,7 +44,7 @@ StorageLive(_4); StorageLive(_5); StorageLive(_6); -- _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute); +- _6 = const <[bool; 0] as std::mem::SizedTypeProperties>::ALIGNMENT as *const [bool; 0] (Transmute); - _5 = NonNull::<[bool; 0]> { pointer: copy _6 }; + _6 = const {0x1 as *const [bool; 0]}; + _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}; diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff index 9e543699da70f..23889b266e4ad 100644 --- a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff +++ b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff @@ -40,7 +40,7 @@ StorageLive(_1); StorageLive(_2); StorageLive(_3); -- _3 = const std::ptr::Alignment::of::::{constant#0}; +- _3 = const ::ALIGNMENT; - _2 = copy _3 as *mut u8 (Transmute); + _3 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }}; + _2 = const {0x1 as *mut u8};